EnumResourceNames issue - unknown error

2019-05-24 11:04发布

问题:

I was recently working with resources from secondary libraries/binary modules and encountered a strange error.

I have two native WinAPI references:

[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool EnumResourceNames(IntPtr hModule, int lpszType, EnumResNameProc lpEnumFunc, IntPtr lParam);

[DllImport("kernel32.dll", SetLastError=true)]
public extern static IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, int dwFlags);

When I call LoadLibraryEx, I am getting the IntPtr instance - just what I need:

IntPtr x = WinApi.LoadLibraryEx(@"D:\Software\Reflector\Reflector.exe",IntPtr.Zero,2);
Debug.WriteLine(x.ToInt32());

However, when I try to enumerate icon resources (defined by the ID = 3):

Debug.WriteLine(WinApi.EnumResourceNames(x, 3, new EnumResNameProc(ListCallback), IntPtr.Zero));
Debug.WriteLine(Marshal.GetLastWin32Error());

I am getting this error code (returned by GetLastError):

-532462766

This usually means there is an unknown error, as far as I know, but I am just curious - what could be the problem with listing resources from the executable?

回答1:

-532462766 == 0xe0434352. The last three hex pairs spell "CCR", a common trick that Microsoft programmers use to try to come up with an easily recognizable exception code. The exact meaning is quite mysterious, beyond it being commonly associated with managed code and seemingly being very low level in a sub-system that doesn't normally fail to produce a meaningful managed exception.

There is an excellent candidate reason for that mysterious exception, your EnumResources pinvoke declaration is wrong. The 2nd argument is IntPtr, not int. That has some odds of going kaboom on a 64-bit operating system.

Please post back if you ever figure out what CCR means.


using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Diagnostics;

class Program {
    static void Main(string[] args) {
        try {
            IntPtr module = LoadLibraryEx(@"C:\windows\system32\user32.dll", IntPtr.Zero, 2);
            if (module == IntPtr.Zero) throw new Win32Exception();
            if (!EnumResourceNames(module, (IntPtr)3, new EnumResNameProc(ListCallback), IntPtr.Zero))
                throw new Win32Exception();
        }
        catch (Win32Exception ex) {
            Console.WriteLine(ex.Message);
        }
        Console.ReadLine();
    }

    static bool ListCallback(IntPtr hModule, IntPtr type, IntPtr name, IntPtr lp) {
        long idorname = (long)name;
        if (idorname >> 16 == 0) Console.WriteLine("#{0}", idorname);
        else Console.WriteLine(Marshal.PtrToStringAnsi(name));
        return true;
    }

    public delegate bool EnumResNameProc(IntPtr hModule, IntPtr type, IntPtr name, IntPtr lp);
    [DllImport("kernel32.dll", SetLastError = true)]
    public extern static bool EnumResourceNames(IntPtr hModule, IntPtr type, EnumResNameProc lpEnumFunc, IntPtr lParam);
    [DllImport("kernel32.dll", SetLastError = true)]
    public extern static IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, int dwFlags);
}


回答2:

Hans Passant has it correct, but to elaborate on the error message, 0xe0434352 is the generic error code for a .NET exception. If you run this from the Visual Studio debugger you'll see that a System.ArgumentException is being raised when EnumResourceNames tries to invoke the callback. The error message is:

The pointer passed in as a String must not be in the bottom 64K of the process's address space.

This exception gets caught by EnumResourceNames and turns into a failure. The solution, as Hans showed, is that the callback function must take the second and third parameters as IntPtr rather than string.