有一个简单的WinAPI应用程序。它现在所做的就是:
由于我习惯于主要在C ++中编写代码,并且不允许使用MFC,因此我不得不以某种方式将其封装到C ++类中。到目前为止,我已经提出了这样的设计:
在WinMain中,完成以下操作:
Application app(hInstance, szCmdLIne, iCmdShow);
return app.exec();
,构造函数执行以下操作:
registerClass();
registerTray();
registerAutostart();
到目前为止一切顺利。现在的问题是:如何创建窗口过程(必须是静态的,因为它是指向函数的c样式指针)并跟踪应用程序对象是什么,即保持指向应用程序的指针。 / p>
主要问题是:这通常是怎么做的?我太复杂了吗?将hInstance作为参数传递给Application
构造函数是否可以? WndProc在哪里?
也许WndProc应该在课外,而Application指针应该是全局的?然后WndProc调用Application方法来响应各种事件。
还有一个可能的解决方案:使应用程序类成为单例。然后从WndProc获取该对象的句柄是微不足道的。
答案 0 :(得分:4)
答案是SetWindowLongPtr。它允许您将void *与给定的hWnd相关联。然后,在WndProc中,您只需提取所述void *,强制转换并调用成员方法。问题解决方案。使用SetWindowLongPtr有一些起伏,你必须调用其他函数来查看效果或某些BS,并且Windows在CreateWindowEx返回之前发送消息,所以你必须为GetWindowLongPtr(hWnd,GWL_USERDATA)准备返回NULL。
这当然意味着对于给定的WindowProc,使用它的所有实例都必须具有公共接口,因为使用void *可以做的事情并不多。
而且,是的,可以将HINSTANCE传递给App构造函数。我见过的样本做了一些奇怪的事情来避免这种情况,但我自己从来没有让它成功。
编辑: 不要将Get / SetWindowLong与Get / SetWindowLongPtr混淆。 Get / SetWindowLong已弃用且不安全。
答案 1 :(得分:2)
你可以扩展这个类(我 用于答案)如你所愿, 取决于您想要的消息 处理
#pragma once
#include <windows.h>
#include <process.h>
#include <iostream>
using namespace std;
static const char *g_AppName = "Test";
class CMyWindow
{
HWND _hWnd;
int _width;
int _height;
public:
CMyWindow(const int width,const int height):_hWnd(NULL),_width(width),_height(height)
{
_beginthread( &CMyWindow::thread_entry, 0, this);
}
~CMyWindow(void)
{
SendMessage(_hWnd, WM_CLOSE, NULL, NULL);
}
private:
static void thread_entry(void * p_userdata)
{
CMyWindow * p_win = static_cast<CMyWindow*> (p_userdata);
p_win->create_window();
p_win->message_loop();
}
void create_window()
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = &CMyWindow::WindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = GetModuleHandle(NULL);
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = g_AppName;
wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wcex);
_hWnd = CreateWindow(g_AppName, g_AppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, GetModuleHandle(NULL), NULL);
ShowWindow(_hWnd, SW_SHOWDEFAULT);
UpdateWindow(_hWnd);
}
void message_loop()
{
MSG msg = {0};
while (GetMessage(&msg, NULL, 0, 0))
{
if(msg.message == WM_QUIT)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
static LRESULT WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_POWERBROADCAST:
{
//power management code here
}
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
};
这是一个最小的引导程序:
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
CMyWindow t(640,480);
Sleep(10000);
return 0;
}
答案 2 :(得分:2)
不要按照提示使用Get/SetWindowLongPtr
存储您的this
指针,因为它是一个巨大的安全漏洞!您只需使用映射将HWND与指向类实例的指针相关联。您可以使用STL中的<map>
类。
顺便说一下,你可以在那里找到关于这个主题的非常好的讨论:http://blogs.msdn.com/b/oldnewthing/archive/2005/04/22/410773.aspx
答案 3 :(得分:0)
我决定把它变成一个单例,因为它是主要的应用程序类,在程序中只有一个该类的实例没有问题。
现在我想问一个更相关的问题:假设我有一个偏好对话框。我创建对话框作为资源,然后给它一个过程,并在过程中,创建一个控制器对象。这是正确的方法吗?