我在此页面上发现了类似的问题,但我似乎无法弄清楚如何解释答案或弄清楚它们是否真的重复。
以下是我发现的可能重复项,并附有评论:
在 32 位机器上为 .NET 4.0 框架编译时,SetWindowsHookEx 返回 0
我的似乎没有返回 0,但我注意到当它崩溃时报告的句柄(32 位上的 .NET 4.0)与它运行时报告的句柄(32 位上的 .NET 3.5)有很大不同,比如崩溃句柄 = 523727,工作句柄 = 172738378。
在 VS2008 调试器中调用 SetWindowsHookEx 总是返回 NULL
在 Visual Studio 之外运行时,我可以重现我的问题
-
这似乎最有希望,除了对已删除答案的评论提到我应该使用 LoadLibrary 和 GetProcAddress 在 .NET 4.0 中加载 user32.dll,因为有关加载程序集的某些内容发生了变化。但是,我很确定这是我自己的模块,它找不到,但我不知道这是否适用。
汉斯·帕桑特(Hans Passant)对已删除的最后一个答案的评论如下:
您使用的是 .NET 4.0 吗?它的 CLR 改变了程序集的加载方式,不再有 LoadLibrary 调用,也不会有它们的模块句柄。改用 GetEntryAssembly() 将是另一种解决方法。– Hans Passant 5 月 5 日 19:43
那么,这里的词是什么?您使用的是 .NET 4.0 吗?您是否尝试使用 LoadLibrary("user32.dll") 来获取可用的 DLL 句柄?– Hans Passant 5 月 6 日 15:43
我很确定我不需要这样做,但显然我不是 100% 确定。如果我需要更改它,我留下的问题是为什么它在编译为 64 位操作系统时可以在 64 位操作系统上运行Any CPU
,但在任何配置中都不能在 32 位操作系统上运行。
如果在加载 .NET 程序集方面确实发生了一些变化,以至于我无法正确处理类库,那么我有以下问题:
- 有什么办法可以欺骗它做我想做的事,而不必降级到 .NET 3.5 或将钩子库更改为非托管?
- 为什么在 64 位操作系统上运行时它可以工作,但在 32 位操作系统上却不行?
背景
我在 .NET 4.0 中构建了一个程序,它使用带有 WH_KEYBOARD_LL 钩子类型的 SetWindowsHookEx 来捕获按键。这在我的 64 位 Windows 7 上运行良好,但在 32 位 Windows 7 上安装键盘挂钩时会因“找不到模块”而崩溃。
这是我尝试过的:
- 为 x86 编译,在 64 位操作系统上运行,因“找不到模块”而崩溃
- 为 x86 编译,在 32 位操作系统上运行,崩溃
- 为任何 CPU 编译,在 64 位操作系统上运行,运行良好
- 为任何 CPU 编译,在 32 位操作系统上运行,崩溃
- 切换到.NET 3.5,重复以上四种情况,都行
我宁愿不将我的代码切换到 .NET 3.5,因为我正在使用我的一些类库来简化工作,而最新的代码仅在 .NET 4.0 中。
如果需要,您可以下载包含所有内容的 .ZIP 文件作为 Visual Studio 2010 项目,也可以粘贴以下两个文件。
如果你想沿着这条路线重新创建:
- 创建一个新的控制台项目,.NET 4.0
- 添加另一个类库项目,也是 .NET 4.0
- 从控制台程序项目中添加对类库项目的引用
- 将下面的 Program.cs 内容粘贴到控制台项目中的 Program.cs 文件中
- 将下面的 Hook.cs 内容粘贴到类库项目中的文件中。您可以将其粘贴到 Class1.cs 默认文件中,或添加另一个文件。你不能把它放到控制台项目中
然后编译运行,测试各种配置。
程序.cs
using System;
using HookLib;
namespace HookTest
{
class Program
{
static void Main()
{
var hook = new Hook();
Console.Out.WriteLine("hooking");
hook.Enable();
Console.Out.WriteLine("hooked");
Console.Out.WriteLine("unhooking");
hook.Disable();
Console.Out.WriteLine("unhooked");
}
}
}
钩子.cs
using System;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.InteropServices;
namespace HookLib
{
public class Hook
{
private IntPtr _Handle;
private HookProcDelegate _Hook;
public void Enable()
{
Module module = Assembly.GetExecutingAssembly().GetModules()[0];
if (module != null)
Console.Out.WriteLine("found module");
IntPtr moduleHandle = Marshal.GetHINSTANCE(module);
if (moduleHandle != IntPtr.Zero)
Console.Out.WriteLine("got module handle: " +
moduleHandle.ToString());
_Hook = HookProc;
_Handle = SetWindowsHookEx(WH_KEYBOARD_LL, _Hook, moduleHandle, 0);
if (_Handle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
public void Disable()
{
bool ok = UnhookWindowsHookEx(_Handle);
_Handle = IntPtr.Zero;
if (!ok)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
private delegate int HookProcDelegate(
int code, IntPtr wParam, IntPtr lParam);
private int HookProc(int code, IntPtr wParam, IntPtr lParam)
{
return CallNextHookEx(_Handle, code, wParam, lParam);
}
private const int WH_KEYBOARD_LL = 13;
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(
int hookType, HookProcDelegate lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", SetLastError = true)]
private static extern int CallNextHookEx(
IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
}
}