What is an efficient way to port HWND to objects in C ++?

I work with C ++ and Win32 (not MFC / ATL). I play with writing my own class library to wrap certain Win32 objects (in particular, HWND).

When it comes to creating windows, I find the RegisterClassEx / CreateWindowEx method very inconvenient. This construction makes it difficult to write simple class wrappers (you need to resort to thunks, or TLS, or some other complex mechanism).

It seems to me that it would be easier just to tell the application to specify the window procedure and the user data pointer during window creation.

Is there an obvious reason why I lack design? Is there really a simple and effective way to do this?

+3
c ++ design winapi
source share
2 answers

ATL CWindow and CWindowImpl are your friends.

CWindowImpl make takes care of the inconvenience of the RegisterClass / CreateWindow you are talking about.

CWindow is the base wrapper class for HWND, in which all win32 functions are abstracted.

I prefer ATL over MFC. ATL is a very easy set of classes with all the source code. This is a simple #include without additional libraries or runtime. After repeatedly rolling my own WndProcs classes and encapsulating windows, I found CWindowImpl a joy to work with. You must declare a global instance of AtlModuleExe in your code in order to use it, but in addition, the ATL remains aloof.

The documentation links for these classes are below: CWindow: http://msdn.microsoft.com/en-us/library/d19y607d.aspx

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

Update: here is an example of the code that I dug for you:

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; } 
+7
source share

Resort to thunks or tls? In this case, I do not know what you mean by impact, but it is quite easy - albeit a bit confusing - to load a window into the shell of the C ++ class.

 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); } }; 

This is all a bit confusing, but that means the window can be safely destroyed by deleting the class, OR, being destroyed. During a call to CreateWindow, one or two size-related messages are sent before WM_CREATE sets GWL_USERDATA to "this", but they have practically no meaning. A window class is automatically created when the window is first created.


One thing this style of auto-registering classes on the first call to does not support is the creation of this window type as a control in a dialog box. To support this case, you will need to change an integer number of things ... provide a function for registering a static class ... the "new MyClass" in the WM_CREATE static handler ... it is not obvious to me how this can be done in the frame type style.

+1
source share

All Articles