2014-10-28 60 views
1

我試圖讓一個應用程序獲得有關創建和銷燬系統級頂層窗口的消息。我做了一個代碼來利用CBT鉤子。該解決方案包含兩個項目,DLL和EXE。 EXE項目具有對DLL項目的引用。掛鉤正在從DLL中設置。 EXE項目中有一個消息循環。問題是CBT鉤子不工作。在VS調試器的幫助下,我發現掛鉤回調永遠不會被調用,而來自SetWindowsHookEx的返回碼不爲零,意味着鉤子被設置。什麼是錯誤?我如何解決它?全局CBT鉤子永遠不會被調用

下面是一個簡單的例子。 DLL,main.cpp中:

#include <windows.h> 

typedef void(*DECODERPROC)(int code, WPARAM wParam, LPARAM lParam); 

HINSTANCE hInst = nullptr; 
HHOOK hHook = nullptr; 
DECODERPROC fpDecoder = nullptr; 

LRESULT CALLBACK cbtProc(int code, WPARAM wParam, LPARAM lParam) { 
    // FIXME: never called 
    if (code > 0 && fpDecoder) { 
     fpDecoder(code, wParam, lParam); 
    } 
    return CallNextHookEx(hHook, code, wParam, lParam); 
} 

__declspec(dllexport) bool InstallHook(DECODERPROC decoder) { 
    if (hHook) return false; 
    fpDecoder = decoder; 
    return (hHook = SetWindowsHookEx(WH_CBT, cbtProc, hInst, 0)) != NULL; 
} 

__declspec(dllexport) bool UninstallHook() { 
    if (!hHook) return false; 
    bool res = UnhookWindowsHookEx(hHook) != NULL; 
    if (res) hHook = NULL; 
    return res; 
} 

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { 
    hInst = reinterpret_cast<HINSTANCE>(hModule); 
    return TRUE; 
} 

EXE,main.cpp中

#include <iostream> 
#include <locale> 

#include <windows.h> 
#include <fcntl.h> 
#include <io.h> 
using namespace std; 

typedef void(*DECODERPROC)(int code, WPARAM wParam, LPARAM lParam); 
__declspec(dllimport) bool InstallHook(DECODERPROC); 
__declspec(dllimport) bool UninstallHook(); 

int main() { 
    _setmode(_fileno(stdout), _O_U8TEXT); 
    WNDCLASS windowClass = {}; 
    windowClass.lpfnWndProc = [](HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -> LRESULT { 
     if (message == WM_DESTROY) 
      UninstallHook(); 
     return DefWindowProc(hWnd, message, wParam, lParam); 
    }; 
    LPCWSTR windowClassName = L"Foobar"; 
    windowClass.lpszClassName = windowClassName; 
    if (!RegisterClass(&windowClass)) { 
     wcerr << L"Failed to register window class" << endl; 
     return 1; 
    } 
    HWND messageWindow = CreateWindow(windowClassName, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, 0, 0); 
    if (!messageWindow) { 
     wcerr << L"Failed to create message-only window" << endl; 
     return 1; 
    } 

    InstallHook([](int code, WPARAM wParam, LPARAM lParam) { 
     wcout << L"Never called" << endl; 
    }); 

    MSG msg; 
    while (GetMessage(&msg, 0, 0, 0) > 0) { 
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
    } 
    return msg.wParam; 
} 

回答

3

如果您在Windows 64位運行,你需要在DLL的32位和64位版本爲了鉤住每個正在運行的進程。 32位DLL無法掛接64位進程,反之亦然。因此,您需要從32位進程調用SetWindowsHookEx()來掛接32位進程,並從64位進程掛接到64位進程。

更重要的是,DLL的單獨實例被注入到每個正在運行的進程中,所以您的fpDecoder回調指針在每個DLL實例中都爲NULL,除了EXE調用InstallHook()的那個。因此,您需要重新設計您的鉤子以使用進程間通信(窗口消息,命名管道,郵筒,套接字等)與主EXE進行通信,但不能使用函數指針。

根據您實際調試的過程,您可能看不到cbtProc()被調用。如果您正在調試您的主EXE進程,則在安裝鉤子後,您的代碼將不會在您的EXE進程中觸發任何CBT活動,並且調試器不會向您顯示在其它進程中沒有進行調試的任何CBT活動。

根據您實際在鉤子中查找的內容,您可能會考慮使用SetWinEventHook(),因爲它可以使用帶或不帶DLL,並且具有比SetWindowsHookEx()更靈活的過濾功能。

+0

我正在運行32位W7。該應用程序編譯爲32位。雖然關於指針的東西對我來說是新東西。 – 2014-10-28 18:57:44

+0

我使用'SetWinEventHook'也爲「前景窗口已更改」消息。那裏沒有「創建窗口」消息。 – 2014-10-28 19:00:12

+0

另外,你沒有回答主要問題:爲什麼'cbtProc'永遠不會被調用? – 2014-10-28 19:01:56

相關問題