C\C++ Windows程序设计[2]:创建一个窗口
上篇文章讲了如何编写一个弹出对话框的程序,这篇文章就来讲讲如何在C\C++下创建一个窗口。
来简单介绍下 Windows窗口程序 的创建过程:
首先我们需要注册一个窗口类,可以使用RegisterClass函数,接着使用CreateWindow创建一个窗口。下一步需要让窗口在屏幕上面显示出来,可以使用ShowWindow来完成,接着是更新窗口,注意一定不能漏掉这个步骤!这个阔以使用UpdateWindow来完成。接着就是建立消息循环队列啦,如果没有这个,程序就不能响应Windows发来的信息啦。
来康康代码:
#include <windows.h>
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ; //声明用来处理消息的函数
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )
{
static TCHAR szAppName[] = TEXT("MyWindow") ;
HWND hwnd ;
MSG msg ;
WNDCLASS wndclass ; //声明一个窗口类对象
//以下为窗口类对象wndclass的属性
wndclass.style = CS_HREDRAW | CS_VREDRAW ; //窗口样式
wndclass.lpszClassName = szAppName ; //窗口类名
wndclass.lpszMenuName = NULL ; //窗口菜单:无
wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH) ; //窗口背景颜色
wndclass.lpfnWndProc = WndProc ; //窗口处理函数
wndclass.cbWndExtra = 0 ; //窗口实例扩展:无
wndclass.cbClsExtra = 0 ; //窗口类扩展:无
wndclass.hInstance = hInstance ; //窗口实例句柄
wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION ) ; //窗口最小化图标:使用缺省图标
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ) ; //窗口采用箭头光标
if( !RegisterClass( &wndclass ) )
{ //注册窗口类, 如果注册失败弹出错误提示
MessageBox( NULL, TEXT("窗口注册失败"), TEXT("错误"), MB_OK | MB_ICONERROR ) ;
return 0 ;
}
hwnd = CreateWindow( //创建窗口
szAppName, //窗口类名
TEXT("我的第一个Windows窗口"), //窗口标题
WS_OVERLAPPEDWINDOW, //窗口的风格
CW_USEDEFAULT, //窗口初始显示位置x:使用缺省值
CW_USEDEFAULT, //窗口初始显示位置y:使用缺省值
CW_USEDEFAULT, //窗口的宽度:使用缺省值
CW_USEDEFAULT, //窗口的高度:使用缺省值
NULL, //父窗口:无
NULL, //子菜单:无
hInstance, //该窗口应用程序的实例句柄
NULL
) ;
ShowWindow( hwnd, iCmdShow ) ; //显示窗口
UpdateWindow( hwnd ) ; //更新窗口
while( GetMessage( &msg, NULL, 0, 0 ) ) //从消息队列中获取消息
{
TranslateMessage( &msg ) ; //将虚拟键消息转换为字符消息
DispatchMessage( &msg ) ; //分发到回调函数(过程函数)
}
return msg.wParam ;
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
HDC hdc ; //设备环境句柄
PAINTSTRUCT ps ; //绘制结构
RECT rect; //矩形结构
switch( message ) //处理得到的消息
{
case WM_PAINT: //处理窗口区域无效时发来的消息
hdc = BeginPaint( hwnd, &ps ) ;
GetClientRect( hwnd, &rect ) ;
DrawText( hdc, TEXT("Hello World"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER ) ; //文字
EndPaint( hwnd, &ps ) ;
return 0 ;
case WM_DESTROY: //处理窗口关闭时的消息
MessageBox( hwnd, TEXT("关闭程序!"), TEXT("结束"), MB_OK | MB_ICONINFORMATION ) ;
PostQuitMessage( 0 ) ;
return 0;
}
return DefWindowProc( hwnd, message, wParam, lParam ) ; //DefWindowProc处理我们自定义的消息处理函数没有处理到的消息
}
编译(方式同上篇文章),你会看到个Windows窗口,啊恭喜你!你已经编译出了你人生第一个Windows窗口程序(滑稽)。
由上面的代码可知Windows窗口的代码框架:
#include <windows.h>
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ; //声明用来处理消息的函数
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow ) //Windows窗口程序的入口函数
{
//创建窗口、消息循环等
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ){
//处理消息
return DefWindowProc( hwnd, message, wParam, lParam ) ;
}
观察上面的代码,你会发现,哎,main不见了,取而代之的是WinMain,还有一个WndProc!是的你没看错,Windows窗口程序不再使用main作为入口函数了,而是使用WinMain来作为主函数。而且WinMain的参数也变了: ```cpp int WINAPI WinMain( //Windows窗口程序入口函数 HINSTANCE hInstance, //实例句柄 HINSTANCE hPrevInstance, //无用参数,总是为NULL(空,即“0”) PSTR szCmdLine, //传入的命令 int iCmdShow //窗口显示类型 ) ```
再仔细观察,你会发现,WinMain旁边还有个WINAPI,这是什么意思呢?其实,WINAPI是个在windef.h里面定义的标识符: ```cpp #define WINAPI __stdcall ``` 这条语句规定了一种函数调用约定,表明如何生成在堆栈中放置函数调用参数的机器代码,绝大部分 Windows 函数调用都定义成 WINAPI。
Windows窗口程序工作原理
运行刚才编译的程序,你会发现不管怎么拖动、缩放来改变 程序 的大小,那段Hello World的文字永远是在屏幕正中央,这是怎么做到的呢?这就涉及到Windows窗口程序的工作原理了。 其实,用户调整窗口大小所产生的琐碎代码都是 Windows 实现的而并非应用程序,那么应用程序如何知道自己的大小以及被改变了?每当用户进行拖动窗口或者点击窗口之类的操作时,Windows都会向程序发送一条消息,这就是上面代码中的WndProc函数的作用。每当Windows向应用程序发送一条消息,都会触发WndProc函数,现在来康康WndProc的参数:
LRESULT CALLBACK WndProc( //处理Windows发送来的消息
HWND hwnd, //窗口句柄(后面会讲到)
UINT message, //Windows发来的消息
WPARAM wParam, //传入的参数,暂时不需要理解
LPARAM lParam //传入的参数,暂时不需要理解
)
显然,WndProc是对message这个参数进行处理。
现在来一个个解析调用到的Windows API:
函数 | 意义 |
---|---|
LoadIcon | 加载图标 |
LoadCursor | 加载鼠标光标 |
GetStockObject | 获取图形对象 |
RegisterClass | 注册一个窗口类 |
MessageBox | 弹出对话框 |
CreateWindow | 创建一个窗口 |
ShowWindow | 显示窗口 |
UpdateWindow | 更新窗口 |
GetMessage | 从消息队列中获取消息 |
TranslateMessage | 转换消息 |
DispatchMessage | 将消息分发到回调函数处理 |
BeginDraw | 开始绘图 |
GetClientRect | 获取窗口大小 |
DrawText | 绘制文字 |
EndPaint | 结束绘制 |
PostQuitMessage | 插入"退出"消息到消息队列 |
DefWindowProc | 执行默认的消息处理 |