所以我在Win32中为GUI编程编写了一些包装类。我从一个Window
类开始,到目前为止它包含一个MainLoop
方法,它基本上是标准Win32 WinMain
函数的克隆。这样,人们可以这样做:
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int nCmdShow) {
Window *win = new Window();
// Do all your widget creation and add it to the window object...
return win->MainLoop(hInst, hPrev, szCmdLine, nCmdShow);
}
在window对象的MainLoop
方法中,它必须通过设置其lpfnWndProc
成员来创建新的Win32窗口。正如任何Win32程序员所知,该成员是指向特定定义的WndProc
函数的函数指针。问题是,如果我要创建一个WndProc
函数,我需要访问该窗口对象的成员(以便它知道在窗口上绘制什么,等等)。这给我留下了两个选择(我知道):
我可以在顶层定义WndProc
,但会切断对对象成员的访问权。
我可以将它定义为类方法,但是它不是lpfnWndProc
要求的确切函数类型,所以我无法设置它!
任何人都可以帮我解开这个捕获22吗?
答案 0 :(得分:4)
您还可以将其设为静态成员函数。 :)
无论如何,解决方案取决于您是否只需要一个窗口或是否需要多个窗口。
首先是单个窗口的解决方案:
// in .h
class Window{
public:
static LRESULT WINAPI MessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT InternalMessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
// ...
};
// in .cpp
#include "Window.h"
Window* global_window = 0;
Window::Window(/*...*/){
if(!global_window)
global_window = this;
else
// error or exception... or something else
}
LRESULT WINAPI Window::MessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
return global_window->InternalMessageProc(hWnd, msg, wParam, lParam);
}
现在,如果您想允许多个窗口,请使用std::map
(或者如果您的编译器支持std::unordered_map
)。
修改:此解决方案带有一些微妙的问题。正如@Ben Voigt在他的评论中指出的那样,由于在MessageProc
内调用了CreateWindow(Ex)
,你会遇到鸡和蛋的问题,但只有在CreateWindow(Ex)
调用之后才会有窗口句柄。这是基于Ben的下一条评论的解决方案(谢谢!):
// Window.h stays the same
// in .cpp
#include "Window.h"
#include <map>
std::map<HWND, Window*> window_map;
Window* currently_created_window = 0;
Window::Window(){
currently_created_window = this;
window_handle = CreateWindow(/*...*/);
}
LRESULT WINAPI Window::MessageProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
// if the key 'hWnd' doesn't exist yet in the map
// a new key-value pair gets created and the value gets value-initialized
// which, in case of a pointer, is 0
if(window_map[hWnd] == 0){
// window doesn't exist yet in the map, add it
window_map[hWnd] = currently_created_window;
}
window_map[hWnd]->InternalMessageProc(hWnd, msg, wParam, lParam);
}
但要小心,因为上面的示例不是线程安全的。你需要互斥锁定窗口的创建:
Window::Window(/*...*/){
Lock lock_it(your_mutex);
currently_created_window = this;
window_handle = CreateWindow(/*...*/);
lock_it.release();
// rest of the initialization
}
上面应该为线程安全做(我希望)。
答案 1 :(得分:3)
您需要创建窗口地图,并在创建新窗口时将其添加到此全局地图中。您可以使用简单的链接列表,而不是当然。
map<HWND, Window *> wndmap;
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wparam, LPARAM lparam)
{
Window *pWnd = wndmap [hwnd];
....
}
答案 2 :(得分:1)
WndProc
不能是实例成员函数,因为Windows不会传递任何隐藏的this
参数。它可以是命名空间范围或静态成员。
一个简单的解决方案是使用map<HWND, Window*>
查找对象,然后将参数转发给对象上的方法。
请注意,WndProc
可以维护地图本身,因为CreateWindow
提供了一个不透明的用户参数,该参数显示在WM_CREATE
中,这对于携带Window *
很有用,然后您删除了WM_DESTROY
中的条目。
答案 3 :(得分:0)
将WndProc定义为静态类成员 - 这将与非成员函数指针兼容(对于我所知的所有编译器),例如Win32编程中使用的那些。
但我不得不说这有点浪费时间 - 那里有很多Windows类库,而且我认为这个世界真的不需要另外一个。
答案 4 :(得分:-1)
听起来你需要声明函数而不定义它。这就是原型的用途。
class Object;
void f(Object* o);
class Object {
public:
...
void some_method() {
... &f ...
}
void another_method() {
...
}
...
};
void f(Object* o) {
...
o->another_method();
...
}
反过来也可能。
class Object {
public:
...
void some_method();
void another_method();
...
};
void f(Object* o) {
...
o->another_method();
...
}
void Object::some_method() {
... &f ...
}
void Object::another_method() {
...
}