Проблема 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, а не как строку.