using the same icon for the .exe and a form in a W

2019-07-13 15:24发布

问题:

I have an icon for my application, and I want to use it as both the EXE icon and the icon on the main form. When I say "EXE icon" I mean the icon that is embedded by the /win32icon option to the C# compiler or the icon specified in the Application section of the project settings in Visual Studio. This is the icon displayed by Windows Explorer.

However, the application form uses a default icon, which is what shows up in the title bar and when you press Alt-Tab.

I want to use the same icon for both without duplicating the data. Practically, this means the WinForms application has to read the embedded Win32 icon at runtime. Presumably this is possible, but I haven't been able to find any information due to the search results being cluttered with pages about accessing embedded resources from .resx files and the like.

I don't mind if this requires p/invoke or similar. I can see with a Win32 resource viewer that the icon is embedded within the EXE with ID 32512 (IDI_APPLICATION). I've tried the following:

IntPtr hInstance = GetModuleHandle(IntPtr.Zero);
IntPtr hIcon = LoadIcon(hInstance, new IntPtr(32512));
icon = Icon.FromHandle(hIcon);

but hIcon == 0. I've also tried:

IntPtr hIcon = LoadIcon(IntPtr.Zero, new IntPtr(32512));
icon = Icon.FromHandle(hIcon);

This loads an icon, but it's a system default application icon, not the one from the EXE.

Does anyone know how to do it?

回答1:

It turns out that the first approach I tried was basically correct.

IntPtr hInstance = GetModuleHandle(null);
IntPtr hIcon = LoadIcon(hInstance, new IntPtr(32512));
icon = Icon.FromHandle(hIcon);

... where the functions are declared like so:

[DllImport("user32.dll")]
static extern IntPtr LoadIcon(IntPtr hInstance, IntPtr iconName);
[DllImport("kernel32.dll")]
static extern IntPtr GetModuleHandle(string moduleName);

With help from Hans Passant's somewhat cryptic comment, I found that the reason it didn't work was because of the Visual Studio hosting process. Not wanting to disable that, I instead changed the code to run conditionally:

IntPtr hInstance = GetModuleHandle(null);
IntPtr hIcon = LoadIcon(hInstance, new IntPtr(32512));
if(hIcon != IntPtr.Zero) icon = Icon.FromHandle(hIcon);

And there we go. No more need to duplicate the icon as both Win32 and .NET resource data.