25win32编程基础——子窗口消息循环
本节我们学习一下子窗口的消息循环的过程:
首先我们创建1个win32的windows应用程序

然后利用上篇的代码先创建1个窗口
// 21C++SonOfWindowLoop.cpp : 定义应用程序的入口点。
//
#include "stdafx.h"
#include "21C++SonOfWindowLoop.h"
#include "Output.h"
#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst; ; // 当前实例
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
hInst= hInstance;
// TODO: 在此放置代码。
//创建我们自己的窗口类
WNDCLASS wc={0};//使用前必须初始化
TCHAR WClassName[] =TEXT("My Class");
wc.lpszClassName = WClassName;
wc.hInstance = hInst;
wc.lpfnWndProc = WndProc;
wc.hbrBackground = (HBRUSH)COLOR_GRAYTEXT;
//在系统里面注册
RegisterClass(&wc);
//创建窗体
HWND hnd=CreateWindow(
WClassName,
TEXT("我的窗口"),
WS_OVERLAPPEDWINDOW,
10,
10,
600,
400,
NULL,
NULL,
hInst,
NULL
);
//ShowWindow(hnd,SW_SHOW|SW_SHOWNORMAL);
ShowWindow(hnd,SW_SHOW);
MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, 0, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
OutputDebugStringQ(L"%d\n",GetLastError());
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
然后加1个子窗口
// 21C++SonOfWindowLoop.cpp : 定义应用程序的入口点。
//
#include "stdafx.h"
#include "21C++SonOfWindowLoop.h"
#include "Output.h"
#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst; ; // 当前实例
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateButton(HWND hnd);
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
hInst= hInstance;
// TODO: 在此放置代码。
//创建我们自己的窗口类
WNDCLASS wc={0};//使用前必须初始化
TCHAR WClassName[] =TEXT("My Class");
wc.lpszClassName = WClassName;
wc.hInstance = hInst;
wc.lpfnWndProc = WndProc;
wc.hbrBackground = (HBRUSH)COLOR_GRAYTEXT;
//在系统里面注册
RegisterClass(&wc);
//创建窗体
HWND hnd=CreateWindow(
WClassName,
TEXT("我的窗口"),
WS_OVERLAPPEDWINDOW,
10,
10,
600,
400,
NULL,
NULL,
hInst,
NULL
);
CreateButton(hnd);
//ShowWindow(hnd,SW_SHOW|SW_SHOWNORMAL);
ShowWindow(hnd,SW_SHOW);
MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, 0, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
OutputDebugStringQ(L"%d\n",GetLastError());
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return 0;
}
void CreateButton(HWND hnd){
//创建窗体
HWND hBtnd=CreateWindow(
TEXT("Button"), //1
TEXT("按钮"),
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON|BS_DEFPUSHBUTTON,
10,
10,
80,
20,
hnd,
(HMENU)1001, //当创建的为WS_CHID的时候,这个参数就成为子窗口编号
hInst,
NULL
);
HWND hftnd=CreateWindow(
TEXT("Button"), //1
TEXT("复选框"),
WS_CHILD|WS_VISIBLE|BS_CHECKBOX|BS_AUTOCHECKBOX,
10,
40,
80,
20,
hnd,
(HMENU)1002, //2
hInst,
NULL
);
HWND hDtnd=CreateWindow(
TEXT("Button"), //1
TEXT("单选框"),
WS_CHILD|WS_VISIBLE|BS_RADIOBUTTON|BS_AUTORADIOBUTTON,
10,
80,
80,
20,
hnd,
(HMENU)1003, //2
hInst,
NULL
);
};
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
从以上代码可以看出:
1、在windows中所有控件都是窗口
2、没有创建Button类,却可以直接使用,是因为这个Button类是windows提前给我们定义好的,定义方式类似于我们前边那样定义的,因此可以直接使用。
3、当使用CreateWindow创建子窗口WS_CHILD的类型的时候,菜单参数就成为这个子窗口的ID了。
问题1:
当windows为我们定义好了窗口类,我们虽然可以拿来使用,但是像之前的那样在窗口类中定义 回调函数和菜单等操作相当于系统也为我们做好了,
我们可以通过2个API找到windows为我们定义好的类名和回调函数。
//创建窗体
HWND hBtnd=CreateWindow(
TEXT("Button"), //1
TEXT("按钮"),
WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON|BS_DEFPUSHBUTTON,
10,
10,
80,
20,
hnd,
(HMENU)1001, //2
hInst,
NULL
);
TCHAR cName[20];
GetClassName(hBtnd,cName,20);
WNDCLASS Bt1cs;
GetClassInfo(hInst,cName,&Bt1cs);
OutputDebugStringQ(L"%s\n",Bt1cs.lpszClassName);
OutputDebugStringF("%04x\n",Bt1cs.lpfnWndProc);
查看输出可以看到

类名是:Button
回调函数地址是:0x7700e6b0
那么问题2就产生了,当我们点击这个按钮之后,我们想添加一些按钮的处理方法,那我们应该加载哪里呢,因为回调函数系统已经为我们做好了,我们应该怎么办呢?
先理解一下子窗口的消息传递过程:

因此父窗口的回调函数应该这样写:
//系统不停的调用回调函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);//取低2字节
wmEvent = HIWORD(wParam);//取高2字节
// 分析菜单选择:
switch (wmId)
{
case 1001:
OutputDebugStringF("按下%d\n",wmId);
break;
case 1002:
OutputDebugStringF("按下%d\n",wmId);
break;
case 1003:
OutputDebugStringF("按下%d\n",wmId);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_LBUTTONDOWN:
OutputDebugStringF("按下左键\n");
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

浙公网安备 33010602011771号