[英]Global hook DLL only calls C# callback method when the C# main window is active/foreground
我有一个C#应用程序,可以打开C ++全局键挂钩。 dll会全局监听按键,并在按下某个键时调用主应用程序回调方法。
dll挂钩过程有效,除了在主应用程序不是活动屏幕(在此状态下没有意义)时不调用回调函数之外。 有没有解决的办法? 我可能会缺少一些东西。
C#代码:
//Global Key DLL callback delegate
public delegate void KeyCallBackFunction(int keyPoints);
[DllImport("Dll1_64.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern void SetCallBack(IntPtr pCallBack);
IntPtr hInstance = IntPtr.Zero;
private delegate void HookSetting();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
...
KeyCallBackFunction keyPointDelegate = new KeyCallBackFunction(DetectKey);
// Use GCHandle to hold the delegate object in memory.
GCHandle gch = GCHandle.Alloc(keyPointDelegate);
// Obtain an unmanaged function pointer for the delegate as usual.
IntPtr intptr_delegate = Marshal.GetFunctionPointerForDelegate(keyPointDelegate);
// Callback is setup as a thread
Thread keyThread= new Thread(() => SetCallBack(intptr_delegate));
keyThread.Start();
// Global hook Setup
hInstance = LoadLibrary("Dll1_64");
hProc = GetProcAddress(hInstance, "SetHook");
HookSetting hookset = (HookSetting)Marshal.GetDelegateForFunctionPointer(hProc, typeof(HookSetting));
hookset();
}
public static void DetectKey(int point)
{
System.Windows.Application.Current.Dispatcher.Invoke(new Action(() =>
{
if (keyPoints == 0)
{
// do something for key point zero
}
else
{
// do something for other key points
}
}));
}
C ++全局密钥挂钩dll:
#include "pch.h"
#include <stdio.h>
#include <ctime>
//these variables will be shared among all processes to which this dll is linked
#pragma data_seg("Shared")
//our hook handle which will be returned by calling SetWindowsHookEx function
HHOOK hkKey = NULL;
HINSTANCE hInstHookDll = NULL; //our global variable to store the instance of our DLL
#pragma data_seg() //end of our data segment
#pragma comment(linker,"/section:Shared,rws")
// Tell the compiler that Shared section can be read,write and shared
HWND pHWnd = NULL;
typedef void(__stdcall* PCallBack)(int points);
PCallBack g_pCallBack = NULL;
// used to register callback
extern "C" __declspec(dllexport) void __stdcall SetCallBack(PCallBack pCallBack)
{
g_pCallBack = pCallBack;
}
// call callbacak function
extern "C" __declspec(dllexport) void __stdcall PerformAction(int points)
{
if (g_pCallBack)
{
g_pCallBack(points);
}
}
//this is the hook procedure
__declspec(dllexport) LRESULT CALLBACK procCharMsg(int nCode, WPARAM wParam, LPARAM lParam)
{
//a pointer to hold the MSG structure that is passed as lParam
MSG* msg;
//to hold the character passed in the MSG structure's wParam
char charCode;
if (nCode >= 0 && nCode == HC_ACTION)
//if nCode is less than 0 or nCode
//is not HC_ACTION we will call CallNextHookEx
{
//lParam contains pointer to MSG structure.
msg = (MSG*)lParam;
if (msg->message == WM_CHAR)
{
charCode = msg->wParam;
if (IsCharLower(charCode))
//we check if the character pressed is a small letter
{
//if so, make it to capital letter
charCode -= 32;
msg->wParam = (WPARAM)charCode;
//overwrite the msg structure's wparam
//with our new value.
}
if (charCode == 'A') {
PerformAction(0); // Point Zero
}
else if (charCode == 'B') {
PerformAction(1); // Point One
}
else {
PerformAction(2); // Point Two
}
}
}
return CallNextHookEx(hkKey, nCode, wParam, lParam);
}
extern "C" __declspec(dllexport) void __stdcall SetHook()
{
if (hkKey == NULL)
hkKey = SetWindowsHookEx(WH_GETMESSAGE, procCharMsg, hInstHookDll, 0);
}
//remove the hook
extern "C" __declspec(dllexport) void __stdcall RemoveHook()
{
if (hkKey != NULL)
UnhookWindowsHookEx(hkKey);
hkKey = NULL;
}
INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved) {
switch (Reason)
{
case DLL_PROCESS_ATTACH:
//we initialize our variable with the value that is passed to us
hInstHookDll = (HINSTANCE)hDLL;
break;
default:
break;
}
return TRUE;
}
编辑:我更改了对主窗口的共享HWND指针的方法。 然后只需向该HWND发送/发布消息,即可通过WndProc接收信息
全局钩子将加载到每个正在运行的进程中,这意味着每个进程将在其中加载自己的DLL副本。
挂钩DLL通过将HHOOK
句柄存储在共享的数据段中供所有进程共享来解决这一问题。 但是g_pCallBack
变量没有存储在该段中,因此没有被共享。 仅实际调用SetCallback()
的原始进程将分配其g_pCallBack
,因此只有该进程中的键盘活动将报告给回调。
您也不g_pCallBack
存储在共享段中,因为不能跨进程边界调用原始函数指针。 您将需要使用像COM或RPC这样的进程间通信框架来执行此操作。
一个简单的解决方案是用共享段中存储的HWND
替换g_pCallBack
,然后加载到每个进程中的钩子可以在需要时将窗口消息发布到HWND
。 C#应用程序可以分配一个窗口来接收这些消息,并将该窗口提供给它的DLL副本以存储在共享段中。 或者,如果要继续使用delegate
则让SetHook()
创建的是自己的HWND
,然后让窗口处理程序(如果分配)调用委托。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.