在32位机器上使用.NET 4.0框架编译时,SetWindowsHookEx返回值为0。

11

我正在尝试设置一个低级别的Windows键盘钩子,以便在应用程序不在焦点时捕获三个按键。为此,我调用了SetWindowsHookEx函数。

// Create an instance of HookProc.
KeyboardHookProcedure = new HookProc(KeyboardHookProc);
//install hook
hKeyboardHook = SetWindowsHookEx(
    WH_KEYBOARD_LL,
    KeyboardHookProcedure,
    Marshal.GetHINSTANCE(
        Assembly.GetExecutingAssembly().GetModules()[0]),
    0);
//If SetWindowsHookEx fails.
if (hKeyboardHook == 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
    Stop(false, true, false);
    //Initializes and throws a new instance of the Win32Exception class with the specified error. 
    throw new Win32Exception(errorCode);
}

这在32位和64位机器上使用.NET Framework 3.5曾经能够工作,但升级到.NET Framework 4.0后在32位机器上停止工作。

有人知道如何解决这个问题,以便我可以使用4.0框架,并使其在32位和64位机器上都能工作吗?


1
在.NET 4.0中,SetWindowsHookEx对我返回0,并显示无效的钩子过程错误。 - tofutim
4个回答

27

像这样导入dll:

[DllImport("kernel32.dll")]
    public static extern IntPtr GetModuleHandle(string name); 
那么使用。
GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName)
替换文本
Marshal.GetHINSTANCE(
    Assembly.GetExecutingAssembly().GetModules()[0]

1
遇到了同样的问题,解决方案是使用建议的代码更改,使用 GetModuleHandle(...) 获取句柄。 - jensrodi
谢谢。这对我来说完美地解决了问题。我没有得到主模块,而是得到了当前模块,并且它返回了0。 - Vince Anzelone
我也遇到同样的问题。正在编写一个快速窗口管理器,这样我就可以按下一个键,在屏幕上拖动一个矩形,并让窗口自动对齐。我找出了我很久之前为.NET 2.0编写的旧版“GlobalHooks”模块,并发现SetWindowsHookEx返回零。这解决了问题。我还注意到需要循环遍历所有按键调用GetAsyncKeyState,而不仅仅是单个调用GetKeyboardState,因为当其他应用程序在前台运行时,它会遇到线程问题,比如无法正确获取修饰键状态。必须使用GetAsyncKeyState。 - Triynko

2

SetWindowsHookEx的文档中得知:

hMod [输入]
HINSTANCE
指向lpfn参数所指钩子过程的DLL句柄。如果dwThreadId参数指定了由当前进程创建的线程,并且钩子过程在与当前进程相关联的代码中,则必须将hMod参数设置为NULL。

因此,您应该传递IntPtr.Zero以获取NULL。

//install hook
  hKeyboardHook = SetWindowsHookEx(
    WH_KEYBOARD_LL,
    KeyboardHookProcedure,
    IntPtr.Zero,
    0);

1
通过分别针对每个平台解决了这个问题。配置VS编译Win32和Win64版本,并在x86和x64机器上部署它们的相应二进制文件。
Win32或x86可以在32位和64位机器上运行。

0

Hans Passant

Any module handle will do since it doesn't actually get used for low-level hooks, no DLL needs to be injected to make them work. Some care in selecting one is required for .NET 4 since its CLR no longer fakes module handles for pure managed assemblies. A good one to use is the one you get out of pinvoking LoadLibrary("user32.dll") since it is always already loaded. You don't have to call FreeLibrary().

You'll need this declaration to call LoadLibrary:

[DllImport("kernel32", SetLastError=true, CharSet = CharSet.Auto)]
private static extern IntPtr LoadLibrary(string fileName);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接