[Note] Co-worker wanted to see this, so I’m reposting to make it more readily available.
For the past couple months I have been working on a small suite of WinForms applications written in C#, one targetting Tablet PC. This was my first project using Visual Studio 2005, and C# 2.0. For the most part, I like what Microsoft has done. In the IDE, I don’t notice that many breakthrough changes. Snaplines are nice, and the new data grid was a breeze to use. The TableLayoutPanel helped me out a great deal. <Rant>I was a little apprehensive at first, since I avoid using tables for layout in HTML, but the TableLayoutPanel is designed for layout, it is semantically correct. Tables are a natural fit for layout in many cases, but html tables semantically represent tabular data, not a method of layout.</Rant>
Anyway, I was chugging along trying to make halfway usable applications and setup packages. Sorry Rob, no WiX yet, but I hope to look into that soon. I decided that I had files worthy of their own Windows file type — you know, you double-click on one and it opens in the appropriate application, they have a uniquely identifying icon, etc. I already had a program icon, so I thought I would just add another icon to my resources and be done with it, my documents could point at the icon using the “program.exe,2” syntax (where program.exe contains multiple icon resources and the number after the comma is the icon index). This is what almost all Microsoft applications do, and seemed like the way to go. For example, if you create a shortcut, and browse to say, winword.exe for the icon, you will be presented with about 15 icons all contained in that file. Anyone that has changed the icon for a shortcut is familiar with what I am talking about. Well, I browsed to my program, and it had one icon. I did about everything imaginable in my resx file, and it had one icon. I googled and googled, and it had one icon. From managed code, I could access all the icons in the resx, but Windows refused to see but the one icon.
As it turns out, Microsoft decided to eschew the established resource methodology to make things more easily accessible in managed code. That is all well and good, but having multiple icons in an EXE or DLL is a VERY common thing to see in Windows applications. After a few more hours of googling, I found a few pages that basically said it’s not possible with resx files to have multiple icons (or other resources) that Windows can see via Win32 API calls. Resources in resx files are managed resources, anything Windows is aware of are unmanaged Win32 resources, and ne’er the twain shall meet. I didn’t find any information out there with a satisfactory answer to the question of “How do I add multiple icons to a .net assembly?”, so here for you now is my solution. Note, I had trouble then and I’m having trouble now finding any references to this problem. I know I found some MSDN articles indicating that resx resources aren’t available in unmanaged contexts, but I can’t find much now. Here is an article suggesting a messy post-build solution and an MSDN article on command line csc.exe options (specifically /win32res). Sorry for the lack of references, though.
While trolling around in the project settings for a C# project, I realized that whatever you select for the Icon on the Application tab is the one icon that is visible via the Win32 API. I also noticed the “Resource File” setting right below that. Turns out, you can either pick a single .ico icon file, or provide a .res compiled Win32 resource file for the application. You want the second option, but how to get a compiled .res file? You can’t accomplish this with a C# project, you have to create a new C++ project. Set the “Configuration Type” under “Configuration Properties | General” to Makefile, otherwise you will get all kinds of build errors. Apply that and the available configuration will completely chagne. Now, back in your C# project, I suggest you add all of your icons and other resources to your resx file just as good practice. Visual Studio tends to put these in a Resources folder under the main project folder. This is where your .ico files will live, this is where you will edit the icons if you need to make changes, etc. Back in your C++ Makefile project, add a new item to your project, pick the Resources category, and then “Resource File (.rc)”. This is the non-compiled version of a .res file. If you double-click on the .rc file in Solution Explorer, you will find the Resource View tool window, which you can dock with the Solution Explorer. If you have multiple .rc files in your project, you will get a node per .rc file in the Resource View; right-click on one and “Add Resource…”, pick Icon and hit the Import button. Browse to your C# project, then into the Resources folder, change the file type to Icon Files so they show up, and pick your files (I think multi-select works here). You will get an Icon node under the .rc node in Resource View, and an item for each icon under that. The names will be pretty cryptic, and the order DOES matter a little bit. My suggestion is that you pick the primary program icon first, all by itself, and let that be IDI_ICON1 (I said they were cryptic). Then go back and add any additional icons you want. Pop open the resource.h header file that was created and you will understand how the icons are indexed. You can make changes here, but it is best to get them right from the get go.
Now you have some resource files with resources, but what you need is a .res file. Open up the properties window for your C++ project and find the NMake settings. Edit the “Build Command Line” and add something like
rc.exe /r /fo file.res file.rc for each .rc file in your project (each command on a separate line). Save that, and copy it to the “Rebuild All Command Line”. In “Clean Command Line” add
del *.res and Ok out of the properties. Build the C++ project and you should find some .res files. Back in the properties for your C# application, find the “Resource File” setting I mentioned earlier and browse to the .res file you just built. That’s basically it. Fiddle with the project dependencies on the Solution settings to make sure your .res files always build before the C# projects that use them and you should be good to go. That should be SOOO much easier than it is, but I’m glad it’s at least possible.
[2006.09.27] Update: Alert reader Matt Murphy points out that this won’t work so well for those using C# Express, since C++ projects can’t be used. He recommendsGoRC or ResourceHacker as workarounds. Not sure if you can use those in a way that automatically incorporates the resource building in a post-build event, though. I’ve not used either of these, so I can’t make any recommendations.