C++ 如何使用 Win32 API 创建多个窗口
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2886609/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make multiple windows using Win32 API
提问by Steven Lu
I see plenty of tutorials and articles showing me how to make a simple windows program, which is great but none of them show me how to make multiple windows.
我看到很多教程和文章向我展示了如何制作一个简单的 Windows 程序,这很棒,但没有一个向我展示如何制作多个窗口。
Right now I have working code that creates and draws a layered window and I can blit stuff using GDI to draw anything I want on it, drag it around, even make it transparent, etc.
现在我有创建和绘制分层窗口的工作代码,我可以使用 GDI 对东西进行 blit 来绘制我想要的任何东西,拖动它,甚至使它透明,等等。
But I wanted a second rectangular area that I can draw to, drag around, etc. In other words, a second window. Probably want it to be a child window. Question is, how do I make it?
但我想要第二个矩形区域,我可以绘制、拖动等。换句话说,第二个窗口。可能希望它是一个子窗口。问题是,我该怎么做?
Also, if anybody knows any good resources (online preferably) like articles or tutorials for window management in the Windows API, please share.
另外,如果有人知道任何好的资源(最好是在线的),例如 Windows API 中窗口管理的文章或教程,请分享。
采纳答案by JustJeff
You can hit CreateWindow() more than once if you want. The message loop in your WinMain will pass events to all the windows that WinMain creates. You can even create two overlapped windows and set the parent window of the 2nd one to be the handle of the 1st one if you want.
如果需要,您可以多次点击 CreateWindow()。WinMain 中的消息循环会将事件传递给 WinMain 创建的所有窗口。如果需要,您甚至可以创建两个重叠的窗口并将第二个的父窗口设置为第一个的句柄。
回答by rauprog
To create more than one window, repeat all the steps that you did when you created the first window to create a second window. A good way to do this is the copy and paste all the code from the first window. Then do search and replaces in which you replace all the names of the first window with unique names for the second window. The code in which I do just that is below.
要创建多个窗口,请重复创建第一个窗口时执行的所有步骤以创建第二个窗口。一个很好的方法是复制并粘贴第一个窗口中的所有代码。然后进行搜索和替换,将第一个窗口的所有名称替换为第二个窗口的唯一名称。我这样做的代码如下。
The most important thing to note is that the windows class for the second window should have a unique name at the code line "windowclassforwindow2.lpszClassName="window class2". If it doesn't have a unique name, the windows registration will fail.
最需要注意的是,第二个窗口的windows类在代码行“windowclassforwindow2.lpszClassName="window class2”处应该有唯一的名字,如果没有唯一的名字,windows注册就会失败。
#include <windows.h>
LRESULT CALLBACK windowprocessforwindow1(HWND handleforwindow1,UINT message,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK windowprocessforwindow2(HWND handleforwindow2,UINT message,WPARAM wParam,LPARAM lParam);
bool window1closed=false;
bool window2closed=false;
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpCmdLine,int nShowCmd)
{
bool endprogram=false;
//create window 1
WNDCLASSEX windowclassforwindow1;
ZeroMemory(&windowclassforwindow1,sizeof(WNDCLASSEX));
windowclassforwindow1.cbClsExtra=NULL;
windowclassforwindow1.cbSize=sizeof(WNDCLASSEX);
windowclassforwindow1.cbWndExtra=NULL;
windowclassforwindow1.hbrBackground=(HBRUSH)COLOR_WINDOW;
windowclassforwindow1.hCursor=LoadCursor(NULL,IDC_ARROW);
windowclassforwindow1.hIcon=NULL;
windowclassforwindow1.hIconSm=NULL;
windowclassforwindow1.hInstance=hInst;
windowclassforwindow1.lpfnWndProc=(WNDPROC)windowprocessforwindow1;
windowclassforwindow1.lpszClassName=L"windowclass 1";
windowclassforwindow1.lpszMenuName=NULL;
windowclassforwindow1.style=CS_HREDRAW|CS_VREDRAW;
if(!RegisterClassEx(&windowclassforwindow1))
{
int nResult=GetLastError();
MessageBox(NULL,
L"Window class creation failed",
L"Window Class Failed",
MB_ICONERROR);
}
HWND handleforwindow1=CreateWindowEx(NULL,
windowclassforwindow1.lpszClassName,
L"Parent Window",
WS_OVERLAPPEDWINDOW,
200,
150,
640,
480,
NULL,
NULL,
hInst,
NULL /* No Window Creation data */
);
if(!handleforwindow1)
{
int nResult=GetLastError();
MessageBox(NULL,
L"Window creation failed",
L"Window Creation Failed",
MB_ICONERROR);
}
ShowWindow(handleforwindow1,nShowCmd);
// create window 2
WNDCLASSEX windowclassforwindow2;
ZeroMemory(&windowclassforwindow2,sizeof(WNDCLASSEX));
windowclassforwindow2.cbClsExtra=NULL;
windowclassforwindow2.cbSize=sizeof(WNDCLASSEX);
windowclassforwindow2.cbWndExtra=NULL;
windowclassforwindow2.hbrBackground=(HBRUSH)COLOR_WINDOW;
windowclassforwindow2.hCursor=LoadCursor(NULL,IDC_ARROW);
windowclassforwindow2.hIcon=NULL;
windowclassforwindow2.hIconSm=NULL;
windowclassforwindow2.hInstance=hInst;
windowclassforwindow2.lpfnWndProc=(WNDPROC)windowprocessforwindow2;
windowclassforwindow2.lpszClassName=L"window class2";
windowclassforwindow2.lpszMenuName=NULL;
windowclassforwindow2.style=CS_HREDRAW|CS_VREDRAW;
if(!RegisterClassEx(&windowclassforwindow2))
{
int nResult=GetLastError();
MessageBox(NULL,
L"Window class creation failed for window 2",
L"Window Class Failed",
MB_ICONERROR);
}
HWND handleforwindow2=CreateWindowEx(NULL,
windowclassforwindow2.lpszClassName,
L"Child Window",
WS_OVERLAPPEDWINDOW,
200,
150,
640,
480,
NULL,
NULL,
hInst,
NULL);
if(!handleforwindow2)
{
int nResult=GetLastError();
MessageBox(NULL,
L"Window creation failed",
L"Window Creation Failed",
MB_ICONERROR);
}
ShowWindow(handleforwindow2,nShowCmd);
SetParent(handleforwindow2,handleforwindow1);
MSG msg;
ZeroMemory(&msg,sizeof(MSG));
while (endprogram==false) {
if (GetMessage(&msg,NULL,0,0));
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (window1closed==true && window2closed==true) {
endprogram=true;
}
}
MessageBox(NULL,
L"Both Windows are closed. Program will now close.",
L"",
MB_ICONINFORMATION);
return 0;
}
LRESULT CALLBACK windowprocessforwindow1(HWND handleforwindow,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY: {
MessageBox(NULL,
L"Window 1 closed",
L"Message",
MB_ICONINFORMATION);
window1closed=true;
return 0;
}
break;
}
return DefWindowProc(handleforwindow,msg,wParam,lParam);
}
LRESULT CALLBACK windowprocessforwindow2(HWND handleforwindow,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY: {
MessageBox(NULL,
L"Window 2 closed",
L"Message",
MB_ICONINFORMATION);
window2closed=true;
return 0;
}
break;
}
return DefWindowProc(handleforwindow,msg,wParam,lParam);
}
A more complex example-using functions to create windows.
一个更复杂的例子——使用函数来创建窗口。
Creating each window without a function can be make the code cluttered-especially if it is in if statements. The code below uses a separate function to create each window. The first three windows have a create window button to create the next window.
在没有函数的情况下创建每个窗口会使代码变得混乱——尤其是在 if 语句中。下面的代码使用单独的函数来创建每个窗口。前三个窗口有一个创建窗口按钮来创建下一个窗口。
#include <Windows.h>
LRESULT CALLBACK windowprocessforwindow1(HWND handleforwindow1,UINT message,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK windowprocessforwindow2(HWND handleforwindow1,UINT message,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK windowprocessforwindow3(HWND handleforwindow1,UINT message,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK windowprocessforwindow4(HWND handleforwindow1,UINT message,WPARAM wParam,LPARAM lParam);
#define createwindowbuttoninwindow1 101
#define createwindowbuttoninwindow2 201
#define createwindowbuttoninwindow3 301
bool window1open,window2open,window3open,window4open=false;
bool windowclass1registeredbefore,windowclass2registeredbefore,
windowclass3registeredbefore,windowclass4registeredbefore=false;
enum windowtoopenenumt {none,window2,window3,window4};
windowtoopenenumt windowtoopenenum=none;
void createwindow2(WNDCLASSEX& wc,HWND& hwnd,HINSTANCE hInst,int nShowCmd);
void createwindow3(WNDCLASSEX& wc,HWND& hwnd,HINSTANCE hInst,int nShowCmd);
void createwindow4(WNDCLASSEX& wc,HWND& hwnd,HINSTANCE hInst,int nShowCmd);
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpCmdLine,int nShowCmd)
{
bool endprogram=false;
WNDCLASSEX windowclassforwindow2;
WNDCLASSEX windowclassforwindow3;
WNDCLASSEX windowclassforwindow4;
HWND handleforwindow2;
HWND handleforwindow3;
HWND handleforwindow4;
//create window 1
MSG msg;
WNDCLASSEX windowclassforwindow1;
ZeroMemory(&windowclassforwindow1,sizeof(WNDCLASSEX));
windowclassforwindow1.cbClsExtra=NULL;
windowclassforwindow1.cbSize=sizeof(WNDCLASSEX);
windowclassforwindow1.cbWndExtra=NULL;
windowclassforwindow1.hbrBackground=(HBRUSH)COLOR_WINDOW;
windowclassforwindow1.hCursor=LoadCursor(NULL,IDC_ARROW);
windowclassforwindow1.hIcon=NULL;
windowclassforwindow1.hIconSm=NULL;
windowclassforwindow1.hInstance=hInst;
windowclassforwindow1.lpfnWndProc=(WNDPROC)windowprocessforwindow1;
windowclassforwindow1.lpszClassName=L"window class 1";
windowclassforwindow1.lpszMenuName=NULL;
windowclassforwindow1.style=CS_HREDRAW|CS_VREDRAW;
if(!RegisterClassEx(&windowclassforwindow1))
{
int nResult=GetLastError();
MessageBox(NULL,
L"Window class creation failed",
L"Window Class Failed",
MB_ICONERROR);
}
HWND handleforwindow1=CreateWindowEx(NULL,
windowclassforwindow1.lpszClassName,
L"Window 1",
WS_OVERLAPPEDWINDOW,
200,
150,
640,
480,
NULL,
NULL,
hInst,
NULL /* No Window Creation data */
);
if(!handleforwindow1)
{
int nResult=GetLastError();
MessageBox(NULL,
L"Window creation failed",
L"Window Creation Failed",
MB_ICONERROR);
}
ShowWindow(handleforwindow1,nShowCmd);
bool endloop=false;
while (endloop==false) {
if (GetMessage(&msg,NULL,0,0));
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (windowtoopenenum !=none) {
switch (windowtoopenenum) {
case window2:
if (window2open==false) {
createwindow2(windowclassforwindow2,handleforwindow2,hInst,nShowCmd);
}
break;
case window3:
if (window3open==false) {
createwindow3(windowclassforwindow3,handleforwindow3,hInst,nShowCmd);
}
break;
case window4:
if (window4open==false) {
createwindow4(windowclassforwindow4,handleforwindow4,hInst,nShowCmd);
}
break;
}
windowtoopenenum=none;
}
if (window1open==false && window2open==false && window3open==false && window4open==false)
endloop=true;
}
MessageBox(NULL,
L"All Windows are closed. Program will now close.",
L"Message",
MB_ICONINFORMATION);
}
void createwindow2(WNDCLASSEX& wc,HWND& hwnd,HINSTANCE hInst,int nShowCmd) {
if (windowclass2registeredbefore==false) {
ZeroMemory(&wc,sizeof(WNDCLASSEX));
wc.cbClsExtra=NULL;
wc.cbSize=sizeof(WNDCLASSEX);
wc.cbWndExtra=NULL;
wc.hbrBackground=(HBRUSH)COLOR_WINDOW;
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hIcon=NULL;
wc.hIconSm=NULL;
wc.hInstance=hInst;
wc.lpfnWndProc=(WNDPROC)windowprocessforwindow2;
wc.lpszClassName=L"wc2";
wc.lpszMenuName=NULL;
wc.style=CS_HREDRAW|CS_VREDRAW;
if(!RegisterClassEx(&wc))
{
int nResult=GetLastError();
MessageBox(NULL,
L"Window class creation failed",
L"Window Class Failed",
MB_ICONERROR);
}
else
windowclass2registeredbefore=true;
}
hwnd=CreateWindowEx(NULL,
wc.lpszClassName,
L"Window 2",
WS_OVERLAPPEDWINDOW,
200,
170,
640,
480,
NULL,
NULL,
hInst,
NULL /* No Window Creation data */
);
if(!hwnd)
{
int nResult=GetLastError();
MessageBox(NULL,
L"Window creation failed",
L"Window Creation Failed",
MB_ICONERROR);
}
ShowWindow(hwnd,nShowCmd);
}
void createwindow3(WNDCLASSEX& wc,HWND& hwnd,HINSTANCE hInst,int nShowCmd) {
if (windowclass3registeredbefore==false) {
ZeroMemory(&wc,sizeof(WNDCLASSEX));
wc.cbClsExtra=NULL;
wc.cbSize=sizeof(WNDCLASSEX);
wc.cbWndExtra=NULL;
wc.hbrBackground=(HBRUSH)COLOR_WINDOW;
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hIcon=NULL;
wc.hIconSm=NULL;
wc.hInstance=hInst;
wc.lpfnWndProc=(WNDPROC)windowprocessforwindow3;
wc.lpszClassName=L"window class 3";
wc.lpszMenuName=NULL;
wc.style=CS_HREDRAW|CS_VREDRAW;
if(!RegisterClassEx(&wc))
{
int nResult=GetLastError();
MessageBox(NULL,
L"Window class creation failed",
L"Window Class Failed",
MB_ICONERROR);
}
else
windowclass3registeredbefore=true;
}
hwnd=CreateWindowEx(NULL,
wc.lpszClassName,
L"Window 3",
WS_OVERLAPPEDWINDOW,
200,
190,
640,
480,
NULL,
NULL,
hInst,
NULL /* No Window Creation data */
);
if(!hwnd)
{
int nResult=GetLastError();
MessageBox(NULL,
L"Window creation failed",
L"Window Creation Failed",
MB_ICONERROR);
}
ShowWindow(hwnd,nShowCmd);
}
void createwindow4(WNDCLASSEX& wc,HWND& hwnd,HINSTANCE hInst,int nShowCmd) {
if (windowclass4registeredbefore==false) {
ZeroMemory(&wc,sizeof(WNDCLASSEX));
wc.cbClsExtra=NULL;
wc.cbSize=sizeof(WNDCLASSEX);
wc.cbWndExtra=NULL;
wc.hbrBackground=(HBRUSH)COLOR_WINDOW;
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hIcon=NULL;
wc.hIconSm=NULL;
wc.hInstance=hInst;
wc.lpfnWndProc=(WNDPROC)windowprocessforwindow4;
wc.lpszClassName=L"window class 4";
wc.lpszMenuName=NULL;
wc.style=CS_HREDRAW|CS_VREDRAW;
if(!RegisterClassEx(&wc))
{
int nResult=GetLastError();
MessageBox(NULL,
L"Window class creation failed",
L"Window Class Failed",
MB_ICONERROR);
}
else
windowclass4registeredbefore=true;
}
hwnd=CreateWindowEx(NULL,
wc.lpszClassName,
L"Window 4",
WS_OVERLAPPEDWINDOW,
200,
210,
640,
480,
NULL,
NULL,
hInst,
NULL /* No Window Creation data */
);
if(!hwnd)
{
int nResult=GetLastError();
MessageBox(NULL,
L"Window creation failed",
L"Window Creation Failed",
MB_ICONERROR);
}
ShowWindow(hwnd,nShowCmd);
}
// windows process functions
LRESULT CALLBACK windowprocessforwindow1(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) {
switch(message) {
case WM_CREATE:
window1open=true;
CreateWindowEx(NULL,
L"BUTTON",
L"Open Window 2",
WS_TABSTOP|WS_VISIBLE|
WS_CHILD|BS_DEFPUSHBUTTON,
50,
220,
150,
24,
hwnd,
(HMENU)createwindowbuttoninwindow1,
GetModuleHandle(NULL),
NULL);
break;
case WM_DESTROY:
window1open=false;
break;
case WM_COMMAND:
switch LOWORD(wParam) {
case createwindowbuttoninwindow1:
windowtoopenenum=window2;
break;
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK windowprocessforwindow2(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) {
switch(message) {
case WM_CREATE:
window2open=true;
CreateWindowEx(NULL,
L"BUTTON",
L"Open Window 3",
WS_TABSTOP|WS_VISIBLE|
WS_CHILD|BS_DEFPUSHBUTTON,
50,
220,
150,
24,
hwnd,
(HMENU)createwindowbuttoninwindow2,
GetModuleHandle(NULL),
NULL);
break;
case WM_DESTROY:
window2open=false;
break;
case WM_COMMAND:
switch LOWORD(wParam) {
case createwindowbuttoninwindow2:
windowtoopenenum=window3;
break;
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK windowprocessforwindow3(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) {
switch(message) {
case WM_CREATE:
window3open=true;
CreateWindowEx(NULL,
L"BUTTON",
L"Open Window 4",
WS_TABSTOP|WS_VISIBLE|
WS_CHILD|BS_DEFPUSHBUTTON,
50,
220,
150,
24,
hwnd,
(HMENU)createwindowbuttoninwindow3,
GetModuleHandle(NULL),
NULL);
break;
case WM_DESTROY:
window3open=false;
break;
case WM_COMMAND:
switch LOWORD(wParam) {
case createwindowbuttoninwindow3:
windowtoopenenum=window4;
break;
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK windowprocessforwindow4(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam) {
switch(message) {
case WM_DESTROY:
window4open=false;
break;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
What if you close and reopen a window?
如果您关闭并重新打开一个窗口会怎样?
If you click on the close button and reopen that same window, note the following. When the window is closed after the close button is closed, it will be destroyed. But destroying a window does not destroy the windows class. It only destroys the window from the createwindow function. This makes it necessary for the if statement in the above program that only creates the Windows class if it is the first time the window has been displayed.
如果您单击关闭按钮并重新打开同一窗口,请注意以下几点。当关闭按钮关闭后窗口关闭时,它会被销毁。但是销毁窗口不会破坏 windows 类。它只会从 createwindow 函数中销毁窗口。这使得上面程序中的 if 语句变得必要,该语句仅在第一次显示窗口时创建 Windows 类。
Some side notes
一些旁注
You could create multiple windows using just one windows class. But the problem with that is that you have one windows process function to deal with more than one window. That would work fine in this simple example. But the more heterogeneous the windows are the more would be the need to create a separate windows class for each window.
您可以只使用一个窗口类来创建多个窗口。但问题是你有一个窗口进程函数来处理多个窗口。这在这个简单的例子中会很好地工作。但是,窗口越是异类,就越需要为每个窗口创建一个单独的窗口类。
Also the multiple createwindow functions could have been combined into one function. Note that the only difference between them was the wc.lpszClassName code line. But Windows are likely to be different from each other so combining the functions into one is not necessary-it is more of a preference of just not having code repeating things.
此外,多个 createwindow 函数也可以合并为一个函数。请注意,它们之间的唯一区别是 wc.lpszClassName 代码行。但是 Windows 可能彼此不同,因此没有必要将这些功能合并为一个 - 它更像是一种不让代码重复的偏好。
Further Reading
进一步阅读
The link at the website with the domain functionx has more details about the concepts in windows design. The link is here
网站上带有域 functionx 的链接包含有关 Windows 设计概念的更多详细信息。链接在这里
The home page at functionx.com has good programming learning resources. Especially important is this page which has programming reference material for things such as changing the windows class, creating listboxes and other windows controls. It is also a good resource for win32 programming learning in general. functionx.com win32 programming
functionx.com 的主页上有很好的编程学习资源。尤其重要的是这个页面,其中包含有关更改 windows 类、创建列表框和其他 windows 控件等编程参考资料。总的来说,它也是一个很好的 win32 编程学习资源。functionx.com win32 编程
回答by Robert Harvey
It sounds like you want a Multiple Document Interface. Here is an example of one:
听起来您想要多文档界面。这是一个例子:
回答by InfiniteStack
I know this has already been answered, but I was just writing a program that opens an arbitrary number of windows via a for loop.
我知道这已经得到了回答,但我只是在编写一个程序,通过 for 循环打开任意数量的窗口。
Here is my version. Basically, it reuses the same class generator to build multiple windows. You can create as many as you want. Just make sure to adjust your HWND[] and WNDCLASSEX wc[] arrays accordingly.
这是我的版本。基本上,它重用相同的类生成器来构建多个窗口。您可以根据需要创建任意数量。只需确保相应地调整您的 HWND[] 和 WNDCLASSEX wc[] 数组。
Note 1: This piece of code uses a global ApplicationInstance, which is derived from WinMain function. In your WinMain, assign received hInstance to ApplicationInstance which, in this example is assumed to be available globally. This is your primary application window instance.
注1:这段代码使用了一个全局的ApplicationInstance,它是从WinMain函数派生出来的。在您的 WinMain 中,将接收到的 hInstance 分配给 ApplicationInstance,在本例中假定它是全局可用的。这是您的主要应用程序窗口实例。
Note 2: Of course, you have to have WinProc routine already pre-written, and included somewhere in another header file, or just above (not shown in this example.) In this code, it is referenced to as WinProc, when it's passed to PopulateClass(WNDPROC process)
注意 2:当然,您必须已经预先编写了 WinProc 例程,并将其包含在另一个头文件中的某处,或者就在上面(本示例中未显示。)在此代码中,它被引用为 WinProc,当它被传递时填充类(WNDPROC 过程)
Note 3: SpawnWindow supports "centered" and "maximized" flags. What they do is self-explanatory.
注 3:SpawnWindow 支持“居中”和“最大化”标志。他们所做的是不言自明的。
Also, window class name is auto-generated, so you never have to worry about naming it, just assign it a good base name.
此外,窗口类名是自动生成的,因此您不必担心命名它,只需为其分配一个好的基本名称即可。
int WindowCounter = 0;
WNDCLASSEX wc[1000];
HWND hwnd[1000];
char class_name[256]; // for auto class name generation
void PopulateClass(WNDPROC process) {
ZeroMemory(&wc[WindowCounter], sizeof(WNDCLASSEX));
wc[WindowCounter].cbSize = sizeof(WNDCLASSEX);
wc[WindowCounter].style = CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
wc[WindowCounter].lpfnWndProc = process;
wc[WindowCounter].cbClsExtra = 0;
wc[WindowCounter].cbWndExtra = 0;
wc[WindowCounter].hInstance = ApplicationInstance;
wc[WindowCounter].hIcon = LoadIcon(nullptr, IDI_APPLICATION);
wc[WindowCounter].hCursor = LoadCursor(nullptr, IDC_ARROW);
wc[WindowCounter].hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wc[WindowCounter].lpszMenuName = nullptr;
sprintf(class_name, "WindowClass%d", WindowCounter);
wc[WindowCounter].lpszClassName = class_name;
wc[WindowCounter].hIconSm = nullptr;
}
Now, let's put it all together by providing a SpawnWindow function!
现在,让我们通过提供一个 SpawnWindow 函数把它们放在一起!
HWND SpawnWindow(int x,
int y,
int width,
int height,
bool centered = false,
bool maximized = false) {
PopulateClass(WinProc);
RegisterClassEx(&wc[ WindowCounter ]);
int config_style = WS_OVERLAPPEDWINDOW;
if (maximized) { width = GetSystemMetrics(SM_CXFULLSCREEN); height = GetSystemMetrics(SM_CYFULLSCREEN); config_style = WS_OVERLAPPEDWINDOW | WS_MAXIMIZE; }
if (centered) { x = (GetSystemMetrics(SM_CXFULLSCREEN) / 2) - (width / 2); y = (GetSystemMetrics(SM_CYFULLSCREEN) / 2) - (height / 2); }
hwnd[WindowCounter] = CreateWindowEx(NULL,
wc[WindowCounter].lpszClassName,
config.namever(),
WS_OVERLAPPEDWINDOW,
x,
y,
width,
height,
nullptr,
nullptr,
ApplicationInstance,
nullptr);
HWND returnID = hwnd[WindowCounter];
ShowWindow(hwnd[WindowCounter++], SW_SHOW);
return returnID;
}
Finally, create as many windows as you want with just a single line of code each:
最后,创建任意数量的窗口,每个窗口只需要一行代码:
void CreateWindows() {
HWND PrimaryWindow1 = SpawnWindow(500, 500, 250, 250);
HWND PrimaryWindow2 = SpawnWindow(500, 500, 250, 250, true);
HWND PrimaryWindow3 = SpawnWindow(500, 500, 250, 250, true, true);
HWND PrimaryWindow4 = SpawnWindow(100, 100, 150, 150);
HWND PrimaryWindow5 = SpawnWindow(450, 500, 350, 150);
}
Call CreateWindows() from your WinMain, before entering main loop.
在进入主循环之前,从 WinMain 调用 CreateWindows()。
Hope this helps someone out there.
希望这可以帮助那里的人。
Using a single WinProc for all windows
对所有窗口使用单个 WinProc
Note, this will require additional modification to the above code. In particular, passing a custom class name to the SpawnWindow function, representing each window. For example: "WindowClass_App", where "WindowClass_" can be a static part of the name, and actual identifier would be just: "App", "Toolbox", "Sidebar", "AnotherCustomWindow", etc. First define them:
请注意,这将需要对上述代码进行额外修改。特别是,将自定义类名传递给 SpawnWindow 函数,代表每个窗口。例如:“WindowClass_App”,其中“WindowClass_”可以是名称的静态部分,而实际标识符将只是:“App”、“Toolbox”、“Sidebar”、“AnotherCustomWindow”等。首先定义它们:
#define WindowClass_App 0
#define WindowClass_Layers 2
#define WindowClass_OpenGL 3
/* ...etc... */
I wrote a function that converts a string to an int ID. This ID will be used to branch out within the single WinProc function, based on which window class the message has been received:
我编写了一个将字符串转换为 int ID 的函数。此 ID 将用于在单个 WinProc 函数内进行分支,基于接收到消息的窗口类:
int IdentifyWindowClassID(const char *lpClassName) {
int WindowClassID = -1;
// Convert string names to integers, because C++ switch does not support strings
if (strcmp(lpClassName, "WindowClass_App") == 0) WindowClassID = WindowClass_App;
if (strcmp(lpClassName, "WindowClass_Layers") == 0) WindowClassID = WindowClass_Layers;
if (strcmp(lpClassName, "WindowClass_OpenGL") == 0) WindowClassID = WindowClass_OpenGL;
/* etc */
return WindowClassID;
}
And finally, the WinProc itself:
最后,WinProc 本身:
long __stdcall WinProc(HWND hwnd, unsigned int msg, WPARAM wparam, LPARAM lparam)
{
char lpClassName[128];
GetClassName(hwnd, lpClassName, 128);
int WindowClassID = IdentifyWindowClassID( lpClassName );
switch (WindowClassID)
{
/* 1.) Main application window */
case WindowClass_App: {
switch (msg) {
case WM_CREATE: {
/* ...code... */
}
/* ...code... */
}
/* 2.) Layers window */
case WindowClass_Layers: {
switch (msg) {
case WM_CREATE: {
/* ...code... */
}
/* ...code... */
}
/* 3.) OpenGL view window... */
...
This is the basic pattern. Of course, you can craft it in any way you want, this is just how I did it, it's simple and it works for me.
这是基本模式。当然,你可以用任何你想要的方式来制作它,这就是我所做的,它很简单而且对我有用。
回答by Hernán
You can create as many windows as you want using CreateWindow/CreateWindowEx, with the relationship between them as you desire (owner/child).
您可以使用 CreateWindow/CreateWindowEx 创建任意数量的窗口,并根据需要创建它们之间的关系(所有者/子项)。
You can make a window "owned" by other with:
您可以通过以下方式制作一个由其他人“拥有”的窗口:
SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR) hwndParent);
To convert a window to child, use SetParent
.
要将窗口转换为子窗口,请使用SetParent
.
Note that the SetWindowLongPtr
call with GWLP_HWNDPARENT
does not behave as SetParent (MSDN is wrong on this I think). GWLP_HWNDPARENT
does not convert a window to "child", but to "owned".
请注意,SetWindowLongPtr
调用 with 的GWLP_HWNDPARENT
行为与 SetParent 不同(我认为 MSDN 在这方面是错误的)。GWLP_HWNDPARENT
不会将窗口转换为“子”,而是“拥有”。