[英]Local keyboard hook terminates the target process
I'm trying to install a LOCAL keyboard hook into a process using a C++ DLL from managed C# code, as follows:我正在尝试使用来自托管 C# 代码的 C++ DLL 将本地键盘挂钩安装到进程中,如下所示:
public class KeyboardHook
{
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("DLL.dll", CallingConvention = CallingConvention.Cdecl)]
protected static extern IntPtr Install(int idHook, IntPtr windowHandle, HookCallback callback);
private IntPtr instance;
private HookCallback handler;
public KeyboardHook()
{
instance = IntPtr.Zero;
handler = Callback;
}
public void Install(Process process)
{
instance = Install(WH_KEYBOARD, process.MainWindowHandle, handler);
}
public void Uninstall()
{
UnhookWindowsHookEx(instance);
}
private IntPtr Callback(int nCode, IntPtr wParam, IntPtr lParam)
{
// TODO Use hook data here
return CallNextHookEx(instance, nCode, wParam, lParam);
}
}
The C++ DLL code should be just enough to dispatch the hook data to the C#'s Callback
function, like this: C++ DLL 代码应该足以将挂钩数据分派到 C# 的
Callback
function,如下所示:
// dll.h
#pragma data_seg(".foo")
HOOKPROC _hookCallback = NULL;
#pragma comment(linker, "/SECTION:.foo,RWS")
#pragma data_seg()
static HINSTANCE _moduleHandle = NULL;
extern "C" __declspec(dllexport)
HHOOK Install(int idHook, HWND window, HOOKPROC hookCallback);
extern "C" __declspec(dllexport)
LRESULT CALLBACK HookProc(int code, WPARAM wparam, LPARAM lparam);
// dll.cpp
HHOOK Install(int idHook, HWND window, HOOKPROC hookCallback)
{
auto processId = 0ul;
auto threadId = GetWindowThreadProcessId(window, &processId);
_hookCallback = hookCallback;
_hookCallback(-1, NULL, NULL); // Test callback (works)
return SetWindowsHookExA(idHook, HookProc, _moduleHandle, threadId);
}
LRESULT CALLBACK HookProc(int code, WPARAM wParam, LPARAM lParam)
{
// The following line terminates the target process
return _hookCallback(code, wParam, lParam);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
_moduleHandle = hModule;
break;
case DLL_PROCESS_DETACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}
The local hook is successfuly installed as the DLL KeyboardProc function is triggered, however, calling the C# delegate from the C++ DLL terminates the application. The local hook is successfuly installed as the DLL KeyboardProc function is triggered, however, calling the C# delegate from the C++ DLL terminates the application. Why?
为什么?
NOTES:笔记:
_hookCallback_
is not null when HookProc
is triggered (although I'm not sure whether it points to a valid memory address) _hookCallback_
在触发 HookProc 时不是HookProc
(虽然我不确定它是否指向有效的 memory 地址)KeyboardProc::handler
shouldn't be garbage collected, as the KeyboardProc instance lives for as long as the C# application does KeyboardProc::handler
不应被垃圾回收,因为 KeyboardProc 实例的存在时间与 C# 应用程序一样长_hookCallback
function pointer within the DLL's Install
function works flawlessly, but terminates the process when used inside the HookProc
function.Install
function 中使用_hookCallback
function 指针可以完美地工作,但在HookProc
ZC1C425268E68385D1AB5074C17A9 中使用时会终止进程。WHAT ALSO HAVE BEEN TRIED:还尝试过什么:
Making HookCallback
a UnmanagedFunctionPointer
, as well as using Marshal.GetFunctionPointerForDelegate
and telling the garbage collector not to collect the handler
property by using GCHandle.Alloc()
and GC.KeepAlive()
:使
HookCallback
成为UnmanagedFunctionPointer
,以及使用Marshal.GetFunctionPointerForDelegate
并使用GCHandle.Alloc()
和GC.KeepAlive()
告诉垃圾收集器不要收集handler
属性:
public class KeyboardHook
{
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("DLL32.dll", CallingConvention = CallingConvention.Cdecl)]
protected static extern IntPtr Install(int idHook, IntPtr windowHandle, IntPtr delegatePointer);
// ...
protected readonly GCHandle garbageCollectorHandle;
public KeyboardHook()
{
instance = IntPtr.Zero;
handler = new HookCallback(Callback);
garbageCollectorHandle = GCHandle.Alloc(handler); // Or GC.KeepAlive(handler)
}
~KeyboardHook()
{
garbageCollectorHandle.Free();
}
public void Install(Process process)
{
IntPtr delegatePointer = Marshal.GetFunctionPointerForDelegate(handler);
instance = Install(WH_KEYBOARD, process.MainWindowHandle, delegatePointer);
}
// ...
}
Using handler
directly into SetWindowsHookExA
(C++):直接在
SetWindowsHookExA
(C++) 中使用handler
:
HHOOK Install(int idHook, HWND window, HOOKPROC hookCallback)
{
auto processId = 0ul;
auto threadId = GetWindowThreadProcessId(window, &processId);
_hookCallback = hookCallback;
return SetWindowsHookExA(idHook, hookCallback, _moduleHandle, threadId);
}
The C# code is calling the DLL's Install()
function with the 2nd and 3rd parameters flipped. C# 代码调用 DLL 的
Install()
function 并翻转了第二个和第三个参数。
Change this line:更改此行:
instance = Install(WH_KEYBOARD, handler, process.MainWindowHandle);
To this:对此:
instance = Install(WH_KEYBOARD, process.MainWindowHandle, handler);
UPDATE : Also, you did not show the C# code's declaration of the DLL'S Install()
function.更新:此外,您没有显示 C# 代码对 DLL 的
Install()
function 的声明。 But in the C++ code, the Install()
function is not specifying a calling convention, so it will (likely, depending on compiler config) default to __cdecl
( HookProc()
is using the __stdcall
calling convention).但是在 C++ 代码中,
Install()
function 没有指定调用约定,因此它(可能,取决于编译器配置)默认为__cdecl
( HookProc()
使用的是__stdcall
调用约定)。 So make sure the C# code is specifying the correct calling convention when importing the Install()
function.因此,请确保 C# 代码在导入
Install()
function 时指定了正确的调用约定。 C# delegate
s use __stdcall
by default. C#
delegate
默认使用__stdcall
。
The process terminates due to accessing an invalid memory address.该进程因访问无效的 memory 地址而终止。
Every Windows process have different virtual memory regions.每个 Windows 进程都有不同的虚拟 memory区域。 In other words, the
0x1234
memory address within the process A
does not point to the same value/function as 0x1234
memory address within the process B
, since 0x1234
is a virtual memory address bound to it's corresponding process. In other words, the
0x1234
memory address within the process A
does not point to the same value/function as 0x1234
memory address within the process B
, since 0x1234
is a virtual memory address bound to it's corresponding process.
In order to achieve that communication between a C++ DLL and a C# application (any different process overall), an inter-process communication (IPC) is required.为了实现 C++ DLL 和 C# 应用程序(任何不同的进程)之间的通信,需要进程间通信(IPC)。
For those interested on this specific case, I ended up creating an invisible dummy window to serve as a central point for receiving messages through SendMessage
calls from the DLL.对于那些对这个特定案例感兴趣的人,我最终创建了一个不可见的虚拟 window作为通过来自 DLL 的
SendMessage
调用接收消息的中心点。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.