后面我们演示了带界面的Windows程序,但那仅仅是一个弹窗,挪用MessageBox函数就可以完成,不是一个真正意义上的窗口。我们平日所说的窗口包括最大化、最小化、封闭按钮,也包括菜单、单选框、图像等各类控件。
一个完好的Windows程序框架:
#includeLRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow){ static TCHAR szClassName[] = TEXT("HelloWin"); //窗口类名 HWND hwnd; //窗口句柄 MSG msg; //音讯 WNDCLASS wndclass; //窗口类 /**********第①步:注册窗口类**********/ //为窗口类的各个字段赋值 wndclass.style = CS_HREDRAW | CS_VREDRAW; //窗口作风 wndclass.lpfnWndProc = WndProc; //窗口进程 wndclass.cbClsExtra = 0; //临时不需求了解 wndclass.cbWndExtra = 0; //临时不需求了解 wndclass.hInstance = hInstance; //以后窗口句柄 wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION); //窗口图标 wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); //鼠标款式 wndclass.hbrBackground= (HBRUSH) GetStockObject (WHITE_BRUSH); //窗口配景画刷 wndclass.lpszMenuName = NULL ; //窗口菜单 wndclass.lpszClassName= szClassName; //窗口类名 //注册窗口 RegisterClass(&wndclass); /*****第②步:创立窗口(并让窗口显示出来)*****/ hwnd = CreateWindow( szClassName, //窗口类的名字 TEXT("Welcome"), //窗口题目(呈现在题目栏) WS_OVERLAPPEDWINDOW, //窗口作风 CW_USEDEFAULT, //初始化时x轴的地位 CW_USEDEFAULT, //初始化时y轴的地位 500, //窗口宽度 300, //窗口高度 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("你好,欢迎离开C言语中文网"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint (hwnd, &ps) ; return 0 ; //窗口烧毁音讯 case WM_DESTROY: PostQuitMessage(0) ; return 0 ; } return DefWindowProc(hwnd, message, wParam, lParam) ; }
运转后果:
关于初学者,这段代码“又臭又长”,难于了解,有点吓人。但这是一个Windows程序的根本框架,只不外不像C言语的框架那么复杂,几行代码搞定。人人不要急于了解每行代码的寄义,大局部代码直接拿来运用就可以。
1) 注册窗口类
在Windows中,挪用 CreateWindow() 函数可以创立一个窗口(请看下面的代码)。窗口有许多属性,比方巨细、地位、题目、配景色彩、鼠标款式、图标等,在创立窗口时都需求指定。这些属性比拟多,超越10个,然则有一局部是通用的,分歧的窗口,它们的值普通相反,Windows将这些通用的属性抽掏出来,用一个构造体表现,就是下面代码中WNDCLASS(window class缩写):
WNDCLASS wndclass; //界说窗口类 //为窗口类的各个字段赋值 wndclass.style = CS_HREDRAW | CS_VREDRAW; //窗口作风 wndclass.lpfnWndProc = WndProc; //窗口进程 wndclass.cbClsExtra = 0; //临时不需求了解 wndclass.cbWndExtra = 0; //临时不需求了解 wndclass.hInstance = hInstance; //以后窗口句柄 wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION); //窗口图标 wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); //鼠标款式 wndclass.hbrBackground= (HBRUSH) GetStockObject (WHITE_BRUSH); //窗口配景画刷 wndclass.lpszMenuName = NULL ; //窗口菜单 wndclass.lpszClassName= szClassName; //窗口类名
这个构造体,我们称之为窗口类。假如你有面向对象的编程经历,那么会很轻易了解,没有的话也没紧要,你可以以为,基于该构造体创立的窗口属于统一个类别,有许多属性是相反的。
留意最初的字段lpszClassName,它指清楚明了以后窗口类的名字,将这个名字传递给 CreateWindow() 函数,就能依据该窗口类来创立窗口。也就是说,今后想运用窗口类,只需晓得它的名字就可以(也就是字段lpszClassName的值)。
窗口类仅仅是一个构造体,假如只是界说了却构体变量,那么在运用时并不克不及经过 lpszClassName 字段的值找到这个构造体。所以还要挪用 RegisterClass() 来注册,让零碎晓得窗口类的名字,下次运用时才干找到。
作为简明教程,我们并不计划深化研讨窗口类的每个字段的寄义,下面是对它们的扼要阐明:
字段 | 阐明 |
---|---|
style | 窗口作风。关于初学者,常用的取值为CS_HREDRAW | CS_VREDRAW,表现当窗口巨细改动时重绘窗口,如许才干包管文字一直处于窗口两头。style还有许多取值,这里纷歧一解说,有兴味的读者可以检查:WNDCLASS中style字段的取值(wndclass.style的取值) |
lpfnWndProc | 窗口处置进程,下面会具体解说。 |
hInstance | 以后窗口句柄。 |
hIcon | 窗口图标。也就是程序运转时在左上角和义务栏看到的图标,需求经过LoadIcon函数加载。 |
hCursor | 鼠标款式。需求经过LoadCursor函数加载。 |
hbrBackground | 窗口配景画刷。也就是窗口配景的填充色彩,前面我们会解说画笔、画刷和画布的概念。 |
lpszMenuName | 窗口菜单。也就是题目栏下方看到的多种多样的菜单,下面的程序没有菜单,所以值为 NULL。 |
lpszClassName | 窗口类的名字。每一个窗口类的名字多是分歧的,以便与其他窗口类辨别。 |
2) 创立窗口
有了窗口类,就可以依据它来创立窗口了。创立窗口运用 CreateWindows() 函数,如下所示:
hwnd = CreateWindow( szClassName, // 窗口类的名字 TEXT("Welcome"), //窗口题目(呈现在题目栏) WS_OVERLAPPEDWINDOW, //窗口作风 CW_USEDEFAULT, //初始化时窗口x轴坐标 CW_USEDEFAULT, //初始化时窗口y轴坐标 500, //窗口宽度 300, //窗口高度 NULL, //父窗口句柄。这里没有父窗口,所认为 NULL NULL, //窗口菜单句柄。以后窗口没有菜单,所认为 NULL hInstance, //以后窗口的句柄,经过 WinMain 函数传入。 NULL //不运用该值 );
几点阐明:
A) CreateWindow 的第一个参数就是窗口类的名字,经过这个名字可以找到方才注册的窗口类,然后再依据它来创立窗口。
B) 显示器上的坐标与数学中的分歧,显示器的左上角是坐标原点,从原点向右是x轴,向下是y轴,多是正坐标,没有正数。如下图所示:
C) 参数 hInstance 是经过主函数 WinMain 传入的。
留意:经过 CreateWindows() 函数创立窗口后,仅仅是为窗口分派了内存空间,取得了句柄,但窗口并没有显示出来,所以还需求挪用 ShowWindow() 函数来显示窗口。
而挪用了 ShowWindow() 函数又仅仅是将窗口显示出来,但不会停止客户区的绘制,所以还需求挪用 UpdateWindow() 函数,生成 VM_PAINT 音讯,将客户区中的各类控件绘制出来,下面会解说。
至此,一个窗口的创立任务就曾经完成了。窗口的各类属性,在窗口类和 CreateWindow() 函数的参数中都停止了阐明。
留意:在窗口类 wndclass 中指定的窗口款式以 CS 扫尾,是通用的;而在 CreateWindow 函数中指定的窗口款式以 WS 扫尾,只对以后窗口无效,概况请检查《 CreateWindow窗口作风取值》。
3) 停止音讯轮回
在 UpdateWindow 函数被挪用之后,新建的窗口在屏幕中就可以显示了。此时,程序必需可以承受用户的键盘或鼠标事情,例如按下回车键、右击鼠标等。
在《与windows编程有关的主要概念》一节中讲到了Windows的音讯机制。Windows 会为每一个使用程序保护一个音讯队列,当有事情发作时,Windows会主动将这些事情转换为“音讯”,并送达到音讯队列。
在我们的程序中,可以经过一段“音讯轮回”代码来从音讯队列中获撤消息:
while( GetMessage(&msg, NULL, 0, 0) ){ TranslateMessage(&msg); //翻译音讯 DispatchMessage (&msg); //分配音讯 }
GetMessage 函数用来从音讯队列中获取一条音讯,并保管到 MSG 构造体变量中。作为简明教程,我们不再具体剖析 getMessage 函数的各个参数,读者依据下面的代码“照猫画虎”就可以,不会影响你后续的进修。
留意:GetMessage 的前往值永远为非零值,while 轮回会不断停止下去。假如队列中没有音讯,GetMessage 函数会等候,直到有音讯进入。
获取到音讯后,需求挪用 TranslateMessage 函数抵消息停止转换(翻译),然后再挪用 DispatchMessage 函数将音讯传给窗口进程行止理(挪用窗口进程)。
4) 窗口进程
所谓窗口进程,就是处置窗口事情的函数,也就是下面代码中最初的 WndProc 函数。GetMessage 每获取到一条音讯,最终都邑丢给窗口进程行止理。
窗口进程有一个参数 message,会传入发作的事情类型,常用的有:
WM_CREATE:窗口被创立。
WM_PAINT:窗口需求更新或重绘。
WM_WM_DESTROY:窗口被烧毁(封闭)。
WM_CREATE 和 WM_DESTROY 很轻易了解,WM_PAINT 将鄙人节中具体解说,它十分主要,不睬解 WM_PAINT 可以说就没有学会Windows编程。
分歧的音讯常常需求停止分歧的处置,所以普通经过 switch case 语句来婚配。
留意:你可以对获取到的音讯停止处置,参加本人的营业逻辑;也可以不处置,让Windows本人看着办(默许处置方法)。窗口进程最初一条语句:
return DefWindowProc(hwnd, message, wParam, lParam) ;
它的感化就是让Windows本人处置使用程序没有处置的音讯,必需要有该语句。
窗口进程在窗口类中指明,然后就不必管了,不需求我们显式挪用。
最初的总结
下面讲到的,是开辟一个Windows使用程序的根本流程,也是Windows使用程序的代码模板,你不需求记住每一个细节,直接套用就可以。
编写Windows使用程序的步调:
注册窗口类
依据窗口类来创立窗口
进入无休止的音讯轮回
编写窗口进程
有了代码模板,剩下的次要任务就是处置各类各样的事情了,也就是在窗口进程中编写代码。