首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >系统宽的Windows CBT钩子不能正常工作

系统宽的Windows CBT钩子不能正常工作
EN

Stack Overflow用户
提问于 2010-06-26 00:20:49
回答 1查看 4.4K关注 0票数 2

我正试图将CBT钩子挂在Windows OSes上。我目前使用的是Windows7 x64。

我读过很多关于这个问题的帖子,但没有一个能解决我的问题。应用程序运行良好;钩子已经安装,我可以看到一些通知即将到来。

实际上,出现的问题是,应用程序没有收到关于运行在同一台计算机上的其他进程的CBT挂钩的通知。

应用程序是用C#编写的(使用Microsoft .NET)。下面是一个正在运行的示例:

代码语言:javascript
复制
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace WindowsHook
{
    class Program
    {
    [STAThread]
    static void Main(string[] args)
    {
        uint thid = (uint)AppDomain.GetCurrentThreadId();
        bool global = true;

        mHookDelegate = Marshal.GetFunctionPointerForDelegate(new HookProc(ManagedCallback));

        if (global == true) {
            mNativeWrapperInstance = LoadLibrary("Native_x64.dll");
            thid = 0;
        } else {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                mNativeWrapperInstance = GetModuleHandle(curModule.ModuleName);
            }
        }

        mNativeWrappedDelegate = AllocHookWrapper(mHookDelegate);
        mHookHandle = SetWindowsHookEx(/*WH_CBT*/5, mNativeWrappedDelegate, mNativeWrapperInstance, thid);

        if (mHookHandle == IntPtr.Zero)
            throw new Win32Exception(Marshal.GetLastWin32Error());

        Application.Run(new Form());

        if (FreeHookWrapper(mNativeWrappedDelegate) == false)
            throw new Win32Exception("FreeHookWrapper has failed");
        if (FreeLibrary(mNativeWrapperInstance) == false)
            throw new Win32Exception("FreeLibrary has failed");

        if (UnhookWindowsHookEx(mHookHandle) == false)
            throw new Win32Exception(Marshal.GetLastWin32Error());
    }

    static int ManagedCallback(int code, IntPtr wParam, IntPtr lParam)
    {
        Trace.TraceInformation("Code: {0}", code);
        if (code >= 0) {
            return (0);
        } else {
            return (CallNextHookEx(mHookHandle, code, wParam, lParam));
        }
    }

    delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);

    static IntPtr mHookHandle;

    static IntPtr mHookDelegate;

    static IntPtr mNativeWrapperInstance = IntPtr.Zero;

    static IntPtr mNativeWrappedDelegate = IntPtr.Zero;

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern IntPtr GetModuleHandle(string lpModuleName);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int hook, IntPtr callback, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", SetLastError = true)]
    internal static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll")]
    internal static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll")]
    private static extern IntPtr LoadLibrary(string lpFileName);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool FreeLibrary(IntPtr hModule);

    [DllImport("Native_x64.dll")]
    private static extern IntPtr AllocHookWrapper(IntPtr callback);

    [DllImport("Native_x64.dll")]
    private static extern bool FreeHookWrapper(IntPtr wrapper);

    [DllImport("Native_x64.dll")]
    private static extern int FreeHooksCount();
}

}

AllocHookWrapper和FreeHookWrapper是从位于应用程序相同目录的x64平台的DLL (Native_x64.dll)编译器导入的例程。AllocHookWrapper存储函数指针(托管例程)并返回调用函数指针的DLL例程。

以下是DLL的代码:

代码语言:javascript
复制
#include "stdafx.h"
#include "iGecko.Native.h"

#ifdef _MANAGED
#pragma managed(push, off)
#endif

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#define WRAPPER_NAME(idx)   Wrapper ## idx

#define WRAPPER_IMPLEMENTATION(idx)                                                 \
LRESULT WINAPI WRAPPER_NAME(idx)(int code, WPARAM wparam, LPARAM lparam)        \
{                                                                               \
    if (sHooksWrapped[idx] != NULL)                                             \
        return (sHooksWrapped[idx])(code, wparam, lparam);                      \
    else                                                                        \
        return (0);                                                             \
}

#define WRAPPER_COUNT       16

HOOKPROC sHooksWrapped[WRAPPER_COUNT] = { NULL };

WRAPPER_IMPLEMENTATION(0x00);
WRAPPER_IMPLEMENTATION(0x01);
WRAPPER_IMPLEMENTATION(0x02);
WRAPPER_IMPLEMENTATION(0x03);
WRAPPER_IMPLEMENTATION(0x04);
WRAPPER_IMPLEMENTATION(0x05);
WRAPPER_IMPLEMENTATION(0x06);
WRAPPER_IMPLEMENTATION(0x07);
WRAPPER_IMPLEMENTATION(0x08);
WRAPPER_IMPLEMENTATION(0x09);
WRAPPER_IMPLEMENTATION(0x0A);
WRAPPER_IMPLEMENTATION(0x0B);
WRAPPER_IMPLEMENTATION(0x0C);
WRAPPER_IMPLEMENTATION(0x0D);
WRAPPER_IMPLEMENTATION(0x0E);
WRAPPER_IMPLEMENTATION(0x0F);

const HOOKPROC sHookWrappers[] = {
    WRAPPER_NAME(0x00),
    WRAPPER_NAME(0x01),
    WRAPPER_NAME(0x02),
    WRAPPER_NAME(0x03),
    WRAPPER_NAME(0x04),
    WRAPPER_NAME(0x05),
    WRAPPER_NAME(0x06),
    WRAPPER_NAME(0x07),
    WRAPPER_NAME(0x08),
    WRAPPER_NAME(0x09),
    WRAPPER_NAME(0x0A),
    WRAPPER_NAME(0x0B),
    WRAPPER_NAME(0x0C),
    WRAPPER_NAME(0x0D),
    WRAPPER_NAME(0x0E),
    WRAPPER_NAME(0x0F)
};

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    return (TRUE);
}

#ifdef _MANAGED
#pragma managed(pop)
#endif

extern "C" IGECKONATIVE_API HOOKPROC WINAPI AllocHookWrapper(HOOKPROC wrapped)
{
    for(int i = 0; i < WRAPPER_COUNT; i++) {
        if (sHooksWrapped[i] == NULL) {
            sHooksWrapped[i] = wrapped;
            return sHookWrappers[i];
        }
    }

    return (NULL);
}

extern "C" IGECKONATIVE_API BOOL WINAPI FreeHookWrapper(HOOKPROC wrapper)
{
    for(int i = 0; i < WRAPPER_COUNT; i++) {
        if (sHookWrappers[i] == wrapper) {
            sHooksWrapped[i] = NULL;
            return TRUE;
        }
    }

    return (FALSE);
}

extern "C" IGECKONATIVE_API INT WINAPI FreeHooksCount()
{
    int c = 0;

    for(int i = 0; i < WRAPPER_COUNT; i++) {
        if (sHooksWrapped[i] == NULL)
            c++;
    }

    return (c);
}

实际上,我对某个系统上与窗口相关的事件(创建、销毁)很感兴趣,但我实际上无法得到操作系统的通知……

到底怎么回事?我错过了什么?

请注意,我正在与Administratos组一起运行。

我在这个页面中发现了这个有趣的部分

全局钩子在.NET框架中不受支持,您不能在.NET框架中实现全局钩子。要安装全局钩子,钩子必须有一个本机DLL导出才能将自身插入到另一个需要调用有效、一致函数的进程中。此行为需要DLL导出。.NET框架不支持DLL导出。托管代码没有函数指针一致值的概念,因为这些函数指针是动态构建的代理。

我想通过实现一个包含钩子回调的本机DLL来完成这个任务,该回调调用托管回调。但是,托管回调只在调用SetWindowsHookEx例程的进程中调用,而不是由其他进程调用。

可能的解决办法是什么?

可能会分配堆内存,存储进程id (托管进程),并发送描述挂起的函数的用户消息?

我试图实现的是一个系统范围的监视器,它检测执行的新进程,检测创建的窗口位置和大小,以及关闭的窗口、移动的窗口、最小化/最大化的窗口。监视器将依次检测鼠标和键盘事件(始终是系统范围内的),还必须“模拟”鼠标和键盘事件。

同一桌面中的每个进程都必须由存档(32位或64位)和底层框架(本机或管理)独立监视。

监视器应强制处理窗口的位置、大小和移动,并能够充当本地用户,以便远程用户充当本地用户(类似于VNC)。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2010-06-26 14:48:28

对不起,我不明白“包装”非托管动态链接库的意义,以及使用ManagedCallback作为托管EXE内部的钩子。

您应该理解,您使用的回调系统范围CBT钩子(SetWindowsHookEx参数)的方法必须加载在all process的地址空间中(它将对实现钩子函数的模块进行DLL注入)。在Windows (MSDN)中,您可以阅读以下内容(参见关于http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx的注释)

SetWindowsHookEx可用于将DLL注入另一个进程。32位DLL不能注入64位进程,64位DLL不能注入32位进程.如果应用程序需要在其他进程中使用钩子,则需要32位应用程序调用SetWindowsHookEx将32位DLL注入32位进程,而64位应用程序调用SetWindowsHookEx将64位DLL注入64位进程。32位和64位DLL必须有不同的名称.

此外,您还编写了关于系统宽钩子的问题,而不是使用0作为SetWindowsHookEx的最后一个参数。还有一个问题:作为SetWindowsHookEx (HINSTANCE hMod)的第三个参数,您使用的是实例,而不是带有钩子代码(当前EXE中的钩子代码)的dll。

因此,我的建议是:您必须编写一个新的本机代码来实现系统范围的CBT挂钩,并将其放入DLL中。我还建议您为DLL选择一个基址(链接器开关),这不是减少DLL重基的标准值。这不是强制性的,但这将节省内存资源。

遗憾的是,坏消息,但在我看来,您目前的代码应该完全重写。

基于问题中的update更新了:我再说一遍,如果您调用一个进程SetWindowsHookEx来设置一个SetWindowsHookEx钩子,您应该作为参数给出一个DLL的模块实例(起始地址)和实现这个钩子的一个函数的地址。从哪个进程调用SetWindowsHookEx函数并不重要。用作参数的DLL将在使用User32.dll的同一个窗口站的所有进程中加载(注入)。所以你有一些本土的限制。如果您想同时支持32位和64位平台,您必须实现两个 DLL :一个32位DLL和64位DLL。此外,在同一过程中使用不同的.NET版本也存在问题。理论上只有在.NET 4.0中才有可能做到这一点。一般来说,这是一个非常复杂的问题。你应该理解我写的关于DLL的我指的不仅仅是,还有它的所有依赖项。因此,如果您实现了调用托管DLL (.NET DLL)的本机DLL,则不可能实现。

因此,如果要使用全局CBT钩子,必须实现if作为两个本地DLL(一个32位和64位),并设置在两个进程(一个32位和64位)内安装钩子。因此,请严格执行SetWindowsHookEx文档http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx的备注中所描述的内容(请参阅上面的引号)。我看不出更容易的方法了。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3122215

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档