0

I have written the following code which is intended to return a List<Icon> which is a list of all the Icons found in the file.

private List<Icon> GetIconsFromFile(string file)
{
    List<Icon> icons = new List<Icon>();
    IntPtr[] large = new IntPtr[999];
    IntPtr[] small = new IntPtr[999];
    Icon ico;
    try
    {
        int count = ExtractIconEx(file, -1, large, small, 999);
        if (count > 0)
        {
            large = new IntPtr[count - 1];
            small = new IntPtr[count - 1];

            ExtractIconEx(file, 0, large, small, count);
            for (int x = 0; x < count; x++)
            {
                ico = (Icon)Icon.FromHandle(large[x]).Clone();
                icons.Add(ico);
            }
        }
    }
    catch (Exception e)
    {
        System.Diagnostics.Debug.WriteLine(e.Message);
    } finally
    {
        foreach (IntPtr ptr in large) {
            if (ptr != IntPtr.Zero) {
                DestroyIcon(ptr);
            }
        }
        foreach (IntPtr ptr in small) {
            if (ptr != IntPtr.Zero) {
                DestroyIcon(ptr);
            }
        }
    }
    return icons;
}

When I use the code as follows :

var icons = GetIconsFromFile(@"c:\windows\explorer.exe");

Here is what I get when I step through the code :

int count = ExtractIconEx(file, -1, large, small, 999);

count is 29

for (int x = 0; x < count; x++)
{
    ico = (Icon)Icon.FromHandle(large[x]).Clone();
    icons.Add(ico);
}

when x reaches 28, an exception is thrown. and the value of x jumps to 39

This doesn't make sense as this would imply that when the count of 29 is returned, that there are actually 27 icons making the first icon a value of -1. If the count starts at 0, then 29 - 1 = 28, and since 28 is less than count (29), no exception should be raised as it is within the bounds.

Why is this code causing an out-of-bounds error when the api is returning (presumably) the correct number of icons in this file ? -- and what can I do aside from change x < count to x < (count - 1)

Kraang Prime
  • 9,981
  • 10
  • 58
  • 124
  • [How the shell converts an icon location into an icon](https://blogs.msdn.microsoft.com/oldnewthing/20100505-00/?p=14153) – Ňɏssa Pøngjǣrdenlarp Aug 05 '16 at 00:19
  • @Plutonix - that doesn't really apply here as I do actually get the resource at id 1, and forward. The last working id where the count returns 29, is 27 -- starting from 0. So instead of 28 or 29 icons, the index is cut short – Kraang Prime Aug 05 '16 at 00:24
  • Modified this to loop on intptr instead of a for x loop. problem solved !! still weird tho. – Kraang Prime Aug 05 '16 at 01:24
  • The code you posted shows you calling `ExtractIconEx` with the second parameter of 0, but then you say when you're single-stepping that the second parameter is -1. Calling with -1 will cause it to start returning icon index 1--the second icon in the file. But the return value then should be 28, not 29. The behavior you describe contradicts the documentation, which makes me think that maybe the doc is wrong. – Jim Mischel Aug 05 '16 at 01:37
  • @JimMischel - I agree, the behaviour is very peculiar. When I pass -1, it gives me the count, which I believe is correct (hunting a way to view the icons to see if it matches), giving me 29 which I know is 0 index based. So logical step when getting out of bounds at 29, is deduct 1 from the count. In this case, going 1 less than the count (eg `x < count`) still gave out of bounds. Taking the route to loop on all IntPtr where the value is not IntPtr.Zero, ensures that I only work with values that exist. I am working on another strange Icon retrieval question using a different API. – Kraang Prime Aug 05 '16 at 01:52
  • @JimMischel what I found was when I pass -1, it gives me a count, but all the IntPtr handles are wrong (aka, can't get any icons from it). Passing a 0, gets me all the icons and should fill the IntPtr array as it was redefined to allow it. It does fill the array, but seems to skip the last two or something. It's just bizarre behavior. When the exception occurs, inspecting the x variable shows it's value was at `39` which is 10 beyond the `count` value, but the exception is caused at `28` being out of bounds leaving 0-27 as the total icons - so why 29 ? (also, i think the docs are shoddy) – Kraang Prime Aug 05 '16 at 02:04

1 Answers1

0

While the specific cause as to why the icon ptr's retrieved does not match the icon count as per the api documentation, I have a workaround to the issue which simply ignores any stray elements where the IntPtr value is IntPtr.Zero by changing the for(int x; x < count; x++) loop to a foreach(var x in large) loop with an if(x != IntPtr.Zero) conditional check in place. The method rewritten is as follows :

private List<Icon> GetIconsFromFile(string file)
{
    List<Icon> icons = new List<Icon>();
    IntPtr[] large = new IntPtr[999];
    IntPtr[] small = new IntPtr[999];
    Icon ico;
    try
    {
        int count = ExtractIconEx(file, -1, large, small, 999);
        if (count > 0)
        {
            large = new IntPtr[count - 1];
            small = new IntPtr[count - 1];

            ExtractIconEx(file, 0, large, small, count);
            foreach(var x in large)
            {
                if(x != IntPtr.Zero) {
                   ico = (Icon)Icon.FromHandle(x).Clone();
                   icons.Add(ico);
                }
            }
        }
    }
    catch (Exception e)
    {
        System.Diagnostics.Debug.WriteLine(e.Message);
    } finally
    {
        foreach (IntPtr ptr in large) {
            if (ptr != IntPtr.Zero) {
                DestroyIcon(ptr);
            }
        }
        foreach (IntPtr ptr in small) {
            if (ptr != IntPtr.Zero) {
                DestroyIcon(ptr);
            }
        }
    }
    return icons;
}

Still open to ideas as to why the count is incorrect or there are no IntPtr values where there should be at the tail end of the large array.

Kraang Prime
  • 9,981
  • 10
  • 58
  • 124