本地键盘挂钩终止目标进程 [英] Local keyboard hook terminates the target process

查看:85
本文介绍了本地键盘挂钩终止目标进程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正尝试使用托管C#代码中的C ++ DLL在进程中安装 LOCAL 键盘钩,如下所示:

I'm trying to install a LOCAL keyboard hook into a process using a C++ DLL from managed C# code, as follows:

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);
    }
}

C ++ DLL代码应该足以将钩子数据分派到C#的 Callback 函数,如下所示:

The C++ DLL code should be just enough to dispatch the hook data to the C#'s Callback function, like this:

// 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;
}

当DLL KeyboardProc函数被触发时,本地挂钩已成功安装,但是,从C ++ DLL调用C#委托将终止应用程序.为什么?

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?

注释:

  1. DLL和应用程序均为32位
  2. 触发 HookProc 时,
  3. _hookCallback _ 不为空(尽管我不确定它是否指向有效的内存地址)
  4. KeyboardProc :: handler 不应被垃圾回收,因为KeyboardProc实例的生存时间与C#应用程序的寿命一样
  5. 在DLL的 Install 函数中使用 _hookCallback 函数指针可以正常工作,但是在 HookProc 函数中使用时终止该过程.
  6. 没有任何例外,该过程只是突然终止
  1. Both the DLL and the application are 32 bits
  2. _hookCallback_ is not null when HookProc is triggered (although I'm not sure whether it points to a valid memory address)
  3. KeyboardProc::handler shouldn't be garbage collected, as the KeyboardProc instance lives for as long as the C# application does
  4. Using _hookCallback function pointer within the DLL's Install function works flawlessly, but terminates the process when used inside the HookProc function.
  5. There's no exception or whatsoever, the process just terminates abruptly

已经尝试过的内容:

HookCallback 一个 UnmanagedFunctionPointer ,以及使用 Marshal.GetFunctionPointerForDelegate 并告诉垃圾回收器不要收集 handler 属性,方法是使用 GCHandle.Alloc() GC.KeepAlive():

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():

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);
    }
    
    // ...
}

直接使用 handler SetWindowsHookExA (C ++)中:

Using handler directly into SetWindowsHookExA (C++):

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 process terminates due to accessing an invalid memory address.

每个Windows进程都有不同的虚拟内存区域.换句话说,进程 A 中的 0x1234 内存地址不指向与进程 0x1234 中的 0x1234 内存地址相同的值/函数.code> B ,因为 0x1234 是绑定到其相应进程的虚拟内存地址.

Every Windows process have different virtual memory regions. 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.

为了实现C ++ DLL和C#应用程序之间的通信(总体上任何不同的过程),请

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.

对于那些对此特定案例感兴趣的人,我最终创建了一个不可见的虚拟窗口用作通过DLL的 SendMessage 调用接收消息的中心点.

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.

这篇关于本地键盘挂钩终止目标进程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆