Ошибка SetWindowsHookEx с ошибкой 126

Я пытаюсь использовать библиотеку Gma.UserActivityMonitor в проекте, и я столкнулся с ошибкой, которую я не могу преодолеть самостоятельно.

В файле HookManager.Callbacks.cs есть статический метод с именем EnsureSubscribedToGlobalMouseEvents со следующим кодом (более или менее):

var asm = Assembly.GetExecutingAssembly().GetModules()[0];
var mar = Marshal.GetHINSTANCE(asm);
s_MouseHookHandle = SetWindowsHookEx(
    WH_MOUSE_LL,
    s_MouseDelegate,
    mar,
    0);
//If SetWindowsHookEx fails.
if (s_MouseHookHandle == 0)
{
    //Returns the error code returned by the last unmanaged function called using platform invoke that has the DllImportAttribute.SetLastError flag set. 
    int errorCode = Marshal.GetLastWin32Error();
    //do cleanup

    //Initializes and throws a new instance of the Win32Exception class with the specified error. 
    throw new Win32Exception(errorCode);
}

SetWindowsHookEx всегда возвращает 0, и приведенный выше код продолжает выдавать исключение с сообщением The specified module could not be found, а вызов Marshal.GetLastWin32Error возвращает код 126. Я могу успешно запустить демонстрационную версию, предоставленную с оригинальным проектом Gma.UserActivityMonitor, но поскольку мой проект слишком сложный, чтобы объяснить здесь, я не могу подробно рассказать о его различии с моим. Я просто надеюсь, что кто-то может ослепить проблему.

Кстати, в проекте FAQ сказано, что у других проблема с моей проблемой (с ошибкой SetWindowsHookEx return), когда Enable Visual Studio hosting process проверяется только тогда, когда проект отлаживается. Поэтому я снял флажок с этой коробки и все еще имею ту же проблему, а не только в режиме отладки, но также когда я дважды щелкаю файл релиза в проводнике Windows (без участия Visual Studio).

Чтобы предоставить дополнительную информацию, в демонстрационном проекте (который отлично работает) переменная asm указывает на {Gma.UserActivityMonitor.dll} и то же самое в моем проекте, который генерируется исключение!

Ответ 1

Этот вид кода больше не работает на .NET 4 и выше. Код ошибки, который вы получаете, в остальном описательный, 126 = "Указанный модуль не найден". Что говорит вам, что переменная "mar" содержит мусор.

.NET 4 имел довольно значительное изменение CLR, он больше не претендует на то, что закодированный код живет внутри неуправляемых модулей. Итак, Marshal.GetHINSTANCE() больше не работает. Затем код становится неаккуратным, он забывает проверить возвращаемое значение, чтобы проверить его на (IntPtr) -1, чтобы обнаружить отказ и объявить катастрофу. Довольно распространенный для кода, который вы найдете в Codeproject, множество ошибок и неряшливости, которые не могут быть исправлены участниками. Не модель SO:)

SetWindowsHookEx() немного неудобно для крючков низкого уровня. Он требует действительного дескриптора модуля и проверяет его, но фактически не использует его. Это было исправлено в Windows, где-то рядом с Win7 SP1. Хотя это, безусловно, предназначалось для использования в качестве полезного исправления, это фактически сделало проблему хуже. Потому что теперь он может работать на вашей машине, но не на вашей пользовательской машине.

Anyhoo, исправление простое, вам просто нужно выкачать допустимый дескриптор модуля. Вы можете получить его из модуля, который всегда присутствует в управляемом приложении, вам нужно pinvoke LoadLibrary, чтобы получить его:

var mar = LoadLibrary("user32.dll");
s_MouseHookHandle = SetWindowsHookEx(
    WH_MOUSE_LL,
    s_MouseDelegate,
    mar,
    0);

Не нужно звонить FreeLibrary(), этот модуль остается загруженным до тех пор, пока ваша программа не закончится.