4

I'm trying to extract an icon from imageres.dll. Specifically the "My Computer" or "This PC" icon. The problem is that at between Win7 and Win10, the icon number changes. However, the icon group does not (109). Is there a way to get that icon group, and then let the computer figure out which icon to use of that group, in the same way it figures out which icon to use for my app?

This is the code I'm using to get the specific icon via the index:

public class GetIcon { public static Icon Extract(string file, int number) { IntPtr large; IntPtr small; ExtractIconEx(file, number, out large, out small, 1); try { return Icon.FromHandle(small); } catch { return null; } } [DllImport("Shell32.dll", EntryPoint = "ExtractIconExW", CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] private static extern int ExtractIconEx(string sFile, int iIndex, out IntPtr piLargeVersion, out IntPtr piSmallVersion, int amountIcons); } 

Thanks.

1 Answer 1

6

There are a couple of ways to do this. The most reliable, and potentially most time consuming (provided you can't find an existing library), is to parse the PE File (i.e. .exe, .dll) and extract the relevant Icon group data yourself. Here's a good resource for the format: https://msdn.microsoft.com/en-us/library/ms809762.aspx

The second way, can be done easily enough with Windows functions, however there is one caveat. It will only work on PE files that are of the same bit-type as your application. So, for example, if your application is 64-bit, it will only work on 64-bit PE files.

Here's a function I just wrote - based off this: https://msdn.microsoft.com/en-us/library/windows/desktop/ms648051(v=vs.85).aspx#_win32_Sharing_Icon_Resources, that takes a file name, group number, and desired icon size, and returns a System.Drawing.Icon

[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Ansi)] static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName); [DllImport("kernel32.dll")] static extern IntPtr FindResource(IntPtr hModule, int lpName, int lpType); [DllImport("kernel32.dll", SetLastError = true)] static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo); [DllImport("kernel32.dll")] static extern IntPtr LockResource(IntPtr hResData); [DllImport("user32.dll")] static extern int LookupIconIdFromDirectoryEx(byte[] presbits, bool fIcon, int cxDesired, int cyDesired, uint Flags); [DllImport("user32.dll")] static extern IntPtr CreateIconFromResourceEx(byte[] pbIconBits, uint cbIconBits, bool fIcon, uint dwVersion, int cxDesired, int cyDesired, uint uFlags); [DllImport("kernel32.dll", SetLastError = true)] static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo); const int RT_GROUP_ICON = 14; const int RT_ICON = 0x00000003; private System.Drawing.Icon GetIconFromGroup(string file, int groupId, int size) { IntPtr hExe = LoadLibrary(file); if(hExe != IntPtr.Zero) { IntPtr hResource = FindResource(hExe, groupId, RT_GROUP_ICON); IntPtr hMem = LoadResource(hExe, hResource); IntPtr lpResourcePtr = LockResource(hMem); uint sz = SizeofResource(hExe, hResource); byte[] lpResource = new byte[sz]; Marshal.Copy(lpResourcePtr, lpResource, 0, (int)sz); int nID = LookupIconIdFromDirectoryEx(lpResource, true, size, size, 0x0000); hResource = FindResource(hExe, nID, RT_ICON); hMem = LoadResource(hExe, hResource); lpResourcePtr = LockResource(hMem); sz = SizeofResource(hExe, hResource); lpResource = new byte[sz]; Marshal.Copy(lpResourcePtr, lpResource, 0, (int)sz); IntPtr hIcon = CreateIconFromResourceEx(lpResource, sz, true, 0x00030000, size, size, 0); System.Drawing.Icon testIco = System.Drawing.Icon.FromHandle(hIcon); return testIco; } return null; } 

The process basically works like this:

  1. use LoadLibrary to load up the .exe or .dll file
  2. Get the handle & data of the RT_GROUP_ICON resource
  3. Pass the data, along with the desired size to LookupIconIdFromDirectoryEx, to get the icon index
  4. From there, you can either use ExtractIconEx, or just repeat step 2 with the icon index, and RT_ICON instead, followed by using CreateIconFromResourceEx to get your icon handle.
Sign up to request clarification or add additional context in comments.

2 Comments

I'm not going to try this, since this project is long past, but I appreciate the time you put into the answer. Hopefully it will help somebody in the future.
I came across this question, while trying to do the same thing - since I figured it out, I thought I'd share :p I'll most likely end up doing the first method myself, due to that caveat. But yes, hopefully this helps someone else.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.