이 article은 클래스 멤버 WndProc를 호출하는 옵션을 훌륭하게 설명합니다. this response in stackoverflow을 보았지만 CreateWindow 이후에 클래스 멤버 WndProc을 연결하는 주된 문제는 the mentioned article에서 설명한대로 일부 메시지가 손실 될 수 있다는 것입니다 (중요한 WM_CREATE 포함). WndProc의 클래스 메서드
내 질문 : 나는 최고의 하나 (성능, maintanability는, ...) 클래스 멤버의 WndProc를 만드는 것입니다 아래 또는 새 노출 방법의있는 전문가의 의견을 듣고 싶습니다. 그것을 만드는 CRITICAL_SECTION으로 보호
this
글로벌 포인터 스토리지
당 윈도우 데이터, 상기 (가 WndProc 메서드를 가진 창 클래스를 존재 suposing) 기사에서 노출이 개 최종 솔루션을 브리핑
(here에서 추출) 스레드 안전 :
// The helper window procedure // It is called by Windows, and thus it's a non-member function // This message handler will only be called after successful SetWindowLong call // We can assume that pointer returned by GetWindowLong is valid // It will route messages to our member message handler LRESULT CALLBACK WndProc2(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { // Get a window pointer associated with this window Window *w = (Window *) GetWindowLong(hwnd, GWL_USERDATA); // It should be valid, assert so _ASSERT(w); // Redirect messages to the window procedure of the associated window return w->WndProc(hwnd, msg, wp, lp); } // The temporary global this pointer // It will be used only between CreateWindow is called and the first message is processed by WndProc // WARNING: it is not thread-safe. Window *g_pWindow; // Critical section protecting the global Window pointer CRITICAL_SECTION g_WindowCS; // The helper window procedure LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { // Stash global Window pointer into per-window data area SetWindowLong(hwnd, GWL_USERDATA, (long) g_pWindow); // Unlock global critical section g_pWindow->HaveCSLock = false; LeaveCriticalSection(&g_WindowCS); // Reset the window message handler SetWindowLong(hwnd, GWL_WNDPROC, (long) WndProc2); // Dispatch first message to the member message handler return WndProc2(hwnd, msg, wp, lp); }
그리고 지금 우리는 창을 만들 수 있습니다
을 0(here에서 추출) CBT 후크 프로 시저 사용 :
// The helper window procedure // It is called by Windows, and thus it's a non-member function // This message handler will only be called after successful SetWindowLong call from the hook // We can assume that pointer returned by GetWindowLong is valid // It will route messages to our member message handler LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { // Get a window pointer associated with this window Window *w = (Window *) GetWindowLong(hwnd, GWL_USERDATA); // It should be valid, assert so _ASSERT(w); // Redirect messages to the window procedure of the associated window return w->WndProc(hwnd, msg, wp, lp); } // The CBT hook procedure // It is called during CreateWindow call before WndProc receives any messages // Its job is to set per-window Window pointer to the one passed through lpParam to CreateWindow LRESULT CALLBACK CBTProc(int code, WPARAM wp, LPARAM lp) { if (code != HCBT_CREATEWND) { // Ignore everything but create window requests // Note: generally, HCBT_CREATEWND is the only notification we will get, // assuming the thread is hooked only for the duration of CreateWindow call. // However, we may receive other notifications, in which case they will not be passed to other CBT hooks. return 0; } // Grab a pointer passed to CreateWindow as lpParam std::pair<Window *, HHOOK> *p = (std::pair<Window *, HHOOK> *) LPCBT_CREATEWND(lp)->lpcs->lpCreateParams; // Only handle this window if it wasn't handled before, to prevent rehooking windows when CreateWindow is called recursively // ie, when you create windows from a WM_CREATE handler if (p->first) { // Stash the associated Window pointer, which is the first member of the pair, into per-window data area SetWindowLong((HWND) wp, GWL_USERDATA, (long) p->first); // Mark this window as handled p->first = 0; } // Call the next hook in chain, using the second member of the pair return CallNextHookEx(p->second, code, wp, lp); }
을 그리고 지금 우리가 창을 만들 수 있습니다 : 나는 개인적으로 이러한 방법 중 하나를 사용하지 않을
// Install the CBT hook // Note: hook the thread immediately before, and unhook it immediately after CreateWindow call. // The hook procedure can only process window creation nofitications, and it shouldn't be called for other types of notifications // Additionally, calling hook for other events is wasteful since it won't do anything useful anyway HHOOK hook = SetWindowsHookEx(WH_CBT, CBTProc, 0, GetCurrentThreadId()); _ASSERT(hook); // Create window // Pass a pair consisting of window object pointer and hook as lpParam std::pair<Window *, HHOOK> p(&w, hook); HWND hwnd = CreateWindow(TEXT("BaseWnd"), TEXT("Hello, World!"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hinst, &p); // Unhook first UnhookWindowsHookEx(hook);
레이몬드 첸 문서는 거의이 문제에 대한 내 솔루션과 동일합니다. +1. 유일한 차이점은''DefWindowProc'가 호출되어야 하는지를 알려주는''pThis-> WndProc'' 함수를 가지고 있다는 것입니다. (OK, 실제로 구조체를 사용하여 함수에 데이터를 전달하고 다시 꺼내지 만, 아이디어) – Skizz
@DavidHeffernan 덕분에, 나는 첫 번째 방법이 약간 '끔찍한'것이라는 직감을 가졌습니다.내가 틀렸다면 나를 바로 잡는다 : 처음에는 항상 정적 WndProc','GetWindowLongPtr' 그리고 마지막으로'클래스 멤버 WndProc'를 항상 호출 할 것이므로이 솔루션을 제거했다. 나는 질문에 노출 된 메서드가 창 클래스를 직접 WndProc 클래스 멤버에게 호출 할 때 더 잘 수행 될 것이라고 맹세했다. 성능 관점에서 CBT 후크를 수행하는 것이 가치가 있습니까? – Miquel
비 정적 클래스 멤버를'WndProc'로 사용할 수 없습니다. 그것은 단순히 잘못된 해결책 일뿐입니다. 그래서 모든 솔루션은'GetWindowLongPtr'를 사용하여 인스턴스를 읽고 해당 인스턴스의 메소드로 호출을 전달합니다. 그들은 모두 똑같이 수행 할 것입니다. –