2011-11-25 2 views
1

Win32 API에서 창에는 메시지를 처리하는 WndProc 함수의 사용자 정의 버전에 대한 포인터가 있습니다.Win32 : 더 많은 "개체 지향"창 메시지 처리 시스템

MFC 메시지 맵과 같은 솔루션으로이 낮은 수준의 메커니즘을 다룰 수있는 몇 가지 방법이 있습니다.

아주 작은 응용 프로그램에서 객체 지향 솔루션으로이 저수준 것을 캡슐화 할 방법을 찾고 있습니다.

HWND 키와 "MyWindowClass"항목을 사용하여 C++ 맵을 만들려고했는데 MyClass의 개체를 만들 때지도에 쌍을 추가 한 다음 HWN에서 MyWindowClass 개체를 찾았습니다. 하지만 문제는 Win32라는 CreateWindowEx가 내부적으로 방금 만든 창에 WM_CREATE 메시지를 보내서이 메시지 앞에 맵을 추가 할 수 없으며 WM_CREATE를 인스턴스화 된 WndProc 인스턴스로 전달하여 제어 할 수 없기 때문입니다.

코드는 다음과 같습니다

#ifndef NOTIFYWINDOW_H 
#define NOTIFYWINDOW_H 

#include "Bacn.h" 

class NotifyWindow 
{ 
private: 

    HWND m_hWnd; 

    Gdiplus::Graphics* m_graphics; 

protected: 

    static std::map<HWND, NotifyWindow*> s_NotifyWindows; 

    static LRESULT CALLBACK s_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 

    static void s_WndMessageLoop(); 

    LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam); 

    void Initialize(); 

    void OnPaint(); 

    void OnCreate(); 

public: 

    NotifyWindow(); 

    ~NotifyWindow(); 
}; 

#endif //NOTIFYWINDOW_H 

및 구현 :

#include "NotifyWindow.h" 

using namespace Gdiplus; 
using namespace std; 

map<HWND*, NotifyWindow> NotifyWindow::s_NotifyWindows; 

LRESULT CALLBACK NotifyWindow::s_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    map<HWND, NotifyWindow*>::iterator search = s_NotifyWindows.find(hWnd); 
    if (search != s_NotifyWindows.end()) 
    { 
     search->second->WndProc(uMsg, wParam, lParam); 
    } 

    return DefWindowProc(hWnd, uMsg, wParam, lParam); 
} 

void NotifyWindow::s_WndMessageLoop() 
{ 

} 

LRESULT NotifyWindow::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    switch (uMsg) 
    { 
    case WM_CREATE: 
     OnCreate(); 
     break; 
    case WM_PAINT: 
     OnPaint(); 
     break; 
    case WM_DESTROY: 
     PostQuitMessage(0); 
     break; 
    case WM_SIZE: 
     break; 
    case WM_SETFOCUS: 
     break; 
    case WM_KILLFOCUS: 
     break; 
    case WM_MOUSEMOVE: 
     break; 
    default: 
     return DefWindowProc(m_hWnd, uMsg, wParam, lParam); 
    } 
    return 0; 
} 

void NotifyWindow::Initialize() 
{ 
    WNDCLASSEX wc; 

    const wchar_t *className = L"BacnNotifyWindowClass"; 
    const wchar_t *windowName = L"BacnNotifyWindow"; 

    HINSTANCE hInstance = GetModuleHandle(NULL); 

    wc.cbSize = sizeof(WNDCLASSEX); 
    wc.lpszClassName = className; 
    wc.lpfnWndProc = NotifyWindow::s_WndProc; 
    wc.hInstance = hInstance; 
    wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
    wc.hIcon = NULL; 
    wc.hIconSm = NULL; 
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 
    wc.lpszMenuName = NULL; 
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; 
    wc.cbClsExtra = 0; 
    wc.cbWndExtra = 0; 

    RegisterClassEx(&wc); 

    DWORD dwExtStyle = WS_EX_TOPMOST; 
    DWORD dwStyle = WS_POPUP | WS_SYSMENU; 

    m_hWnd = CreateWindowEx(
     dwExtStyle, 
     className, 
     windowName, 
     dwStyle, 
     300, 
     300, 
     100, 
     100, 
     NULL, 
     NULL, 
     hInstance, 
     NULL); 

    s_NotifyWindows.insert(pair<HWND, NotifyWindow*>(m_hWnd, this)); 

    ShowWindow(m_hWnd, SW_SHOW); 
} 

NotifyWindow::NotifyWindow() 
{ 
    Initialize(); 
} 

NotifyWindow::~NotifyWindow() 
{ 

} 

void NotifyWindow::OnPaint() 
{ 

} 

void NotifyWindow::OnCreate() 
{ 

} 
+0

가능한 복제본 [WndProc에서 사용할이 포인터를 저장하는 가장 좋은 방법] (http://stackoverflow.com/questions/117792/best-method-for-storing-this-pointer-for-use-in-wndproc)) – legends2k

답변

4

제안 : 당신의 윈도우 기본 클래스에 가상 WndProc을합니다. 포인터를 저장하기에 충분히 큰 각 창 인스턴스에 대해 추가 메모리를 할당하도록 Windows에 요청하십시오 (cbWndExtra 사용). 창을 만들 때 SetWindowLongPtr을 사용하여 각 창 인스턴스와 연관된이 추가 메모리에 Windows 클래스 객체에 대한 포인터를 놓습니다. static sWndProc에서 GetWindowLongPtr을 사용하여이 포인터를 검색하고 창 기본 클래스 함수 인 virtual WndProc을 호출하십시오. 필자의 의견으로는 WndProc 호출을 전담하는 전체 추가지도 객체를 갖는 것보다 훨씬 깔끔한 방법입니다.

편집 : 게다가, 당신은 당신의 코드에서 창 클래스의 개체를 만들 때마다 Windows 창 클래스를 등록하려고한다는 것을 알고 있습니까? 하나의 창만 만들면 기술적으로 문제가 없지만 오류가 발생하기 쉬운 디자인이라고해도 과언이 아닙니다. Windows 창 클래스는 한 번만 등록해야하며이 Windows 창 클래스로 창을 만들 때마다 등록해야합니다.

편집 : switch 절, static sWndProc에서 두 번째 시간 내에 멤버 함수에서 처음으로 : 당신이 당신의 WndProc에 Windows 메시지를 처리하지 않는 경우 또한, 코드에서, 당신은 코드이 메시지를 두 번 DefWindowProc를 호출합니다. 동일한 메시지에서 DefWindowProc을 두 번 호출하면 안됩니다.

편집 : 미안하지만, 어떻게 든 당신의 실제 질문을 놓친 나는 귀하의 게시물은 WM_CREATE이 아니라 디자인에 관한 것이라고 생각했습니다. 균일 한 방법으로 WM_NCCREATE, WM_NCCALCSIZE 또는 WM_CREATE을 처리하려면 CreateWindowEx을 호출하여 lpParam을 다시 창 클래스 개체에 대한 포인터로 설정할 수 있습니다. 이 매개 변수는 CREATESTRUCT의 회원 인 static sWndProcWM_NCCREATEWM_CREATE으로 전달됩니다. 예를 들어 WM_NCCREATE (보낼 첫 번째 메시지)을 static sWndProc 안에 처리하고이 포인터를 객체에 넣은 다음 SetWindowLongPtr을 사용하여 추가 메모리 공간에 넣은 다음이를 사용하여 멤버 함수 WndProc을 호출 할 수 있습니다. 생성자에서 CreateWindowEx을 호출하면 Windows 클래스의 완전히 생성 된 객체가 아닙니다. 이렇게하면 프로그램의 다른 곳으로 파견되는 "저수준"Windows 메시지에 대해 걱정할 필요가 없습니다. 물론 실제 함수 호출에 메시지를 보내려면 멤버 함수 WndProc이 필요합니다.

+0

Borland의 VCL 프레임 워크는 WndProc 프록시 함수로 사용하기 위해 실행 가능한 메모리 블록을 동적으로 할당하여이 문제를 해결합니다. 대상 객체 포인터는이 프록시 내에 저장됩니다. OS가 프록시를 호출 할 때 프록시는 객체 포인터를 가져 와서 가상 메소드를 호출하여 메시지를 처리합니다. 이렇게하면 첫 번째 메시지가 수신되기 전에도 개체 포인터가 이동할 준비가됩니다 (개체 포인터는 다른 목적으로 HWND에 저장되지만 메시지를 전달하는 데는 사용되지 않습니다). 메시지는 HWND를 사용하지 않고 OS에서 직접 개체로 전달됩니다. –

+0

@ RemyLebeau-TeamB 일반적으로 콜백을 다루는 깔끔한 방법이지만이 경우 비트가 너무 심하지 않습니까? 호출 규칙, 'this'포인터 및 가능한 가상 테이블, 플랫폼 종속 어셈블러 사용, 객체 당 VirtualAlloc()으로 할당 된 메모리 (여러 포인터 및 어셈블러 명령어 등)에 대한 컴파일러 규칙에 대한 지식이 필요합니다. 적어도), 그리고 느린 실행. – lapk

+0

또한,이 특정 케이스 - 윈도우 생성과 함께, 나는 당신이이 기술로부터 어떻게 이익을 얻는 지 조금 혼란스러워합니다. 내가 만든 각 창 인스턴스를 하위 클래스 화하지 않고 메서드를 사용하는 방법을 알아낼 수 없습니다. Windows에 동적으로 할당 된 메모리에있는 콜백 주소를 각 개체마다 다르게 제공해야합니다. 그리고 창 인스턴스를 서브 클래스 화하려면'HWND'가 필요합니다. 즉, 가장 먼저 할 수있는 것은 WM_NCCREATE입니다. 아니면 각 창 인스턴스를 하위 클래스 화하지 않고도이를 수행 할 수있는 방법이 있습니까? 방법?! – lapk