2014-02-12 3 views
3

article은 클래스 멤버 WndProc를 호출하는 옵션을 훌륭하게 설명합니다. this response in stackoverflow을 보았지만 CreateWindow 이후에 클래스 멤버 WndProc을 연결하는 주된 문제는 the mentioned article에서 설명한대로 일부 메시지가 손실 될 수 있다는 것입니다 (중요한 WM_CREATE 포함). WndProc의 클래스 메서드

내 질문 : 나는 최고의 하나 (성능, maintanability는, ...) 클래스 멤버의 WndProc를 만드는 것입니다 아래 또는 새 노출 방법의있는 전문가의 의견을 듣고 싶습니다. 그것을 만드는 CRITICAL_SECTION으로 보호 this 글로벌 포인터 스토리지

  1. 당 윈도우 데이터, 상기 (가 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
  2. (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); 
    

답변

5

. 전역 변수 접근 방식은 작동하지만 더러워집니다. 특히 자물쇠와. 그리고 CBT 후크는 맨 위에 있습니다. 그것은 올바른 방향을 가리 키지 만.

작성하는 동안 당신의 윈도우 프로 시저에 상태 정보를 전달하는 표준 방법은 CreateWindow 또는 CreateWindowExlpParam 매개 변수를 통해입니다.

  1. CreateWindow 또는 CreateWindowExlpParam 매개 변수에 인스턴스 포인터를 전달합니다 다음과 같이 그래서 기술이다.
  2. WM_NCCREATE 처리기에서이 값을 읽으십시오. 이 메시지는 정보를 CREATESTRUCT 구조체의 일부로 제공합니다.
  3. WM_NCCREATE에 여전히 SetWindowLongPtr을 호출하여 창의 사용자 데이터를 인스턴스 포인터로 설정하십시오.
  4. 나중에 window 프로 시저를 호출 할 때마다 GetWindowLongPtr을 호출하여 인스턴스 포인터를 얻을 수 있습니다.

레이몬드 첸 여기에 세부 사항을 보여 How can I make a WNDPROC or DLGPROC a member of my C++ class?

+0

레이몬드 첸 문서는 거의이 문제에 대한 내 솔루션과 동일합니다. +1. 유일한 차이점은''DefWindowProc'가 호출되어야 하는지를 알려주는''pThis-> WndProc'' 함수를 가지고 있다는 것입니다. (OK, 실제로 구조체를 사용하여 함수에 데이터를 전달하고 다시 꺼내지 만, 아이디어) – Skizz

+0

@DavidHeffernan 덕분에, 나는 첫 번째 방법이 약간 '끔찍한'것이라는 직감을 가졌습니다.내가 틀렸다면 나를 바로 잡는다 : 처음에는 항상 정적 WndProc','GetWindowLongPtr' 그리고 마지막으로'클래스 멤버 WndProc'를 항상 호출 할 것이므로이 솔루션을 제거했다. 나는 질문에 노출 된 메서드가 창 클래스를 직접 WndProc 클래스 멤버에게 호출 할 때 더 잘 수행 될 것이라고 맹세했다. 성능 관점에서 CBT 후크를 수행하는 것이 가치가 있습니까? – Miquel

+0

비 정적 클래스 멤버를'WndProc'로 사용할 수 없습니다. 그것은 단순히 잘못된 해결책 일뿐입니다. 그래서 모든 솔루션은'GetWindowLongPtr'를 사용하여 인스턴스를 읽고 해당 인스턴스의 메소드로 호출을 전달합니다. 그들은 모두 똑같이 수행 할 것입니다. –