Windows全局钩子实现方案回顾

来源:互联网 发布:中医宝典大全软件 编辑:程序博客网 时间:2024/06/03 19:51

首先声明此文没有任何新的地方,俗话说阳光之下无原创。写下这个短文仅仅是对前几天做的一个小事情的总结,也许有可能对一些初学者起到一定的帮助作用,则善莫大焉。

先看任务要求:需要实现全局的鼠标钩子,但是是在后台运行的,希望安装程序之后在系统启动时自动启动,并且监视一个具体的设备,当该设备插入时,启动全局钩子;当该设备从系统删除时,撤销全局钩子。非常简单的一句话概括了任务。

再看解决方案:当拿到这个任务的时候,我们大概的解决思路和关键技术在于几个方面:1. 钩子技术的实现,包括钩子的创建和撤销,因为是鼠标钩子所以牵涉到动态连接库的实现和调用;2.后台运行的实现,直接想法应该是应用windows服务程序,这样的程序可以在系统启动是自动启动并且运行于后台。

钩子DLL没有什么难题,不进行详细解释。关于windows服务程序的实现,需要监视设备的插入/删除,另外这样的程序的调试也稍稍有点不同,就是在debug的菜单里面,关联process来进行调试。实现设备的监视关键代码如下:

ServiceMain(DWORD dwArgc, PWSTR *pszArgv){Sleep(3000);    // Register the handler function for the service    RegisterServiceCtrlHandlerEx(        s_service->m_name,(LPHANDLER_FUNCTION_EX)ServiceCtrlHandler, 0);    if (s_service->m_statusHandle == NULL)    {        throw GetLastError();    }    // Start the service.    s_service->Start(dwArgc, pszArgv);// Register the device change notification DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;memcpy( &(NotificationFilter.dbcc_classguid),&(GUID_DEVINTERFACE_USB),sizeof(struct _GUID));HDEVNOTIFY  hDevNotify = RegisterDeviceNotification(s_service->m_statusHandle, &NotificationFilter,DEVICE_NOTIFY_SERVICE_HANDLE|DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);}

在相应的serviceHandler里面有如下的代码:

void WINAPI CServiceBase::ServiceCtrlHandler(DWORD dwCtrl,DWORD evtype, PVOID evdata, PVOID Context){    switch (dwCtrl)    {    case SERVICE_CONTROL_STOP: s_service->Stop(); break;    case SERVICE_CONTROL_PAUSE: s_service->Pause(); break;    case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break;    case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break;    case SERVICE_CONTROL_DEVICEEVENT: s_service->DeviceEventNotify(evtype, evdata); break;    case SERVICE_CONTROL_INTERROGATE: break;    default: break;    }}

DeviceEventNotify函数如下:

unsigned long  DeviceEventNotify(DWORD evtype, PVOID evdata){HINSTANCE hmodule = LoadLibrary(_T("MouseHookDLL.dll"));if (hmodule == NULL){WriteToLog("DLL Load Failed.");FreeLibrary(hmodule);return 0;}typedef HHOOK (*pInstallHook)(HINSTANCE hmodule);typedef BOOL (*pUnInstallHook)(HHOOK hook);pInstallHook InstallHookFunction = (pInstallHook) GetProcAddress(hmodule, "InstallHook");pUnInstallHook UnInstallHookFunction = (pUnInstallHook) GetProcAddress(hmodule, "UnInstallHook");switch (evtype){case DBT_DEVICEREMOVECOMPLETE:{// Stop the HookWriteToLog("Remove USB Device.");UnInstallHookFunction(hook);}break;case DBT_DEVICEARRIVAL:{// Create a Hook to reset the cursorWriteToLog("Insert USB Device.");hook = InstallHookFunction(hmodule);}break;}FreeLibrary(hmodule);return 0;}

道理上上面的方案应该是可行的,但是程序调试发现,虽然DLL可以正确的load但是,确调用不到DLL的callback函数,查阅资料之后发现,windows不允许服务程序实现全局钩子。说明此种方案行不通。那么我们考虑第二种方案,就是利用一个windows后台程序实现设备的插入/删除监视功能,并且在里面调用相应的DLL。这个也不是难题,但是怎么实现后台运行是我们这个时候需要考虑的问题,解决方法也很简单,就是把这个windows的窗口的show里面的参数改为SW_HIDE.

{    DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;    ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );    NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);    NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;    NotificationFilter.dbcc_classguid = InterfaceClassGuid;    *hDeviceNotify = RegisterDeviceNotification(         hWnd,                       // events recipient        &NotificationFilter,        // type of device        DEVICE_NOTIFY_ALL_INTERFACE_CLASSES|DEVICE_NOTIFY_WINDOW_HANDLE // type of recipient handle        );    if ( NULL == *hDeviceNotify )     {ErrorHandler(TEXT("RegisterDeviceNotification"));        return FALSE;    }    return TRUE;}

// Main app window    HWND hWnd = CreateWindowEx(                    WS_EX_CLIENTEDGE | WS_EX_APPWINDOW,                    WND_CLASS_NAME,                    g_pszAppName,                    WS_OVERLAPPEDWINDOW, // style                    CW_USEDEFAULT, 0,                     640, 480,                    NULL, NULL,                     hInstanceExe,                     NULL);        if ( hWnd == NULL )    {ErrorHandler(TEXT("CreateWindowEx: main appwindow hWnd"));        return -1;    }    // Actually draw the window.    ShowWindow(hWnd, SW_HIDE);    UpdateWindow(hWnd);

总结:


总体方法就是在windows service程序不允许全局钩子的情况下,创建隐藏窗口的windows应用程序实现全局钩子。


原创粉丝点击