2010-06-26 3 views
3

나는 C++과 Win32 (비 MFC/ATL)로 작업 해왔다. 특정 Win32 개체 (특히 HWND)를 래핑하기 위해 내 자신의 클래스 라이브러리를 작성하고있다.C++의 개체에 HWND를 래핑하는 효율적인 방법은 무엇입니까?

Windows를 만들 때 "RegisterClassEx/CreateWindowEx"메서드가 매우 어색함을 발견했습니다. 이 디자인은 간단한 클래스 래퍼를 작성하는 것을 어렵게 만듭니다 (하나는 썽크 (thunks) 또는 TLS 또는 다른 복잡한 메커니즘에 의존해야합니다).

응용 프로그램이 창 만들기 시간에 창 프로 시저와 사용자 데이터 포인터를 지정하게하는 것이 더 간단했을 것 같습니다.

디자인 선택에 대한 분명한 이유가 있습니까? 이 작업을 수행하기위한 정말 간단하고 효율적인 방법이 있습니까?

답변

7

ATL의 CWindow 및 CWindowImpl은 (는) 친구입니다.

CWindowImpl 제작자가 말하는 RegisterClass/CreateWindow 어색함을 처리합니다.

CWindow는 모든 win32 함수가 추상화 된 HWND의 기본 "래퍼"클래스입니다.

MFC보다 ATL을 선호하는 이유. ATL은 모든 소스 코드가 제공된 매우 가벼운 클래스 세트입니다. 처리 할 추가 라이브러리 나 런타임이없는 간단한 #include입니다. 수년 동안 내 자신의 WndProcs 및 창 캡슐화 클래스를 롤백 한 후 CWindowImpl과 함께 작업 할 수있는 기쁨을 찾았습니다. 코드에서 전역 AtlModuleExe 인스턴스를 사용하여 선언해야하지만, 그 외에도 ATL은 방해가되지 않습니다. 아래에 이러한 클래스에 대한 documenation하는

링크 : CWindow : http://msdn.microsoft.com/en-us/library/d19y607d.aspx

는 CWindowImpl : http://msdn.microsoft.com/en-us/library/h4616bh2.aspx

업데이트 : 여기 당신을 위해 발굴 일부 샘플 코드입니다 :

class CMyApp : public CAtlExeModuleT<CMyApp> 
{ 
public: 
    static HRESULT InitializeCom() 
    { 
     CoInitialize(NULL); 
     return S_OK; 
    } 
}; 

CMyApp g_app; 

class CMyWindow : public CWindowImpl<CMyWindow> 
{ 
public: 
    CMyWindow(); 
    ~CMyWindow(); 
    BEGIN_MSG_MAP(CMyWindow) 
     MESSAGE_HANDLER(WM_PAINT, OnPaint); 
     MESSAGE_HANDLER(WM_CLOSE, OnClose); 
    END_MSG_MAP(); 

    LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled); 
    LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled); 
}; 

LRESULT CMyWindow::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled) 
{ 
    // your WM_PAINT code goes here 
} 

LRESULT CMyWindow::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& fHandled) 
{ 
    PostQuitMessage(); 
} 

int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdline, int nCmdShow) 
{ 
    // 640x480 window 
    CMyWindow appwindow; 
    RECT rect = {0, 0, 640, 480}; 
    RECT rectActual = {0}; 
    appwindow.Create(NULL, rect, L"App Window", WS_OVERLAPPEDWINDOW); 
    appwindow.ShowWindow(SW_SHOW); 

    { 
     MSG msg; 
     while (GetMessage(&msg, 0, 0, 0)) 
     { 
      TranslateMessage(&msg); 
      DispatchMessage(&msg); 
     } 
    } 
    // app shutdown 

    appwindow.DestroyWindow(); 

    return 0; 
} 
+0

ATL에 대한 [WTL extensions (http://wtl.sourceforge.net/)]는 컨트롤, GDI 개체의 RAII 캡슐화, 프레임 윈도우 등을 추가하므로 유용합니다. – holtavolt

1

리조트 썽크 또는 tls? 이 경우 썽크가 무슨 의미인지는 알지 못하지만, C++ 클래스 랩퍼로 창을 부트 스트랩하기는 매우 쉽습니다.

class UserWindow 
{ 
    HWND _hwnd; 
public: 
    operator HWND(){ 
    return _hwnd; 
    } 
    UserWindow():_hwnd(0){} 
    ~UserWindow(){ 
    if(_hwnd){ 
     SetWindowLongPtr(GWL_USERDATA,0); 
     DestroyWindow(_hwnd); 
    } 
    static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ 
    UserWindow* self = 0; 
    if(uMsg == WM_CREATE) 
    { 
     LPCREATESTRUCT crst = (LPCREATESTRUCT)lParam; 
     self = (Window*)crst->lpCreateParams; 
     SetWindowLongPtr(hwnd,GWL_USERDATA,(LONG_PTR)self); 
     self->_hwnd = hwnd; 
    } 
    else 
     self = (Window*)GetWindowLongPtr(hwnd,GWL_USERDATA); 

    if(self){ 
     LRESULT lr = self->WndProc(uMsg,wParam,lParam); 
     if(uMsg == WM_DESTROY){ 
     if(self = (Window*)GetWindowLongPtr(hwnd,GWL_USERDATA)) 
      self->_hwnd = NULL; 
     } 
     return lr; 
    } 
    return DefWindowProc(hwnd,uMsg,wParam,lParam); 
    } 
    HWND Create(int x, int y, int w, int h, LPCTSTR pszTitle,DWORD dwStyle,DWORD dwStyleEx,LPCTSTR pszMenu,HINSTANCE hInstance, HWND hwndParent){ 
    WNDCLASSEX wcex = { sizeof (wcex),0}; 
    if(!GetClassInfo(hInstance,ClassName(),&wcex)){ 
     wcex.style = CS_HREDRAW | CS_VREDRAW; 
     wcex.lpfnWndProc = WindowndProc; 
     wcex.cbClsExtra = 0; 
     wcex.cbWndExtra = 0; 
     wcex.hInstance = hInstance; 
     wcex.lpszClassName = ClassName(); 
     OnCreatingClass(wcex); 
    RegisterClassEx(&wcex); 
    } 
    return CreateWindowEx(dwStyleEx, ClassName(), pszTitle, dwStyle, x, y, w, h, hwndParent, pszMenu, hInstance, this); 
    } 
    // Functions to override 
    virtual LPCTSTR ClassName(){ 
    return TEXT("USERWINDOW"); 
    } 
    virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam,LPARAM lParam){ 
    return DefWindowProc(uMsg,wParam,lParam); 
    } 
    virtual void Window::OnCreatingClass(WNDCLASSEX& wcex){ 
    wcex.hCursor = LoadCursor(NULL,IDC_ARROW); 
    } 
}; 

그것은 모든 조금 복잡하지만, 그것은 윈도우 클래스를 삭제하여, 또는 파괴에 의해 안전하게 파괴 될 수 있다는 것을 의미한다. WM_CREATE가 GWL_USERDATA를 "this"로 설정하기 전에 CreateWindow를 호출하는 동안 보내지는 하나 또는 두 개의 크기 조정 관련 메시지가 있지만 실제적으로 아무런 결과가 없습니다. 창 클래스는 창을 처음으로 인스턴스화 할 때 자동으로 만들어집니다. 첫 번째 호출에 자동 클래스 등록이 스타일은 지원하지 않습니다 만드는


한 가지 대화 상자의 컨트롤로 윈도우의 이러한 유형의 실체화 -이 경우를 필요 사물의 늪 전체를 지원하기 위해 변경할 수 있습니다 ... 정적 클래스 등록 함수를 제공합니다 ... "새 MyClass"정적 WM_CREATE 처리기에서 ... 그게 나에게 분명하지 않습니다 어떻게 Frameworkish 형식 패션에서 할 수 있습니다.

+0

두 가지 큰 실패 : 먼저 Get/SetWindowLong은 Get/SetWindowLongPtr이어야합니다. 그렇지 않으면 64 비트를 컴파일 할 수 없습니다.두 번째로, SetWindowLong/Ptr에는 효과가 나타나기 전에 호출해야하는 애매한 함수가 있습니다. http://msdn.microsoft.com/en-us/library/ms644898(VS.85).aspx – Puppy

+0

1. 나는 그것을 메모리에서 썼다. 그래서 오타가 예상된다. 그러나 정확한, 나는 Ptr 형태의 함수를 사용해야했다. 나는 그 밖의 모든 곳에서 그것을 기억했다 .- P 2. 예, 아니오. SetWindowLong은 윈도우의 여러 측면에 영향을 미칠 수 있습니다.이 중 많은 부분은 비 클라이언트 프레임 스타일 비트와 같이 윈도우 생성시에만 실제로 읽혀집니다. 이러한 스타일은 창에 새 프레임을 재생성하기 위해 플래그에 SWP_FRAMECHANGED와 함께 SetWindowPos를 호출해야합니다. GWL_USERDATA는 GetWindowLong이 값을 검색하는지 확인하기 때문에 "즉시 적용됩니다". –

관련 문제