Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Global Mouse Hook


Автор: FiMa1 16.6.2008, 18:55
Добрейшего времени суток, друзья!

Я столкнулся с довольно распространенной, судя по результатам поиска, проблемой, но все же не смог решить ее.
Мне нужно глобально отлавливать активность мыши (click, double click). Я воспользовался этим примером - http://cppbuilder.ru/articles/0007.php, но смог добиться только локального перехвата кликов мыши, похоже созданная мною dll проецируется все же на пространство вызывающего процесса, т.е., по просту, моего приложения.

Кто-нибудь может подсказать как мне сделать хук глобальным? Может быть нужно еще что-то предпринять для компиляции с разделяемым сегментом данных (Shared)? Заранее спасибо за помощь.

Для удобства приведу код здесь:
 --- код mousehook.dll ---
Код

#include <windows.h>

#pragma option -zRSHARESEG // sets segment name for this source file to SHARESEG
#pragma option -zTSHARECLS // sets class name for segment to SHARECLS

// экспортируемые функции
extern "C" __declspec(dllexport) bool InstallMouseHook();
extern "C" __declspec(dllexport) bool RemoveMouseHook();

// объявление функции обратного вызова
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam);

// глобальные переменные
HHOOK HookHandle;
HINSTANCE DllInstance;
bool IsInRect = false;

//----------------------------------------------------------------------
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) {
    DllInstance = hinst;
    return 1;
}
//----------------------------------------------------------------------
bool InstallMouseHook() {
    HookHandle = SetWindowsHookEx(WH_MOUSE,
        reinterpret_cast<HOOKPROC>(MouseProc), DllInstance, 0);
    if(HookHandle == NULL)
        return false;
    else
        return true;
}
//----------------------------------------------------------------------
bool RemoveMouseHook() {
    if(UnhookWindowsHookEx(HookHandle) == 0)
        return false;
    else
        return true;
}
//----------------------------------------------------------------------
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) {
    if(code < 0)
        return CallNextHookEx(HookHandle, code, wParam, lParam);

    HWND myapp = FindWindow("TForm1", "Form1");
    switch( LOWORD( wParam ) ) {
        case WM_LBUTTONDOWN :
            SendMessage(myapp, LBUTTONDOWN, 0, 0);
            break;
            ...
    }

    // вызываем следующий хук в цепи
    return CallNextHookEx(HookHandle, code, wParam, lParam);
}

--- код исполняемого приложения. mousehook.h ---
Код

typedef bool (__stdcall *MOUSEHOOKFCT)();
HINSTANCE DllHandle;

--- код исполняемого приложения. mousehook.cpp ---
Код

#include <vcl.h>
#include "mousehook.h"
//----------------------------------------------------------------------
TForm1 *Form1;
//----------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner) { }
//----------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender) {
    DllHandle = LoadLibrary("mousehook.dll");
    MOUSEHOOKFCT InstHook = reinterpret_cast<MOUSEHOOKFCT>
        (GetProcAddress(DllHandle, "_InstallMouseHook"));
    if(!InstHook())
        Label1->Caption = "Unable to set mouse hook!";
}
//----------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender) {
    MOUSEHOOKFCT RemHook = reinterpret_cast<MOUSEHOOKFCT>
        (GetProcAddress(DllHandle, "_RemoveMouseHook"));
    if( ! RemHook() )
        Label1->Caption = "Unable to remove mouse hook";

    FreeLibrary(DllHandle);
}
//----------------------------------------------------------------------
void __fastcall TForm1::ApplicationEvents1Message(tagMSG &Msg,
        bool &Handled) {
    switch( Msg.message ) {
        case WM_LBUTTONDOWN :
            Form1->Caption = "LEFT MOUSE BUTTON PRESSED!";
            break;
            ...
    }
}

Автор: jonie 16.6.2008, 19:16
1) SendMessage вашему окну плозая идея. используйте PostMessage.
2) if (DLL_PROCESS_ATTACH ==reason ) DllInstance = hinst; добавьте в DllMain
3) на это я думаю тоже можно забить :
Код

    if(code < 0)
        return CallNextHookEx(HookHandle, code, wParam, lParam);

4) незабывайте проверять на ошибки.

Автор: FiMa1 16.6.2008, 21:22
Цитата

1) SendMessage вашему окну плохая идея. используйте PostMessage.

jonie, спасибо за справедливое замечание. Я так и планировал в конечном счете заменить SendMessage на PostMessage. Для последователей цитата из http://www.rsdn.ru/article/baseserv/hookdll.xml:
Цитата

Существует множество решений для организации взаимодействия вашей DLL и ее управляющего сервера. Одно из решений заключается в использовании ::PostMessage или ::SendMessage (заметим, что здесь я ссылаюсь на вызовы чистого API, а не вызовы MFC!). Там, где возможно использовать вызов ::PostMessage, лучше используйте его, а не ::SendMessage, т.к. иначе вы можете получить опасные тупиковые ситуации. Если Ваш Процесс в итоге останавливается, все остальные процессы в системе остановятся, т.к. все заблокированы на вызове ::SendMessage, который никогда не возвратится, и вы просто вывели всю систему из строя с возможностью серьезной потери данных в важных для пользователя приложениях. Это Совершенно Однозначно Не Хорошая Ситуация.

Цитата

3) на это я думаю тоже можно забить:
Код

    if(code < 0)
        return CallNextHookEx(HookHandle, code, wParam, lParam);


Ммм... даже не знаю, позаимствовал отсюда - http://www.rsdn.ru/article/baseserv/hookdll.xml:
Цитата

Код

static LRESULT CALLBACK msghook(int nCode, WPARAM wParam, LPARAM lParam)
   {
    // If the value of nCode is < 0, just pass it on and return 0
    // this is required by the specification of hook handlers
    // Если значение nCode < 0, просто передаем его дальше и возвращаем 0
    // этого требует спецификация обработчиков хуков
    if(nCode < 0)


И вот еще от http://interprogramm.narod.ru/WinApi.htm:
Цитата

code - определяет когда должна процедура перехвата обработать сообщение. Если code равен HC_ACTION, хук процедура должна обработать сообщение. Если code меньше нуля, хук процедура должна передать сообщение к функции CallNextHookEx без дальнейшей обработки и должна возвратить значение, возвращенное функцией CallNextHookEx. 

Я попробовал собрать dll в VC2005 с помещением хэндла процесса в разделяемый сегмент данных, но в этом случае dll не загружается корректно в исполняемое приложение - обращение по нулевому адресу памяти...
--- код mousehook.dll (VC2005) ---
Код

// mousehook.cpp : Defines the entry point for the DLL application.

#include "stdafx.h"

#pragma data_seg(".hdata") // секция в памяти общая для всех процессов
HINSTANCE h = NULL; // эта переменная может изменяться любым процессом, поэтому здесь не д.б. ничего лишнего
#pragma data_seg() // конец секции
#pragma comment(linker, "/section:.hdata,rws") // даем права этой секции

// объявление функции обратного вызова
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam); 

// глобальные переменные
HHOOK HookHandle; 
HINSTANCE DllInstance; 

#ifdef _MANAGED
#pragma managed(push, off)
#endif
//----------------------------------------------------------------------
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    // вызывается каждый раз когда новый процесс грузит DLL в свою память
    if( ul_reason_for_call == DLL_PROCESS_ATTACH ) {
        if( ! h ) {
            // сохpаняем handle пpоцесса DLL в глобальную пеpеменную DllInstance
            // для использования внутpи функции InstallMouseHook.
            // Так как входная функция вызывается пpежде, чем будут вызваны
            // дpугие функции в DLL, InstallMouseHook будет всегда веpен.
            // Мы помещаем hInstance в секцию .data (см. директивы pragma выше),
            // поэтому это значение будет pазличаться от пpоцесса к пpоцессу.
            DllInstance = hModule;
        }
    }

    return TRUE;
}
//----------------------------------------------------------------------
extern "C" __declspec (dllexport) BOOL InstallMouseHook(void) { 
   HookHandle = SetWindowsHookEx(WH_MOUSE, 
   reinterpret_cast<HOOKPROC>(MouseProc), DllInstance, 0); 
   if(HookHandle == NULL) 
     return false;
   else 
     return true; 
 } 
//----------------------------------------------------------------------
extern "C" __declspec (dllexport) BOOL RemoveMouseHook(void) { 
   if(UnhookWindowsHookEx(HookHandle) == 0) 
     return false; 
   else 
     return true; 
 } 
//----------------------------------------------------------------------
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam) { 
    if(code < 0) 
        return CallNextHookEx(HookHandle, code, wParam, lParam); 

    HWND h = FindWindow(L"TForm1", L"Form1");
    switch( LOWORD ( wParam ) ) {
        case WM_LBUTTONDOWN :
            PostMessage(h, WM_LBUTTONDOWN, 0, 0);
            break;
        case WM_LBUTTONUP :
            PostMessage(h, WM_LBUTTONUP, 0, 0);
            break;
        case WM_RBUTTONDOWN :
            PostMessage(h, WM_RBUTTONDOWN, 0, 0);
            break;
        case WM_RBUTTONUP :
            PostMessage(h, WM_RBUTTONUP, 0, 0);
            break;
    }
   
    // вызываем следующий хук в цепи 
    return CallNextHookEx(HookHandle, code, wParam, lParam); 
 }
//----------------------------------------------------------------------

#ifdef _MANAGED
#pragma managed(pop)
#endif

Автор: jonie 17.6.2008, 00:11
убери прагмы. по умолчанию все итак должно быть ок (если я чего не путаю).
могу только дать пример длл-ки на asm.. когдато писал (правда там хук клавы, но фигня одна). простенькая и без наворотов....она работала)
кстати, есть низкоуровневые хуки, они не требуют длл-ки...

Автор: FiMa1 18.6.2008, 23:13
jonie, спасибо еще раз помощь!
Цитата

кстати, есть низкоуровневые хуки, они не требуют длл-ки...

На том и порешим. Ниже код рабочего низкоуровневого хука для мыши. Вот только по закрытии формы, все жестко подвисает, но это уже совсем другая история  smile .
Код

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include <windows.h>
#include <stdio.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
HHOOK MouseHook;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode == HC_ACTION) {
        switch (wParam) {
            case WM_LBUTTONDOWN :
                Form1->Label1->Caption = "LEFT BUTTON PRESSED";
                break;
            case WM_RBUTTONDOWN :
                Form1->Label1->Caption = "RIGHT BUTTON PRESSED";
                break;
        }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnStopHookClick(TObject *Sender) {
    UnhookWindowsHookEx(MouseHook);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender) {
    MouseHook = SetWindowsHookEx(WH_MOUSE_LL,
        reinterpret_cast<HOOKPROC>(LowLevelMouseProc), HInstance, NULL);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) {
    UnhookWindowsHookEx(MouseHook);
}

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)