简体   繁体   English

本地键盘挂钩终止目标进程

[英]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:笔记:

  1. Both the DLL and the application are 32 bits DLL 和应用程序都是 32 位
  2. _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 地址)
  3. KeyboardProc::handler shouldn't be garbage collected, as the KeyboardProc instance lives for as long as the C# application does KeyboardProc::handler不应被垃圾回收,因为 KeyboardProc 实例的存在时间与 C# 应用程序一样长
  4. Using _hookCallback function pointer within the DLL's Install function works flawlessly, but terminates the process when used inside the HookProc function.在 DLL 的Install function 中使用_hookCallback function 指针可以完美地工作,但在HookProc ZC1C425268E68385D1AB5074C17A9 中使用时会终止进程。
  5. There's no exception or whatsoever, the process just terminates abruptly没有例外或任何情况,该过程只是突然终止

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 没有指定调用约定,因此它(可能,取决于编译器配置)默认为__cdeclHookProc()使用的是__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.

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