简体   繁体   中英

C#: How to tell if an EXE has an icon?

I'm looking for a way to tell whether or not an EXE file contains an application icon. From the answer here , I tried this:

bool hasIcon = Icon.ExtractAssociatedIcon(exe) != null;

But this seems to work even if the EXE has no icon. Is there a way to detect this in .NET?


edit: I'm OK with solutions involving P/Invoke.

You can get the IDI_APPLICATION icon through SystemIcons.Application property from SystemIcons class

if (Icon.ExtractAssociatedIcon(exe).Equals(SystemIcons.Application)) 
{
    ...
}

See MSDN for more details.

Try this. Define your pinvoke like this:

[DllImport("user32.dll")]
internal static extern IntPtr LoadImage(IntPtr hInst, IntPtr name, uint type, int cxDesired, int cyDesired, uint fuLoad);

[DllImport("kernel32.dll")]
static extern bool EnumResourceNames(IntPtr hModule, int dwID, EnumResNameProcDelegate lpEnumFunc, IntPtr lParam);

delegate bool EnumResNameProcDelegate(IntPtr hModule, IntPtr lpszType, IntPtr lpszName, IntPtr lParam);

[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr LoadLibraryEx(string name, IntPtr handle, uint dwFlags);

private const int LOAD_LIBRARY_AS_DATAFILE = 0x00000002;
private const int LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020;
private const int IMAGE_ICON = 1;
private const int RT_GROUP_ICON = 14;

Then you can write a function like this:

static bool HasIcon(string path)
{
    // This loads the exe into the process address space, which is necessary
    // for LoadImage / LoadIcon to work note, that LOAD_LIBRARY_AS_DATAFILE
    // allows loading a 32-bit image into 64-bit process which is otherwise impossible
    IntPtr moduleHandle = LoadLibraryEx(path, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);

    if (moduleHandle == IntPtr.Zero)
    {
        throw new ApplicationException("Cannot load executable");
    }

    IntPtr index = IntPtr.Zero;
    bool hasIndex = false;

    bool enumerated = EnumResourceNames(moduleHandle, RT_GROUP_ICON, (module, type, name, param) =>
    {
        index = name;
        hasIndex = true;
        // Only load first icon and bail out
        return false;
    }, IntPtr.Zero);

    if (!enumerated || !hasIndex)
    {
        return false;
    }

    // Strictly speaking you do not need this you can return true now
    // This is to demonstrate how to access the icon that was found on
    // the previous step
    IntPtr result = LoadImage(moduleHandle, index, IMAGE_ICON, 0, 0, 0);
    if (result == IntPtr.Zero)
    {
        return false;
    }

    return true;
}

It has added bonus that if you want to, after LoadImage you can load the icon with

Icon icon = Icon.FromHandle(result);

and do whatever you want with that.

Important note: I have not done any clean up in the function, so you cannot use it as is, you'll leak handles/memory. Proper clean up is left as an exercise for the reader. Read the description of every of the winapi function used in MSDN and call corresponding clean up functions as needed.

An alternate way using shell32 api can be found here , although I don't know if it has the same problem you encountered.

Also, old, but still very relevant article: https://msdn.microsoft.com/en-us/library/ms997538.aspx

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM