Проблема EnumResourceNames - неизвестная ошибка

Недавно я работал с ресурсами из вторичных библиотек / бинарных модулей и обнаружил странную ошибку.

У меня есть две родные ссылки на WinAPI:

[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);

Когда я вызываю LoadLibraryEx, я получаю экземпляр IntPtr - именно то, что мне нужно:

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

Тем не менее, когда я пытаюсь перечислить ресурсы значков (определяется ID = 3):

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

Я получаю этот код ошибки (возвращается GetLastError):

-532462766

Насколько я знаю, это обычно означает, что существует неизвестная ошибка, но мне просто любопытно - в чем может быть проблема с перечислением ресурсов из исполняемого файла?

2 ответа

Решение

-532462766 == 0xe0434352. Последние три шестнадцатеричных пары означают "CCR", распространенный прием, который используют программисты Microsoft, чтобы попытаться придумать легко узнаваемый код исключения. Точное значение весьма загадочно, за исключением того, что оно обычно ассоциируется с управляемым кодом и, по-видимому, является очень низким уровнем в подсистеме, которая обычно не дает значимого управляемого исключения.

Для этого загадочного исключения есть отличная причина, ваша декларация EnumResources неверна. Второй аргумент - IntPtr, а не int. Это имеет некоторые шансы перейти на kaboom в 64-битной операционной системе.

Пожалуйста, напишите обратно, если вы когда-нибудь выясните, что означает CCR.


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);
}

У Ханса Пассанта все правильно, но для уточнения сообщения об ошибке 0xe0434352 - это общий код ошибки для исключения.NET. Если вы запустите это из отладчика Visual Studio, вы увидите, что System.ArgumentException вызывается, когда EnumResourceNames пытается вызвать обратный вызов. Сообщение об ошибке:

Указатель, передаваемый в виде строки, не должен находиться в нижних 64 КБ адресного пространства процесса.

Это исключение перехватывается EnumResourceNames и превращается в сбой. Решение, как показал Ганс, состоит в том, что функция обратного вызова должна принимать второй и третий параметры как IntPtr, а не как строку.

Другие вопросы по тегам