I'm using an unmanaged C# dll (using DllExport and dotnet framework 4.0) to add UI capabilities to an NSIS installer, but basic controls constructors throw a font '?' cannot be found. exception on windows 7. Code example:
[DllExport]
public static void CreateRichTextBox()
{
try
{
RichTextBox myRichTextBox = new RichTextBox();
MessageBox.Show("RichTextBox created on windows 10");
}
catch (Exception ex)
{
MessageBox.Show("RichTextBox contractor failed on windows 7 with " + ex.Message);
}
}
So after some digging, I got to the conclusion that this happeneds because of a function used by NSIS that messes internal paths for the Dotnet: SetDefaultDllDirectories.
The function was added for security reasons explaind by the NSIS team:
Using SetDefaultDllDirectories we can globally change the behavior
of LoadLibraryEx so that it only looks into the System32 folder and into any
directories explicitly added with AddDllDirectories.
I only guess that this is a Gdiplus bug that was eventually solved but I still wonder if someone knows of a workaround to make it work on windows 7?
The public Microsoft bug report claims the bug is in Windows (GDI+) and not technically a .NET specific issue.
Unfortunately MSDN says this about SetDefaultDllDirectories:
It is not possible to revert to the standard DLL search path or remove any directory specified with SetDefaultDllDirectories from the search path. ...
This means the only workaround is to not call the function in the first place. The only way to do that in NSIS is to use a hex-editor and modify the stubs in NSIS\Stubs. Search for SetDefaultDllDirectories in the relevant stub and change it to something like SetDefaultxxxxxxxxxxxxxx (replace as many characters as you want but the length has to stay the same!).
This of course means that your installer will be somewhat vulnerable to malicious .DLL planting attacks. You can try to restore the functionality somewhat with:
!include WinVer.nsh
!include LogicLib.nsh
Function .onInit
${If} ${AtLeastWin8}
System::Call 'KERNEL32::SetDefaultDllDirectories(i 0x800|0x400)'
${EndIf}
FunctionEnd
but the protection is slightly less effective this way and does nothing on Windows 7.
Related
I'm currently using C# and Win32 interop to register power setting notifications, enumerate power setting guids, etc. In the process of this, I came across a function in the MSDN documentation called "PowerIsSettingRangeDefined" that looked useful. When I imported the function and tried to test it, the compiler threw "System.EntryPointNotFound" exception. Obviously I double checked my spelling, etc, to no avail. So I looked for the header file on my computer and I did find the function listed in the 'powrprof.h' header for the 8.0 SDK, however, I don't have a copy of the 7.0 or 7.0A SDK on this computer. But according to the documentation, this was supposed to have been implemented starting with Windows 7. So this should have worked...
Then I used ProcessHacker to inspect the export table of the 'powrprof.dll' module in my process. The function was NOT there! To take it one step further, I used PE Explorer on the actual 'powrprof.dll' file located in "C:\Windows\SysWOW64\" and the entry point in question, again, was NOT in the name list. So the official header files include this function with "#if (NTDDI_VERSION >= NTDDI_WIN7)" above, and the documentation shows the function exists starting with Win7, however, it seems the MS team did not include it when compiling 'powrprof.dll' for Windows 7.
Maybe someone can check their copy in Windows 8.1/10 to see if this issue is only on Windows 7? I don't have another version of Windows in this building or I'd test it myself. And I wanted to bring it to attention here first, before trying to report this to Microsoft, just in case I'm missing something.
Sorry for a long question but I have to put as many details as I can to help you understand this issue better.
I'm using an msi installer created by InstallShield 2012, it runs properly on most computers but on some I get the generic 1001 error and upon clicking OK on that error everything rolled back. To troubleshoot, I ran the following code to generate the debug log from the installation
Setup.exe /v"/l*v \"C:\log.dat\""
The debug log shows error 2769 with a custom action xxxx.install did not close 1 MSIHANDLES.
While googling about this issue, I see a lot of people having this exact same error and most of the suggestions come down to check what your custom action is doing because it is the one that generates this error.
Here are what I've done to troubleshoot and isolate this problem so far:
Open up the InstallShield project and look at the MSI deubbger, I noticed the custom action names in the log is part of the custom action InstallShield uses to install a Window Service as part of the installation.
I look at how this service is installed, it turns out to be a C# executable that InstallShield invokes as a .NET Installer Class to install the service under the component's setting.
As per what is .NET installer class? you can look at the note below from InstallShield.
Fearing something is not right with my custom action code, i put in some debug logging and run the whole installation again, I still receive the same error but I do not see any exception being logged. The service actually created successfully and i can even run it as long as I don't click OK on the 1001 error which will trigger the roll back and uninstall this service.
public ProjectInstaller()
{
try
{
using (StreamWriter w = File.AppendText("c:\\log.txt"))
{
Log("start installing", w);
}
InitializeComponent();
using (StreamWriter w = File.AppendText("c:\\log.txt"))
{
Log("End Install", w);
}
}
catch (Exception ex)
{
using (StreamWriter w = File.AppendText("c:\\log.txt"))
{
Log(ex.Message, w);
Log(ex.StackTrace, w);
}
}
}
private void InitializeComponent()
{
this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
//
// serviceProcessInstaller1
//
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
this.serviceProcessInstaller1.Password = null;
this.serviceProcessInstaller1.Username = null;
//
// serviceInstaller1
//
this.serviceInstaller1.Description = "Healthcare Platform Service";
this.serviceInstaller1.ServiceName = "psService";
this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;
//
// ProjectInstaller
//
this.Installers.AddRange(new System.Configuration.Install.Installer[] {
this.serviceProcessInstaller1,
this.serviceInstaller1});
}
Based on my troubleshooting so far, I don't think the error is within the custom action code. However, that really leave me hanging because I don't really know what cause the custom action to fails; it looks like something did not close out the msi handles but this is really a black box to me.....
So any idea what this might be?
How can I further delve down to figure out what the heck went wrong with this customer action _502E509F9B6F6675DFF9C310662BC1B5.install ?
Below are the custom actions sequence.
*EDIT:
I found the link that talks about the similar error I have... however I verified my custom action doesn't have any parameter and that based on my verbose debug log I see all the path are properly resolved.
**EDIT: Add custom action sequence screenshots.
The error is a generic one resulting from the infrastructure of installer classes not working. This is mostly a black box developed for Visual Studio setup projects. It uses a C++ Dll call to ManagedInstall which then loads a framework version, locates your assembly, instantiates your class with reflection and then calls the Install method. InstallUtilLib is architecture specific, and a mismatch between it and your managed code and the framework version will cause errors. If this happens only on one machine it might be this mismatch, or maybe that machine is broken in some way regarding that service.
That's just information that may help. However, if you have an actual InstallShield 2012 then you don't need installer classes at all. They were created for Visual Studio setups, and are not needed for practically every other MSI building tool that exists because they have built-in support for the MSI ServiceInstall and ServiceControl tables.
To quote Jerry MaGuire... Stop, you had me at 1001. InstallUtil managed custom actions must be avoided at all cost. The first action is to eliminate the need for the custom action by using native windows installer capabilities. If the custom action is still needed the second action is to refactor using Windows Installer XML (Wix) Deployment Tools Foundation (DTF). Is a far better pattern for integrating managed code with MSI. It works very well with InstallShield.
IMO, InstallShield should have never made it so easy to wire up an InstallUtil custom action. They undoubtedly did so to satisfy customer requests and to ease migration to InstallShield but at the huge sacrifice of quality standards.
I really need to see where this is sequenced. If this is deferred and before install initialize or after install finalize, that could be an issue.
Hmm,
This is rather interesting... I found a Microsoft link that talks about the Event Source
By default, if I uninstall my software the event source will get removed as well. For some reason that is not the case I see, i think it isn't removed because the file to which EventMessageFile points to was being used/uninstall?
So reading the article above I went and remove out the registry manually found under
Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog\MyProgramName
The installation no longer gives me 1001 error after I remove the above registry. It looks like InstallShield tries to install a window service but upon looking at this registry it deems the services already exists (even though it isn't).
I want to start the default windows sound record in a Winforms app (c++/cli).In "Run" dialog, the command for Vista/7 is "soundrecorder". So I began with the easiest way:
System::Diagnostics::Process::Start("soundrecorder");
but it throws a System.ComponentModel.Win32Exception (Message: The System Cannot Find the File Specified). So I used the real path:
String^ path = Environment::GetFolderPath(Environment::SpecialFolder::System);
path = System::IO::Path::Combine(path, "soundrecorder.exe");
System::Diagnostics::Process::Start(path);
Same result. Hard-coding full path fail too. Any other command like "mspaint" runs correctly.
I thought that the problem was in my environment/current user/machine. Then I write a stupid program in C#:
public abstract class StupidProgram{
public static void Main(string[] args)
{
Process.Start("soundrecorder");
}
}
It works, and even more: If I run "StupidProgram.exe" from my app, it works too.
The application is coded in C++/CLI as 32-bits program. I'm working in Windows 7 64bits. I tested the app in a Windows 7 32bits and it works, so it seams a 32/64 bits compatibility issue.
Do you have any idea about this behaviour?
Quoting your C++/CLI code:
System::Diagnostics::Process::Start("soundrecord");
Quoting your C# code:
Process.Start("soundrecorder");
^^
Specifying "soundrecord" doesn't work, because it's called "soundrecord er"
Without the exact exception text, I think specifying the full path doesn't work because your application is being compiled as x86, and you're running 64-bit Windows, and the OS is doing filesystem redirection.
I recommend you use the equivalent to your C# command: Windows has the functionality to search the path for the executable, let it do so. Hard-coding the full path will give compatibility issues in the future, when MS decides to move it.
System::Diagnostics::Process::Start("soundrecorder");
^^
we are making a project to run in ASP.Net on Mono/*nix
Our problem is that we develop on Windows, and we just build and test it every so often on Mono. So we have been having a lot of trouble recently with case sensitive filenames. Everything seems to work good in Windows and then we move to Mono and it's silently broken.(as in, it builds but won't run or parts of it don't work)
How would you recommend that I detect this while we are developing on Windows? Basically, how do we make the case-sensitive filenames look wrong in our code where the code works on Windows but not *nix?
One thing you can do is use MONO_IOMAP so that Mono silently corrects the errors and emulates a case-insensitive file system:
http://www.mono-project.com/IOMap
Another thing you can use to actually find the issues is a new "profiler module" that logs every time a string triggers MONO_IOMAP and tells you where in your code it was created:
http://twistedcode.net/blog/post/2009/12/21/A-utility-to-help-porting-Windows-NET-applications-to-MonoUnix.aspx
You could use a compiler directive which would indicate when you are building for *nix systems which would validate the file paths.
for extracting special folder icons I'm using
ExtractIconEx(Environment.SystemDirectory + "\\shell32.dll",ncIconIndex, handlesIconLarge, handlesIconSmall, 1);
Here im passing explicitly nIconIndex for special folders like MyDocs,MyPictures ..etc
and its working fine in XP ,however in Vista its not retrieving the correct icons ..there it retrieves yellow folder icons..it should not be the case.
Cn anybody help me on this..
Vista added a new API called SHGetStockIconInfo but it does not support my documents AFAIK. But that does not matter since the method you SHOULD be using works on both XP and Vista (Your current solution will not work when the user has selected a custom icon, you are just looking in hardcoded system dlls, this could change at any point)
So, what you should do is, get the path or PIDL to the shell folder you are interested in (SHGetFolderPath and friends) and pass that path/PIDL to SHGetFileInfo. SHGetFileInfo can give you a icon handle, or the index into the system image list.
I'm not sure what the .NET equivalent for those functions are, but you should be able to figure that out, or use PInvoke
Check out the IconLib library at codeproject.
The best example I've seen of success in this area from .NET (and it was done with VB.NET) is in this article.
http://www.codeproject.com/KB/cpp/VbNetExpTree.aspx
My $.02 is that working with the shell API is extremely painful from .NET due to the level of COM interop required and the complexity of the API's.