我一直在使用C ++和Win32(非MFC / ATL)我正在编写自己的类库来包装某些Win32对象(特别是HWND)。
在创建窗口时,我发现“RegisterClassEx / CreateWindowEx”方法非常笨拙。这种设计使得编写简单的类包装器变得困难(必须使用thunk,或者TLS或其他一些复杂的机制)。
在我看来,让窗口过程在窗口创建时指定窗口过程和用户数据指针会更简单。
有没有明显的理由让我错过这里的设计选择?是否有一种非常简单有效的方法可供使用?
答案 0 :(得分:7)
ATL的CWindow和CWindowImpl是你的朋友。
CWindowImpl负责处理您所说的RegisterClass / CreateWindow尴尬。
CWindow是HWND的基本“包装器”类,其中抽象出了所有win32函数。
我更喜欢ATL而非MFC的原因。 ATL是一组非常轻量级的类,提供了所有源代码。这是一个简单的#include,没有额外的库或运行时来处理。在滚动我自己的WndProcs和窗口封装类多年之后,我发现CWindowImpl很愉快。您必须在代码中声明一个全局AtlModuleExe实例才能使用它,但除此之外,ATL仍然不受影响。
链接到以下这些类的文档: 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;
}
答案 1 :(得分:1)
诉诸thunks或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期间发送了一个或两个与大小相关的消息,但实际上它们并不重要。窗口类是在第一次实例化窗口时自动创建的。
在第一次调用create时,这种自动类注册方式不支持的一种方法是将这种类型的窗口实例化为对话框的控件 - 为了支持这种情况,需要更改大量的东西。 ..提供静态类注册函数...静态WM_CREATE处理程序中的“新MyClass”...对我来说,如何以框架式方式完成这一点并不明显。