繁体   English   中英

全局挂钩DLL仅在C#主窗口处于活动状态/前景时才调用C#回调方法

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM