windows程序设计 c语言,【教程】基于C语言的Windows编程入门

(三) 窗口的创建与消息处理

原来那个程序是不是感觉有点简单?这里我们要制作一个复杂一点的Windows程序 - 显示一个空白窗口。

出于稳定性考量,Visual C++编译器中int、long等基本类型基本上没有变化(16->32仅int变化了一次,64位没有变化),但是指针则16位、32位、64位一直在变化,为了更好地适应指针的变化,以及方便地与指针相互转换,Windows中定义了平台相关整数,它们随指针长度而变化:

INT_PTR 和 UINT_PTR - 16/32/64位平台相关整数,前者有符号,后者无符号

LONG_PTR 和 ULONG_PTR - 32/32/64位平台相关整数,前者有符号,后者无符号

这四个类型在以后的编程中要经常用到,务必明白它们的意思。

关于调用约定:调用约定表示调用函数时参数的传递方式。在Windows中绝大多数WinAPI都是__stdcall,只有wsprintf等参数数量可变的函数是__cdecl,自己定义的各种Win32函数也最好采用__stdcall。关于调用约定的内部信息请参阅相关资料,这里不详述。

一、准备一个窗口消息处理函数

Windows中窗口事件的处理是靠窗口消息来完成的,为了处理窗口消息,每一个窗口都至少对应一个窗口消息处理函数,窗口消息处理函数的定义如下:

<code class="lang-cpp">LONG_PTR __stdcall WndProc(void* hWnd, unsigned int msg, UINT_PTR wp, LONG_PTR lp)

{

&nbsp;&nbsp;&nbsp;&nbsp;return DefWindowProc(hWnd, msg, wp, lp);

}</code>

程序在处理完消息之后,return 0;直接返回,否则应该调用DefWindowProc来处理不想处理的消息。

常用的消息如下:

WM_CREATE - 窗口创建时被调用

WM_DESTROY - 窗口销毁时被调用

WM_PAINT - 窗口绘制时被调用

WM_COMMAND - 控件或窗口有消息时被调用

WM_SIZE - 窗口大小改变时被调用

为了在同一个函数中处理不同消息,我们一般使用switch (msg) { case WM_XXX: ... }分支来编写WndProc函数:

<code class="lang-cpp">// 窗口消息处理函数 - WndProc

LONG_PTR __stdcall WndProc(void* hWnd, unsigned int msg, UINT_PTR wp, LONG_PTR lp)

{

&nbsp;&nbsp;&nbsp;&nbsp;switch (msg) // 判断窗口消息的种类

&nbsp;&nbsp;&nbsp;&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp;case WM_CREATE: // 窗口创建

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// ...

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;

&nbsp;&nbsp;&nbsp;&nbsp;case WM_DESTROY: // 窗口销毁

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// ...

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;

&nbsp;&nbsp;&nbsp;&nbsp;// case ...

&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;return DefWindowProc(hWnd, msg, wp, lp);

}</code>

在这里,我们只需处理WM_DESTROY即可:

<code class="lang-cpp">// 窗口消息处理函数 - WndProc

LONG_PTR __stdcall WndProc(void* hWnd, unsigned int msg, UINT_PTR wp, LONG_PTR lp)

{

&nbsp;&nbsp;&nbsp;&nbsp;switch (msg) // 判断窗口消息的种类

&nbsp;&nbsp;&nbsp;&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp;case WM_DESTROY: // 窗口销毁

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// ...

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;

&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;return DefWindowProc(hWnd, msg, wp, lp);

}</code>

二、全局变量

我们需要两个全局变量保存模块和主窗口的句柄:

<code class="lang-cpp">void* hInst; // 模块句柄

void* hMainWnd; // 主窗口句柄</code>

在wWinMain中,第一个参数提供了模块句柄,现在保存到全局变量:

<code class="lang-cpp">// 入口点 - wWinMain

int __stdcall wWinMain(void* hInstance, void* reserved, wchar_t* cmdline, int nshow)

{

&nbsp;&nbsp;&nbsp;&nbsp;hInst = hInstance; // 保存模块句柄

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;// ... 注册窗口类 建立窗口 显示和更新窗口 消息循环 ...

}</code>

三、注册窗口类

在Windows中,窗口的部分特性需要与窗口类(WNDCLASS)进行自定义。WNDCLASS是一个C语言结构体,我们需要填写它们的内容。

WNDCLASS {

样式 函数(样式=窗口类样式,常用的有CS_HREDRAW和CS_VREDRAW;函数=窗口处理函数)

类增 窗增(高级参数,表示窗口类和每个窗口附加数据的字节数,一般填0)

模块 图标 光标(模块=模块句柄;图标=左上角图标;光标=常说的鼠标指针)

背景 菜单 类名(背景=背景画刷;菜单=菜单资源标识,字符串或小于65536的整数;类名=窗口类的名称,随便写)

}

在这里如下填写,这些内容一般情况下是比较固定的:

<code class="lang-cpp">// 窗口类设定

// 样式、函数

// 类增、窗增&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(高级参数)

// 模块、图标、光标&nbsp;&nbsp;&nbsp;&nbsp; (光标=鼠标指针)

// 背景、菜单、类名

WNDCLASS wc = {

&nbsp;&nbsp;&nbsp;&nbsp;CS_HREDRAW | CS_VREDRAW, // 样式:横向、纵向改变大小时重绘

&nbsp;&nbsp;&nbsp;&nbsp;WndProc, // 窗口消息处理函数

&nbsp;&nbsp;&nbsp;&nbsp;0, 0,

&nbsp;&nbsp;&nbsp;&nbsp;hInst, // 模块句柄

&nbsp;&nbsp;&nbsp;&nbsp;LoadIcon(NULL, IDI_APPLICATION), // 图标:默认图标

&nbsp;&nbsp;&nbsp;&nbsp;LoadCursor(NULL, IDC_ARROW), // 光标:箭头

&nbsp;&nbsp;&nbsp;&nbsp;GetStockObject(WHITE_BRUSH), // 背景画刷:白色

&nbsp;&nbsp;&nbsp;&nbsp;NULL, // 菜单:无

&nbsp;&nbsp;&nbsp;&nbsp;L"MyWindow", // 窗口类名称

};</code>

填写完这个类之后,我们可以调用RegisterClass函数注册这个窗口类:

<code class="lang-cpp">if (!RegisterClass(&amp;wc)) // 注册窗口类

&nbsp;&nbsp;&nbsp;&nbsp;return 1;</code>

四、新建窗口

新建窗口需要使用CreateWindowEx函数,这个函数有12个参数,看似比较多,其实也不难记:

CreateWindowEx(

扩展样式, 窗口类名, 窗口标题, 窗口样式, // 窗口样式常用WS_OVERLAPPEDWINDOW

左, 上, 宽, 高, // 不想填写可以使用CW_USEDEFAULT

父窗句柄, 菜单句柄, 模块句柄, 可选参数

)

不是所有参数都是必要的,部分参数可以填0或NULL。

在这里,我们这样建立一个标准的窗口:

<code class="lang-cpp">// 创建主窗口 - CreateWindowEx

// 扩展样式、窗口类名、窗口标题、窗口样式(样式=覆盖式窗口)

// 左、上、宽、高

// 父窗句柄、菜单句柄、模块句柄、可选参数

hMainWnd = CreateWindowEx(

&nbsp;&nbsp;&nbsp;&nbsp;0, L"MyWindow", L"这是主窗口", WS_OVERLAPPEDWINDOW,

&nbsp;&nbsp;&nbsp;&nbsp;CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

&nbsp;&nbsp;&nbsp;&nbsp;NULL, NULL, hInst, NULL);</code>

建立窗口以后,我们一般需要显示并更新窗口,这一步比较简单:

<code class="lang-cpp">ShowWindow(hMainWnd, nshow); // 显示窗口

UpdateWindow(hMainWnd); // 更新窗口</code>

五、进入消息循环

窗口显示以后,我们的程序并没有就此停住并继续接收窗口消息,因此我们要在wWinMain中不停地等待消息,并让系统进行处理。

其中GetMessage等待并获取消息,TranslateMessage将虚拟键码转换为Unicode字符,DispatchMessage将消息分发到处理函数

代码如下:

<code class="lang-cpp">MSG msg;

int rslt;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

// 消息循环 - 等待和处理各种消息

while (rslt = GetMessage(&amp;msg, NULL, 0, 0)) // 获取消息

{

&nbsp;&nbsp;&nbsp;&nbsp;if (rslt == -1) // 错误则退出

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 1;

&nbsp;&nbsp;&nbsp;&nbsp;TranslateMessage(&amp;msg); // 将虚拟键码转换为Unicode字符

&nbsp;&nbsp;&nbsp;&nbsp;DispatchMessage(&amp;msg); // 分发消息到窗口消息处理函数

}</code>

六、退出消息循环

接收到一般消息时,GetMessage会返回TRUE,这时消息循环继续运行。当接收到WM_QUIT消息时,GetMessage会返回FALSE,程序将退出。退出代码保存在msg.wParam中:

<code class="lang-cpp">return (int)msg.wParam;</code>

在窗口被销毁时,我们期望消息循环退出,因此要向消息循环发送WM_QUIT消息,方法是调用PostQuitMessage(n)函数,n为退出码。在WndProc中添加以下代码:

<code class="lang-cpp">// 窗口消息处理函数 - WndProc

LONG_PTR __stdcall WndProc(void* hWnd, unsigned int msg, UINT_PTR wp, LONG_PTR lp)

{

&nbsp;&nbsp;&nbsp;&nbsp;switch (msg) // 判断窗口消息的种类

&nbsp;&nbsp;&nbsp;&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp;case WM_DESTROY: // 窗口销毁

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PostQuitMessage(0); // 发送WM_QUIT,退出消息循环

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;

&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;return DefWindowProc(hWnd, msg, wp, lp);

}</code>七、运行效果

f740ba37c394d6018b076e85580773c5.png

八、完整的程序

<code class="lang-cpp">// 不严格区分句柄,均定义为void*

#define NO_STRICT

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

// 包含WinAPI定义

#include &lt;windows.h&gt;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

// 平台相关的整数类型:

// INT_PTR 16 -&gt; 32 -&gt; 64

// UINT_PTR 16 -&gt; 32 -&gt; 64

// LONG_PTR 32 -&gt; 32 -&gt; 64

// ULONG_PTR 32 -&gt; 32 -&gt; 64

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

void* hInst; // 模块句柄

void* hMainWnd; // 主窗口句柄

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

// 窗口消息处理函数 - WndProc

LONG_PTR __stdcall WndProc(void* hWnd, unsigned int msg, UINT_PTR wp, LONG_PTR lp)

{

&nbsp;&nbsp;&nbsp;&nbsp;switch (msg) // 判断窗口消息的种类

&nbsp;&nbsp;&nbsp;&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp;case WM_DESTROY: // 窗口被销毁时

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PostQuitMessage(0); // 发送WM_QUIT,退出消息循环

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;

&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;// 如果消息未经处理,则由DefWindowProc处理

&nbsp;&nbsp;&nbsp;&nbsp;return DefWindowProc(hWnd, msg, wp, lp);

}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

// 入口点 - wWinMain

int __stdcall wWinMain(void* hInstance, void* reserved, wchar_t* cmdline, int nshow)

{

&nbsp;&nbsp;&nbsp;&nbsp;hInst = hInstance; // 保存模块句柄

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;// 窗口类设定

&nbsp;&nbsp;&nbsp;&nbsp;// 样式、函数

&nbsp;&nbsp;&nbsp;&nbsp;// 类增、窗增&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(高级参数)

&nbsp;&nbsp;&nbsp;&nbsp;// 模块、图标、光标&nbsp;&nbsp;&nbsp;&nbsp; (光标=鼠标指针)

&nbsp;&nbsp;&nbsp;&nbsp;// 背景、菜单、类名

&nbsp;&nbsp;&nbsp;&nbsp;WNDCLASS wc = {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CS_HREDRAW | CS_VREDRAW, // 样式:横向、纵向改变大小时重绘

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WndProc, // 窗口消息处理函数

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0, 0,

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hInst, // 模块句柄

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LoadIcon(NULL, IDI_APPLICATION), // 图标:默认图标

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LoadCursor(NULL, IDC_ARROW), // 光标:箭头

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GetStockObject(WHITE_BRUSH), // 背景画刷:白色

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL, // 菜单:无

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;L"MyWindow", // 窗口类名称

&nbsp;&nbsp;&nbsp;&nbsp;};

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;if (!RegisterClass(&amp;wc)) // 注册窗口类

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 1;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;// 创建主窗口 - CreateWindowEx

&nbsp;&nbsp;&nbsp;&nbsp;// 扩展样式、窗口类名、窗口标题、窗口样式(样式=覆盖式窗口)

&nbsp;&nbsp;&nbsp;&nbsp;// 左、上、宽、高

&nbsp;&nbsp;&nbsp;&nbsp;// 父窗句柄、菜单句柄、模块句柄、可选参数

&nbsp;&nbsp;&nbsp;&nbsp;hMainWnd = CreateWindowEx(

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0, L"MyWindow", L"这是主窗口", WS_OVERLAPPEDWINDOW,

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;NULL, NULL, hInst, NULL);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;ShowWindow(hMainWnd, nshow); // 显示窗口

&nbsp;&nbsp;&nbsp;&nbsp;UpdateWindow(hMainWnd); // 更新窗口

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;MSG msg;

&nbsp;&nbsp;&nbsp;&nbsp;int rslt;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;// 消息循环 - 等待和处理各种消息

&nbsp;&nbsp;&nbsp;&nbsp;while (rslt = GetMessage(&amp;msg, NULL, 0, 0)) // 获取消息

&nbsp;&nbsp;&nbsp;&nbsp;{

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (rslt == -1) // 错误则退出

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 1;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TranslateMessage(&amp;msg); // 将虚拟键码转换为Unicode字符

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DispatchMessage(&amp;msg); // 分发消息到窗口消息处理函数

&nbsp;&nbsp;&nbsp;&nbsp;}

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;return (int)msg.wParam;

}&lt;/windows.h&gt;</code>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值