Globalny hook na myszkę => przekazywanie MOUSEHOOKSTRUCT

0

Piszę prostą aplikację robiącą za menu podręczne, która reaguje na odpowiednie kliknięcia na myszce. Program zakłada globalnego hook`a na myszkę (dll), dzięki czemu może reagować w (praktycznie) każdym momencie na akcję użytkownika. Jaki mam problem? Ano z odczytaniem współrzędnych kursora, chociaż nawet nie tyle odczytanie co przekształcenie tych wartości na poprawne dane (czyt. współrzędne globalne, a nie lokalne danego okna). Funkcja:

mouseProc(int code, WPARAM wParam, LPARAM lParam) {
    PostMessage(hWnd, MS_HOOK, wParam, lParam);
    return CallNextHookEx(hMouseHook, code, wParam, lParam);
}

Wysyła do głównego (mojego) okna informację o tym, że coś się dzieje z myszką, w wParam znajduje się komunikat, w lParam wskaźnik na strukturę MOUSEHOOKSTRUCT (link). W owej strukturze znajdują się między innymi koordynaty kursora oraz uchwyt okna z którego został wysłany komunikat.

I problem występuje właśnie w tym momencie. W samej dll`ce (w mouseProc), wartości te istnieją i dają się odczytywać, natomiast gdy próbuję odczytać dane w moim oknie (programie), dostaję od systemu "Access Violation". Przykład:

MOUSEHOOKSTRUCT *pm;
HWND test;

pm = (MOUSEHOOKSTRUCT *)lParam;
test = pm->hwnd; // Access Violation

Próbowałem to sobie tłumaczyć w taki oto sposób: dllka w momencie zakładania hooka jest wstrzykiwana w program, z którego będą odbierane komunikaty (myszki). Gdy dllka posiada wskaźnik, to ma prawo dostępu do obszaru pamięci, na który owy pointer wskazuje - dlatego w funkcji mouseProc(), mogę odczytać potrzebne dane. Natomiast, gdy dllka prześle wskaźnik na strukturę do mojego okna (programu), to system odmawia pozwolenia na odczyt danego obszaru pamięci - na co wskazuje właśnie "Access Violation".

Jeśli dobrze rozumuję, to w takim razie jak obejść ten problem...? Mogę wysłać każdą składową struktury w osobnym komunikacie, jednak jest to IMHO czasochłonne, skomplikowane (odbieranie wielu komunikatów, synchronizacja/kolejkowanie itp.) i raczej mało atrakcyjne rozwiązanie. W takim razie powinienem skorzystać z pamięci współdzielonej (shared memory)? Jeśli tak, to gdzie znajdę klasę dla C++ +WinAPI?

0
mordek napisał(a)

Próbowałem to sobie tłumaczyć w taki oto sposób: dllka w momencie zakładania hooka jest wstrzykiwana w program

dokladnie tak

mordek napisał(a)

Gdy dll`ka posiada wskaźnik, to ma prawo dostępu do obszaru pamięci, na który owy pointer wskazuje - dlatego w funkcji mouseProc(), mogę odczytać potrzebne dane.

tak. dokladniej mowiac: DLLka pracuje w przestrezni adresowej programu-ofiary. wszystkie adresy ktore DLLka widzi, sa adresami z pamieci tego programu.

mordek napisał(a)

Natomiast, gdy dll`ka prześle wskaźnik na strukturę do mojego okna (programu), to system odmawia pozwolenia na odczyt danego obszaru pamięci - na co wskazuje właśnie "Access Violation".

dzieje sie tak, poniewaz DLLka przesyla ten wskaznik 'zywcem'. a przeciez wysyla ten numerek(adres) do innego programu, ktory ma wlasna przestrzen adresowa! pamiec nie jest jednolita. adres 0xaabbccdd z programu A moze zawierac wartosc XYZ, a ten sam adres 0xaabbccdd dla programu B moze zawierac ABC. poczytaj o wirtualnej przestrzeni adresowej, o pamieci wirtualnej itp. w skrocie dosc powiedziec, ze ani program A, ani program B nie ma racji. adres 0xaabbccdd jest udawany przez system operacyjny i w rzeczywistosci jest dla programu A przeliczany w locie np. na 0x12345678 a dla programu B na 0x87654321. dlatego NIGDY PRZENIGDY *) nie wolno przekazywac wskaznikow zywcem do innych programow (tzn. do innych procesow)

*) tzn. czasem trzeba, tez pewnie bedziesz musial.. mozesz poczytac o pamieci wspoldzielonej (shared memory), czyli mapowaniu tego samego 'fizycznego' obszarow dla dwoch+ procesow na raz. wtedy wskaznik X w programie A pokazuje na dane z programu B spod wskaznika Y. w szczegolnosci czasem moze sie nawet udac ze X=Y, ale nie warto liczyc na to.

mordek napisał(a)

Jeśli dobrze rozumuję, to w takim razie jak obejść ten problem...? Mogę wysłać każdą składową struktury w osobnym komunikacie, jednak jest to IMHO czasochłonne, skomplikowane (odbieranie wielu komunikatów, synchronizacja/kolejkowanie itp.) i raczej mało atrakcyjne rozwiązanie.

oj, tak. zapomnij

mordek napisał(a)

W takim razie powinienem skorzystać z pamięci współdzielonej (shared memory)? Jeśli tak, to gdzie znajdę klasę dla C++ +WinAPI?

obawiam sie ze gotowa to bedzie trudno znalezc. chociaz np. w standardowym Boost znajdziesz pakiet shmem. tylko-uwaga-sprawdzalem go, jest miejscami niedopracowany. SHMem ogolnie nie jest taki latwy.. moze sprobuj najpierw socketow po localhoscie (to bedzie bardzo proste) albo named pipe'ow (ladniejsze, minimalnie trudniejsze) i jak sie okaze zbyt wolne to wtedy przejdz na shmema?

0

dzieje sie tak, poniewaz DLLka przesyla ten wskaznik 'zywcem'. a przeciez wysyla ten numerek(adres) do innego programu, ktory ma wlasna przestrzen adresowa! pamiec nie jest jednolita. adres 0xaabbccdd z programu A moze zawierac wartosc XYZ, a ten sam adres 0xaabbccdd dla programu B moze zawierac ABC. poczytaj o wirtualnej przestrzeni adresowej, o pamieci wirtualnej itp. w skrocie dosc powiedziec, ze ani program A, ani program B nie ma racji. adres 0xaabbccdd jest udawany przez system operacyjny i w rzeczywistosci jest dla programu A przeliczany w locie np. na 0x12345678 a dla programu B na 0x87654321. dlatego NIGDY PRZENIGDY *) nie wolno przekazywac wskaznikow zywcem do innych programow (tzn. do innych procesow)

Ok przyswojone, zresztą wcześniej już mnie przestrzegano przed taką zabawą.

obawiam sie ze gotowa to bedzie trudno znalezc. chociaz np. w standardowym Boost znajdziesz pakiet shmem. tylko-uwaga-sprawdzalem go, jest miejscami niedopracowany. SHMem ogolnie nie jest taki latwy.. moze sprobuj najpierw socketow po localhoscie (to bedzie bardzo proste) albo named pipe'ow (ladniejsze, minimalnie trudniejsze) i jak sie okaze zbyt wolne to wtedy przejdz na shmema?

Z socketami problemu nie będzie - choć wolałbym zrobić to bez ingerencji w tą warstwę. Zostają pipey. Czy będą problemy, na pewno ;] (bo wiem tylko jak i do czego są używane np. w Linuksie). Jednak jeśli to rozwiąże mój problem to spróbuję... IMHO zbyt wolne nie powinny być, ale to już się okaże ;}.
Thx za odpowiedź, wracam do zabawy ^^.

0

Nie byłoby prościej z DirectInput? Będziesz miał dostęp do ruchów myszy w obrębie całej aktualnej sesji, jedyny imgo głupi wymóg to posiadanie okna, które musi być aktywne podczas inicjacji.

Ostatnio pisałem program zapisujący 'desktop' w avi i potrzebowałem wykryć klikanie myszą by uwizualizować ten fakt w materiale video. Hook odpadł bo jest przecież prostsze rozwiąznie:

void __stdcall CDesktopCapture::MouseThreadProc()
{
  IDirectInput8       *dinput;
  IDirectInputDevice8 *mouse;
  InitCommonControls();

  HWND hwnd = CreateWindowEx(0, TOOLTIPS_CLASS,0, WS_POPUP|WS_VISIBLE, 0, 0, 0, 0, 0, 0, _hinstance, 0);

  if (!DirectInput8Create(_hinstance, DIRECTINPUT_VERSION, IID_IDirectInput8A, &dinput, 0))
  {
    if (!dinput->CreateDevice(GUID_SysMouse, &mouse, 0))
    {
      mouse->SetDataFormat(c_dfDIMouse))

      if (!mouse->SetCooperativeLevel(hwnd, DISCL_NONEXCLUSIVE|DISCL_BACKGROUND))
      {
        mouse->SetEventNotification(m_mouseevent))
        mouse->Acquire();

        while (m_mouseevent) // wyzeruj by zakończyć wątek
        {
          if (!WaitForSingleObject(m_mouseevent, 1000))
          {
            DIMOUSESTATE state;
            if (!mouse->GetDeviceState(sizeof(state), &state))
            {
              if (state.rgbButtons[0] & 0x80) // left button
              {
                m_clicked = true;
              }
              if (state.rgbButtons[2] & 0x80) // wheel
              {
                m_break = true;
              }
            }
          }
        }
        mouse->SetEventNotification(0);
      }
      mouse->Release();
    }
    dinput->Release();
  }
  DestroyWindow(hwnd);
  ExitThread(0);
}
0

W takim razie powinienem skorzystać z pamięci współdzielonej (shared memory)? Jeśli tak,
to gdzie znajdę klasę dla C++ +WinAPI?

To i tak będzie wymagać jakiejś synchronizacji ,najprostszy przykład pamięci
współdzielonej to pliki mapowane w pamięci .
Proces tworzący plik [1] :

unsigned char*_buf ;

  HANDLE  hFileMapp = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,
                                                     0,_size,_name);
  if(NULL == hFileMapp)
  {
       _buf = NULL;

  }else{

         _buf =(unsigned char*)MapViewOfFile(hFileMapp,
                                         FILE_MAP_ALL_ACCESS,0,0,0);

         _buf <----* 
         ...

Inny otwiera [2] , oba posiadają wspólny obszar pamięci wskazywany przez _buf :

    hFileMapp=OpenFileMapping(FILE_MAP_ALL_ACCESS,true,_name);
    if(NULL == hFileMapp)
    {
          _buf = NULL;

    }else{
          _buf=(unsigned char*)MapViewOfFile(hFileMapp,
                                     FILE_MAP_ALL_ACCESS,0,0,0);

         _buf <----*
         ....

Rozmiar przydzielanej pamięci powinien być == lub być wielokrotnością najmniejszego rozmiaru
strony przydzielanej przez system .
Jeśli rozmiar ten wynosi np. 4096 bajtów
próba przydzielenia 4097 spowoduje przydzielenie pamięci o wielkości 2*4096 .

Mogę wysłać każdą składową struktury w osobnym komunikacie,
jednak jest to IMHO czasochłonne, skomplikowane (odbieranie wielu komunikatów, synchronizacja/

Można w jednym WM_COPYDATA .., "wysłać" całą strukturę MOUSEHOOKSTRUCT .

0

@sapero:
W sumie... czemu nie ;] Tylko wyczuwam iż będę musiał utworzyć wątek już na samym początku działania programu, w którym będzie działać sobie funkcja MouseThreadProc.....? ;>

@dzejo:
Hym, ciekawe.. lecę poczytać...

1 użytkowników online, w tym zalogowanych: 0, gości: 1