Сбои во время UnhookWindowsHookEx () - PullRequest
1 голос
/ 14 марта 2011

Я сделал крючок для калькулятора и хочу получать сообщения, которые получает калькулятор.Для этого я установил собственную оконную процедуру, но во время отсоединения, если я использую SetWindowLong(..) для восстановления старой программы оконной процедуры, происходит сбой.

Код DLL:

#define EXPORT_API extern "C" __declspec(dllexport)

EXPORT_API void InstallHook();
EXPORT_API void UninstallHook();

#pragma data_seg("Shared")
HHOOK   g_hHook  = NULL;
WNDPROC g_OldWndProc = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:Shared,rws")

HWND GetTargetWindowHwnd()
{
return ::FindWindowA(0, "Calculator");
}

// my new wnd procedure to catch messages
LRESULT CALLBACK NewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LRESULT lResult = 0;
    switch(uMsg)
    {
    case WM_CLOSE:
        {
            MessageBoxA(0, "Here we are!", "", 0);
        }
        break;  
    default:
        lResult = CallWindowProc(g_OldWndProc, hwnd, uMsg, wParam, lParam);
        break;
    }
    lResult = CallWindowProc(g_OldWndProc, hwnd, uMsg, wParam, lParam);
    return lResult;
}
// hook procedure
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    MSG *pMsg = (MSG *)lParam;
    HWND hWnd = GetTargetWindowHwnd();  
    bool flagIn = false;    

    if( hWnd == pMsg->hwnd )
    {// if messege was sent to my target window
        if(g_OldWndProc == NULL)
        {
            // save the adress of old wnd procedure to recover it later
            g_OldWndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC);
            // set my wnd procedure
            SetWindowLong(hWnd, GWL_WNDPROC, (LONG)NewWndProc);
        }
    }
    return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}

EXPORT_API void InstallHook()
{
    try
    {       
        g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hInstance, 0); 
    } 
    catch(...) 
    {
        MessageBoxA(0, "Hook error", "Error", 0);
    }
}

EXPORT_API void UninstallHook()
{
    if(g_OldWndProc)
    {
        // recovering old wnd proc
        HWND hWnd = GetTargetWindowHwnd();
        SetWindowLong(hWnd, GWL_WNDPROC, (LONG)g_OldWndProc);
        g_OldWndProc = NULL;
    }
    if (g_hHook)
    {
        UnhookWindowsHookEx(g_hHook);
        g_hHook = NULL;
    }   
}

BOOL APIENTRY DllMain( HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
    )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        g_hInstance  = (HINSTANCE) hModule;
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

EXE-код:

void CHookTestDlg::OnBnClickedBtnInstall()
{   
    InstallHook();  
}

void CHookTestDlg::OnBnClickedBtnUninstall()
{
    UninstallHook();
}

Если я не использую процедуру wnd, она работает нормально.Если я использую SetWindowLong (..), чтобы восстановить старую оконную процедуру, программа ломается во время unhook.Что не так?

Ответы [ 2 ]

2 голосов
/ 15 марта 2011

Проблема заключается в том, что вы настраиваете процедуру окна в целевом окне из целевого процесса (calc), и в этом случае это происходит успешно. Но когда вы вызываете UninstallHook, этот код запускается в процессе вашего собственного исполняемого файла; и в этом случае SetWindowLong завершится ошибкой.

(Помещение значений ловушек в разделяемую память не поможет; SetWindowLong все равно откажется изменять процедуру окна через границу процесса - подробности см. В MSDN.)

Чтобы это сработало, вам нужно связаться с подключенным экземпляром DLL и попросить его сбросить wndproc из этого целевого процесса, и как только это будет сделано, отцепите крючок.

(совет atzz по отсоединению также действителен. Лучше всего избегать зацепления окон, которыми вы не владеете.)

2 голосов
/ 14 марта 2011

При отмене подклассов всегда проверяйте, чтобы окно не было подклассами кем-то другим после вас. То есть перед восстановлением WindowProc вы должны снова прочитать его и сравнить с ожидаемым значением (NewWndProc). Если это не так, вы не должны выгружать DLL, потому что в другом подклассе хранится указатель на ваш код DLL, и этот указатель будет зависать, как только ваша DLL выгружена.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...