SetWindowsHookEx 在另一个进程线程 ID 上不与 WH_CBT 一起工作(但在自己的线程 ID 上工作)
SetWindowsHookEx not working with WH_CBT on another process thread ID (but works on own thread ID)
我已经为此绞尽脑汁好几天了,需要一些帮助!当 CBT 挂钩挂接到我的 WPF 应用程序的线程 ID 时,我可以使用 SetWindowsHookEx 检测到它,但我无法让它挂接到另一个进程的线程 ID window 当 window 成为前台应用程序时。
图 1:显示我可以获取用于检测 window 最大化主应用程序线程 ID 的 CBT 挂钩
图 2:显示我在侦听另一个应用程序的线程 ID 时无法获取 CBT 挂钩,这会使应用程序崩溃!
我想避免发送 ThreadId=0 并使其成为一个完整的全局钩子,因为我知道我只想听前台应用程序,而不是桌面上的所有应用程序。对于当前具有前景焦点的任何 window,我希望能够在它们发生之前收听一些 window 事件(WH_CBT 根据我的理解执行此操作)。
同样,以下代码在当前 WPF 应用程序成为前台应用程序时有效,但当另一个应用程序的 window 成为前台(例如记事本、Internet Explorer、文件资源管理器、Chrome等)。
完整代码:(link to github zip file)
这里有一些代码片段来展示我所做的:
定义回调的 DLL (inject.dll):
inject.h:片段
typedef void(__stdcall* MYFUNCPTR)(int code, WPARAM wparam, LPARAM lparam);
extern "C" __declspec(dllexport) void Init(MYFUNCPTR funcPtr);
extern "C" __declspec(dllexport) LRESULT CALLBACK CbtProcCallback(int code, WPARAM wparam, LPARAM lparam);
WPARAM wparam, LPARAM lparam);
MYFUNCPTR _handler = 0;
inject.cpp:片段
void Init(MYFUNCPTR funcPtr)
{
_handler = funcPtr;
}
LRESULT CALLBACK CbtProcCallback(int code, WPARAM wparam, LPARAM lparam)
{
if (code >= 0)
{
// Only send the code if you are about to MAXIMIZE
if (code == HCBT_MINMAX)
{
if (lparam == SW_MAXIMIZE)
{
_handler(0, wparam, lparam);
}
}
}
return CallNextHookEx(NULL, code, wparam, lparam);
}
设置 CBT 挂钩的 DLL (dllwrapper.dll):
dllwrapper.cpp:片段
// Load library in which we'll be hooking our functions.
HMODULE dll = LoadLibrary(L"inject.dll");
if (dll == NULL) {
char errorMessage[100];
sprintf_s(errorMessage, "ERR-LoadLibrary failed! ErrorCode=%d", GetLastError());
SendManagedMessage(errorMessage);
return false;
}
SendManagedMessage("LoadLibrary passed!");
// Get the address of the function inside the DLL.
MYPROC iaddr = (MYPROC)GetProcAddress(dll, "Init");
if (iaddr == NULL) {
char errorMessage[100];
sprintf_s(errorMessage, "ERR-GetProcAddress for Init failed! ErrorCode=%d", GetLastError());
SendManagedMessage(errorMessage);
return false;
}
SendManagedMessage("GetProcAddress for Init passed!");
iaddr(OnInjectionCallback);
// Get the address of the function inside the DLL.
HOOKPROC cbtProcAddress = (HOOKPROC)GetProcAddress(dll, "CbtProcCallback");
if (cbtProcAddress == NULL) {
char errorMessage[100];
sprintf_s(errorMessage, "ERR-GetProcAddress for CbtProcCallback failed! ErrorCode=%d", GetLastError());
SendManagedMessage(errorMessage);
return false;
}
SendManagedMessage("GetProcAddress for CbtProcCallback passed!");
// Hook the function
cbtProcHook = SetWindowsHookEx(WH_CBT, cbtProcAddress, dll, _threadId);
if (cbtProcHook == NULL) {
char errorMessage[100];
sprintf_s(errorMessage, "ERR-SetWindowsHookEx cbtProcAddress failed! ErrorCode=%d", GetLastError());
SendManagedMessage(errorMessage);
return false;
}
SendManagedMessage("SetWindowsHookEx for cbtProcAddress passed!");
片段导出到 C#
typedef void(__stdcall* CodeCallback)(int code, WPARAM wparam, LPARAM lparam);
typedef void(__stdcall* MessageCallback)(const char* message);
#ifdef __cplusplus
extern "C" { // only need to export C interface if
// used by C++ source code
#endif
__declspec(dllexport) bool StartHooks(unsigned int threadId, MessageCallback messageCallback, CodeCallback codeCallback);
__declspec(dllexport) void StopHooks();
#ifdef __cplusplus
}
#endif
NativeMethods.cs: C# dll 片段导入 WPF 应用程序
public delegate void MessageCallback(string message);
public delegate void CodeCallback(int code, IntPtr wParam, IntPtr lParam);
[DllImport("dllwrapper.dll")]
public extern static bool StartHooks(uint threadId, MessageCallback messageHandler, CodeCallback codeHandler);
[DllImport("dllwrapper.dll")]
public extern static void StopHooks();
我可以从 WPF 应用 window 中的消息中看到挂钩正在传递并且没有返回任何 Win32 错误,但它只是在另一个 window 获得焦点时不执行回调(即使使用调试器时)。
如有任何帮助,我们将不胜感激!
开发环境:
- Windows 10 1909
- VS2019 16.7.4
- C# .NET Framework 4.7.2、C++
当主应用程序调用 Init()
时,它会调用自己的 DLL 实例,因此会设置自己的 _handler
副本。
当您的挂钩 DLL 被注入另一个进程时,该进程将获得自己的 DLL 副本,从而获得自己的 _handler
副本。但是 Init()
永远不会在该 DLL 实例上调用,因此当您的挂钩函数在该进程中被调用时,它的 _handler
副本仍然是 0。这就是进程崩溃的原因,因为挂钩没有检查该条件。
您在另一个进程中的 DLL 无法跨进程边界调用主应用程序中的函数。当 _handler
为 0 时,您将不得不更改钩子函数以使用您选择的 IPC 机制与主应用程序进程通信。Window 消息、管道、邮槽、套接字等可以用于该通信。
我已经为此绞尽脑汁好几天了,需要一些帮助!当 CBT 挂钩挂接到我的 WPF 应用程序的线程 ID 时,我可以使用 SetWindowsHookEx 检测到它,但我无法让它挂接到另一个进程的线程 ID window 当 window 成为前台应用程序时。
图 1:显示我可以获取用于检测 window 最大化主应用程序线程 ID 的 CBT 挂钩
图 2:显示我在侦听另一个应用程序的线程 ID 时无法获取 CBT 挂钩,这会使应用程序崩溃!
我想避免发送 ThreadId=0 并使其成为一个完整的全局钩子,因为我知道我只想听前台应用程序,而不是桌面上的所有应用程序。对于当前具有前景焦点的任何 window,我希望能够在它们发生之前收听一些 window 事件(WH_CBT 根据我的理解执行此操作)。
同样,以下代码在当前 WPF 应用程序成为前台应用程序时有效,但当另一个应用程序的 window 成为前台(例如记事本、Internet Explorer、文件资源管理器、Chrome等)。
完整代码:(link to github zip file)
这里有一些代码片段来展示我所做的:
定义回调的 DLL (inject.dll):
inject.h:片段typedef void(__stdcall* MYFUNCPTR)(int code, WPARAM wparam, LPARAM lparam);
extern "C" __declspec(dllexport) void Init(MYFUNCPTR funcPtr);
extern "C" __declspec(dllexport) LRESULT CALLBACK CbtProcCallback(int code, WPARAM wparam, LPARAM lparam);
WPARAM wparam, LPARAM lparam);
MYFUNCPTR _handler = 0;
inject.cpp:片段
void Init(MYFUNCPTR funcPtr)
{
_handler = funcPtr;
}
LRESULT CALLBACK CbtProcCallback(int code, WPARAM wparam, LPARAM lparam)
{
if (code >= 0)
{
// Only send the code if you are about to MAXIMIZE
if (code == HCBT_MINMAX)
{
if (lparam == SW_MAXIMIZE)
{
_handler(0, wparam, lparam);
}
}
}
return CallNextHookEx(NULL, code, wparam, lparam);
}
设置 CBT 挂钩的 DLL (dllwrapper.dll):
dllwrapper.cpp:片段 // Load library in which we'll be hooking our functions.
HMODULE dll = LoadLibrary(L"inject.dll");
if (dll == NULL) {
char errorMessage[100];
sprintf_s(errorMessage, "ERR-LoadLibrary failed! ErrorCode=%d", GetLastError());
SendManagedMessage(errorMessage);
return false;
}
SendManagedMessage("LoadLibrary passed!");
// Get the address of the function inside the DLL.
MYPROC iaddr = (MYPROC)GetProcAddress(dll, "Init");
if (iaddr == NULL) {
char errorMessage[100];
sprintf_s(errorMessage, "ERR-GetProcAddress for Init failed! ErrorCode=%d", GetLastError());
SendManagedMessage(errorMessage);
return false;
}
SendManagedMessage("GetProcAddress for Init passed!");
iaddr(OnInjectionCallback);
// Get the address of the function inside the DLL.
HOOKPROC cbtProcAddress = (HOOKPROC)GetProcAddress(dll, "CbtProcCallback");
if (cbtProcAddress == NULL) {
char errorMessage[100];
sprintf_s(errorMessage, "ERR-GetProcAddress for CbtProcCallback failed! ErrorCode=%d", GetLastError());
SendManagedMessage(errorMessage);
return false;
}
SendManagedMessage("GetProcAddress for CbtProcCallback passed!");
// Hook the function
cbtProcHook = SetWindowsHookEx(WH_CBT, cbtProcAddress, dll, _threadId);
if (cbtProcHook == NULL) {
char errorMessage[100];
sprintf_s(errorMessage, "ERR-SetWindowsHookEx cbtProcAddress failed! ErrorCode=%d", GetLastError());
SendManagedMessage(errorMessage);
return false;
}
SendManagedMessage("SetWindowsHookEx for cbtProcAddress passed!");
片段导出到 C#
typedef void(__stdcall* CodeCallback)(int code, WPARAM wparam, LPARAM lparam);
typedef void(__stdcall* MessageCallback)(const char* message);
#ifdef __cplusplus
extern "C" { // only need to export C interface if
// used by C++ source code
#endif
__declspec(dllexport) bool StartHooks(unsigned int threadId, MessageCallback messageCallback, CodeCallback codeCallback);
__declspec(dllexport) void StopHooks();
#ifdef __cplusplus
}
#endif
NativeMethods.cs: C# dll 片段导入 WPF 应用程序
public delegate void MessageCallback(string message);
public delegate void CodeCallback(int code, IntPtr wParam, IntPtr lParam);
[DllImport("dllwrapper.dll")]
public extern static bool StartHooks(uint threadId, MessageCallback messageHandler, CodeCallback codeHandler);
[DllImport("dllwrapper.dll")]
public extern static void StopHooks();
我可以从 WPF 应用 window 中的消息中看到挂钩正在传递并且没有返回任何 Win32 错误,但它只是在另一个 window 获得焦点时不执行回调(即使使用调试器时)。
如有任何帮助,我们将不胜感激!
开发环境:
- Windows 10 1909
- VS2019 16.7.4
- C# .NET Framework 4.7.2、C++
当主应用程序调用 Init()
时,它会调用自己的 DLL 实例,因此会设置自己的 _handler
副本。
当您的挂钩 DLL 被注入另一个进程时,该进程将获得自己的 DLL 副本,从而获得自己的 _handler
副本。但是 Init()
永远不会在该 DLL 实例上调用,因此当您的挂钩函数在该进程中被调用时,它的 _handler
副本仍然是 0。这就是进程崩溃的原因,因为挂钩没有检查该条件。
您在另一个进程中的 DLL 无法跨进程边界调用主应用程序中的函数。当 _handler
为 0 时,您将不得不更改钩子函数以使用您选择的 IPC 机制与主应用程序进程通信。Window 消息、管道、邮槽、套接字等可以用于该通信。