SetWindowsHookEx greift bei manchen Fenstern nicht
-
Hallo
habe vor, die Messages eines bestimmten Fensterns abzufangen (Maus und Tastaturereignisse), um diese auszuwerten. Bin dazu auf Hooks gestoßen und habe entsprechend mit Hilfe einer DLL so einen auch umgesetzt.
Das funktioniert soweit auch ganz gut, wenn mein Programm / Windows Explorer oder auch Notepad im Vordergrund ist. Sobald ein Ereignis auf Ebene von Firefox oder meiner Entwicklungsumgebung ausgeführt wird, scheint der Hook nicht zu funktionieren.
Mein Aufruf sieht derzeit so aus:SetWindowsHookEx(WH_MOUSE, mouseProc, this->hInstHookDll, NULL);
Prinzipiell möchte ich nur von genau einem Fenster die Messages auslesen (Firefox) und alle anderen Fenster sollen nicht berücksichtigt werden.
Meine Frage dazu also:
1. Woran könnte es liegen,dass der Hook bei manchen Fenstern nicht greift
2. Brauch ich überhaupt Hooks oder kann ich sowas auch einfacher lösen.
-
BasicMan01 schrieb:
Prinzipiell möchte ich nur von genau einem Fenster die Messages auslesen (Firefox) und alle anderen Fenster sollen nicht berücksichtigt werden.
Hast du es schonmal mit subclassing versucht? damit schaltet man zB vor die WndProc des Fensters eine eigene in der man Nachrichten verarbeiten/abfangen kann.
BasicMan01 schrieb:
2. Brauch ich überhaupt Hooks oder kann ich sowas auch einfacher lösen.
also ich hätte es einfach mit subclassing gelöst^^
Beispiel für notepad:
#include<iostream> #include<Windows.h> using namespace std; LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { switch(message) { case WM_PAINT: cout<<"WM_PAINT\n"; MessageBox(0, TEXT("WM_PAINT"), TEXT("Message:"), MB_OK); break; } WNDPROC pWndProc = (WNDPROC)GetWindowLongPtrW(hwnd,GWL_USERDATA); return CallWindowProc(pWndProc,hwnd,message,wparam,lparam); } int main() { HWND hwnd=FindWindow(0, TEXT("Unbenannt - Editor")); DWORD pid; GetWindowThreadProcessId(hwnd, &pid); HANDLE handle=OpenProcess(PROCESS_ALL_ACCESS, false, pid); LONG_PTR OldWndProc= SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) WndProc); SetWindowLongPtrW(hwnd,GWL_USERDATA,OldWndProc); }
in dem Bsp habe ich zB mal WM_PAINT genommen^^
EDIT: man kann natürlich keine fenster eines anderen prozesses subclassen ohne in diesen eine dll zu injezieren^^ (aber für fenster des eigenen prozesses geht das)
lg,
andi01.
-
Für ein ähnliches Problem habe ich mal den Code 1:1 adaptiert allerdings stürzt mein Programm anschließend sofort ab.
Mit ein bisschen Debugging habe ich herrausgefunden das SetWindowLongPtr (im übrigen ein Macro für SetWindowLong) mir den Returnwert 0 zurückgibt das laut MSDN ein Fehlercode ist.
Dort wird weiter beschieben das man vorher SetLastError(0) aufrufen soll. Das habe ich auch gemacht nur dann bekomme ich bei GetLastError() wiederum den Fehlercode 0.
Also doch kein Fehler? Aber warum crasht meine Programm?
-
hm code 0 heißt schonmal, dass das Ersetzen der WndProc fehlerfrei lief... vielleicht solltest du in der neuen WndProc mal die wichtigsten nachrichten behandeln (in meinem bsp hatte ich ja nur WM_PAINT).
lg,
andi01.
-
also ich habe es jetzt mal selbst getestet (an einem fenster meines prozesses), und ich muss sagen, obiger Code funktioniert bei mir perfekt!
(edit: ich benutze Visual Studio 2010 und Win7 Professional 32bit)daran sollte es nicht liegen...obiger Code stimmt
ansonsten kann ich dir jetzt ohne codeausschnitte auch nicht sagen warum dein prog crasht weil der Fehler offensichtlich irgendwo im Rest deines Codes liegt.
für den Fall dass du den Fehler selber suchen willst, hier mal mein getesteter Code (funktioniert!)
#include<iostream> #include<Windows.h> #include<WindowsX.h> #include<cstdio> HINSTANCE hi; HWND fenster; LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { switch(message) { case WM_PAINT: cout<<"WM_PAINT\n"; break; } WNDPROC pWndProc = (WNDPROC)GetWindowLongPtrW(hwnd,GWL_USERDATA); return CallWindowProc(pWndProc,hwnd,message,wparam,lparam); } LRESULT CALLBACK wndproc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { return DefWindowProc(hwnd, message, wparam, lparam); } DWORD WINAPI neues_fenster(LPVOID IpParameter) { WNDCLASS wc; wc.cbClsExtra=0; wc.cbWndExtra=0; wc.hbrBackground=(HBRUSH) GetStockObject(LTGRAY_BRUSH); wc.hCursor=LoadCursor(hi, IDC_ARROW); wc.hIcon=LoadIcon(hi, IDI_WINLOGO); wc.hInstance=hi; wc.lpfnWndProc=wndproc; wc.lpszClassName=TEXT("Fensterklasse"); wc.lpszMenuName=0; wc.style=CS_HREDRAW|CS_VREDRAW; RegisterClass(&wc); fenster=CreateWindow(TEXT("Fensterklasse"), TEXT("Test"), WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, 0, 0, hi, 0); UpdateWindow(fenster); ShowWindow(fenster, SW_NORMAL); MSG msg; while(GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } int main() { DWORD id; HANDLE h=CreateThread(0, 0, neues_fenster, 0, 0, &id); Sleep(1000); DWORD pid; GetWindowThreadProcessId(fenster, &pid); HANDLE handle=OpenProcess(PROCESS_ALL_ACCESS, false, pid); cout<<"errorcode OpenProcess: "<<GetLastError()<<endl; LONG_PTR OldWndProc= SetWindowLongPtr(fenster, GWLP_WNDPROC, (LONG_PTR) WndProc); SetWindowLongPtrW(fenster,GWL_USERDATA,OldWndProc); cout<<"errorcode SetWindowLongPtr: "<<GetLastError()<<endl; getch(); }
immer wenn mein fenster neu gezeichnet wird wird in der Konsole brav WM_PAINT ausgegeben
und auch die errorcodes sind 0
alles hat gelklappt!
(mein fenster hat zunächst die wndproc namens "wndproc()", diese wird dann später durch "WndProc()" ersetzt^^)
edit: man kann den Code natürlich so nur für fenster des eigenen Prozesses verwenden, da man in einen fremden prozess erst eine dll injectien müsste.
lg,
andi01.
-
Also ich hole mal ein bisschen weiter aus ich schreibe ein NSIS plugin bei dem ich an die WndProc irgendwie ran muss um ein lable umzufärben.
Mein code sieht so aus (ungenutzte funktionen habe ich entfernt):
#include <windows.h> #include <Winuser.h> #include <stdio.h> #define NSISCALL __stdcall typedef struct { LPVOID xxx1;//exec_flags_type *exec_flags; int (NSISCALL *ExecuteCodeSegment)(int, HWND); void (NSISCALL *validate_filename)(char *); int (NSISCALL *RegisterPluginCallback)(HMODULE,LPVOID); } extra_parameters; typedef struct _stack { struct _stack *next; TCHAR text[ANYSIZE_ARRAY]; } stack; HMODULE g_ThisDll; #define EXPORT extern "C" void __declspec(dllexport) __cdecl LRESULT CALLBACK MessageHook(HWND, UINT, WPARAM, LPARAM); void ColorHook(WPARAM, LPARAM); EXPORT Redesign(HWND hwnd, int N_CCH, char* N_Vars, LPVOID ppST, extra_parameters* pXP) { DWORD pid; DWORD ppid=GetWindowThreadProcessId(hwnd, &pid); FILE *fp=fopen("hook.log", "a+"); fprintf(fp, "pid: %08x ppid: %08x\n", pid, ppid); fclose(fp); HANDLE handle=OpenProcess(PROCESS_ALL_ACCESS, false, pid); fp=fopen("hook.log", "a+"); fprintf(fp, "pid: %08x Handle: %08x\n", pid, handle); fclose(fp); LONG_PTR OldWndProc= SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)MessageHook); fp=fopen("hook.log", "a+"); fprintf(fp, "OldWndProc: %08x\n", OldWndProc); fclose(fp); SetLastError(0); LONG ret=SetWindowLong(hwnd,GWL_USERDATA,OldWndProc); DWORD err=GetLastError(); fp=fopen("hook.log", "a+"); fprintf(fp, "SetWindowLong: %08x Error: %08x\n", ret, err); fclose(fp); } LRESULT CALLBACK MessageHook(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { if(message == WM_CTLCOLORSTATIC) ColorHook(wparam, lparam); if(message!=275) { // um timer events nicht zu loggen FILE *fp=fopen("hook.log", "a+"); fprintf(fp, "event: %04x wp: %08x lp: %08x\n", message, wparam, lparam); fclose(fp); } WNDPROC pWndProc = (WNDPROC)GetWindowLongA(hwnd,GWL_USERDATA); return CallWindowProc(pWndProc,hwnd,message,wparam,lparam); } void ColorHook(WPARAM wp, LPARAM lp) { FILE *fp=fopen("hook.log", "a+"); fprintf(fp, "wp: %08x lp: %08x\n", wp, lp); fclose(fp); } extern "C" BOOL WINAPI _DllMainCRTStartup(HMODULE hInst,UINT Reason,LPVOID lpReserved) { g_ThisDll=hInst; return TRUE; }
Logfile
pid: 00002060 ppid: 000021ec
pid: 00002060 Handle: 000001bc
OldWndProc: 77a98954
SetWindowLong: 00000000 Error: 00000000Dummerweise wird sonnst nichts geloggt. Wenn ich die Funktion Redesign leere klappt das Programm.
Getestet unter VS2010 auf Win7 x64 compeilt als x86 Code.
-
Wie kommt es eigentlich das alle Fragen zu meinem NSIS Plugin die ich in diesem Forum stelle ignoriert werden?
-
ich ignoriere dich nicht^^ ich habe bis jetzt nur keinen offensichtlichen Fehler gefunden xD
hast du schonmal mit dem debugger nachgesehen an welcher Stelle das Programm genau crasht? das wäre auch schonmal hilfreich damit man weiß wo genau man suchen muss...
lg,
andi01.
-
Bist Du sicher, dass dieses Fenster im selben Prozess liegt wie Deine DLL?
-
@andi01
Der Code enthällt auch keinen Fehler wie ich heute erst erkannt habe...@Martin Richter
Ja tut es.Mein Fehler war:
Das Modul das meinen Code enthällt wurde nach dem Aufruf wieder entladen. Damit war der Callback auch nicht mehr erreichbar was zum Crash führte.
-
UIPI spielt Dir aber nicht zufälli einen Streich?
http://en.wikipedia.org/wiki/User_Interface_Privilege_Isolation
http://blogs.msdn.com/b/vishalsi/archive/2006/11/30/what-is-user-interface-privilege-isolation-uipi-on-vista.aspx
-
Interessant das kannte ich noch nicht. Aber nein das läuft bei mir, da das NSIS Setup ja gezielt meine Funktion (per Skript) aufruft.