步骤:
- 窗口类的设计
- 窗口类的注册
- 窗口的正式创建
- 窗口的显示与更新
1. 设计:使用WNDCLASSEX结构体,这里注意的是C++中的结构体中的成员默认是共有的,所以可以直接通过 . 来调用。
typedef <span>struct</span><span> tagWNDCLASSEX {
UINT cbSize; </span><span>//</span><span>UINT类型的cbSize,表示该结构体的字节数大小</span>
UINT style; <span>//</span><span>指定窗口的风格样式</span>
WNDPROC lpfnWndProc; <span>//</span><span>指向窗口过程函数的函数指针 </span><span>int</span> cbClsExtra; <span>//</span><span>窗口类附加内存</span><span>int</span> cbWndExtra; <span>//</span><span>窗口的附加内存</span>
HINSTANCE hInstance; <span>//</span><span>包含窗口过程的实例句柄</span>
HICON hIcon; <span>//</span><span>指定图标句柄</span>
HCURSOR hCursor; <span>//</span><span>窗口的光标句柄</span>
HBRUSH hbrBackground; <span>//</span><span>背景画刷句柄</span>
LPCTSTR lpszMenuName; <span>//</span><span>指定菜单资源的名字</span>
LPCTSTR lpszClassName; <span>//</span><span>指定窗口类的名字</span>
HICON hIconSm; <span>//</span><span>指定窗口类的小图标句柄,一般不用</span>
} WNDCLASSEX, *PWNDCLASSEX;
关于这个结构体的具体说明见msdn文档:https://msdn.microsoft.com/zh-CN/library/windows/desktop/ms633577(v=vs.85).aspx
关于windows中的基本数据类型的说明见:https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx和Windows编程 Win32API中常见的数据类型
来个例子:
<span> 1</span>
<span>//</span>
<span>【1】窗口创建四步曲之一:开始设计一个完整的窗口类</span>
<span> 2</span> WNDCLASSEX wndClass = { <span>0</span> }; <span>//</span><span>用WINDCLASSEX定义了一个窗口类,{0}用来初始化结构体</span><span> 3</span> wndClass.cbSize = <span>sizeof</span>( WNDCLASSEX ) ; <span>//</span><span>设置结构体的字节数大小,一般取sizeof(WNDCLASSEX)就好</span><span> 4</span> wndClass.style = CS_HREDRAW | CS_VREDRAW; <span>//</span><span>设置窗口的样式,常用的取值及其意义可参考msdn文档</span><span> 5</span> wndClass.lpfnWndProc = WndProc; <span>//</span><span>设置指向窗口过程函数的指针</span><span> 6</span> wndClass.cbClsExtra = <span>0</span>; <span>//</span><span>窗口类的附加内存,取0就可以了</span><span> 7</span> wndClass.cbWndExtra = <span>0</span>; <span>//</span><span>窗口的附加内存,依然取0就行了</span><span> 8</span> wndClass.hInstance = hInstance; <span>//</span><span>指定包含窗口过程的程序的实例句柄。</span><span> 9</span> wndClass.hIcon=(HICON)::LoadImage(NULL,L<span>"</span><span>icon.ico</span><span>"</span>,IMAGE_ICON,<span>0</span>,<span>0</span>,LR_DEFAULTSIZE|LR_LOADFROMFILE); <span>//</span><span>本地加载自定义ico图标</span><span>10</span> wndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); <span>//</span><span>指定窗口类的光标句柄。</span><span>11</span> wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); <span>//</span><span>为hbrBackground成员指定一个灰色画刷句柄 </span><span>12</span> wndClass.lpszMenuName = NULL; <span>//</span><span>用一个以空终止的字符串,指定菜单资源的名字。</span><span>13</span> wndClass.lpszClassName = L<span>"</span><span>ForTheDreamOfGameDevelop</span><span>"</span>; <span>//</span><span>用一个以空终止的字符串,指定窗口类的名字。</span>
关于窗口的过程函数:
首先要明白的是lpfnWndProc是 WNDPROC类型的,一个函数指针(msdn中的说明),指向窗口过程函数。窗口过程函数是一个回调函数。针对windows的消息处理机制,窗口过程函数被调用过程是这样的:
2. 注册
调用RegisterClassEx函数对其进行注册,注册成功后才可以创建该类型的窗口。RegisterClassEx(&wndClass)
3. 窗口的正式创建
首先可以调用AdjustWindowRect()函数来根据我们设定的尺寸和风格来计算窗口的尺寸。设计好窗口类并将其注册成功后,就可以用CreateWindow函数来创建设计好的这种类型的窗口了。
<span>//</span>
<span>【3】窗口创建四步曲之三:正式创建窗口</span>
HWND hwnd = CreateWindow( L<span>"</span><span>ForTheDreamOfGameDevelop</span><span>"</span>,WINDOW_TITLE, <span>//</span><span>喜闻乐见的创建窗口函数CreateWindow</span><span> WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );</span>
4. 窗口的显示和更新
主要是用到三个函数:用于设定窗口和显示位置的MoveWindow函数,用于显示窗口的ShowWindow函数,用于更新窗口的UpdateWindow函数
5. 消息循环体系
在经过窗口创建的四步之后,我们还需要编写一个消息循环,不断地从消息队列中取出消息,并且进行响应。有两个函数可以选择:GetMessage和PeekMessage
在游戏编写过程中,更多用的是PeekMessage函数。因为PeekMessage在程序消息队列无论是否有消息时,PeekMessage都立即返回而GetMessage会等待消息队列中有消息时才返回
6. 窗口过程函数
主要用于处理发送给窗口的消息。
7. 窗口类的注销
使用UnregisterClass函数
8. 一个完整的例子
<span> 1</span> #include <windows.h>
<span> 2</span><span>//</span><span> 描述:定义一些辅助宏
</span><span> 3</span><span>//</span><span>------------------------------------------------------------------------------------------------</span><span> 4</span><span>#define</span> WINDOW_WIDTH 800 <span>//</span><span>为窗口宽度定义的宏,以方便在此处修改窗口宽度</span><span> 5</span><span>#define</span> WINDOW_HEIGHT 600 <span>//</span><span>为窗口高度定义的宏,以方便在此处修改窗口高度</span><span> 6</span><span>#define</span> WINDOW_TITLE L"【致我们永不熄灭的游戏开发梦想】程序核心框架" <span>//</span><span>为窗口标题定义的宏</span><span> 7</span><span> 8</span><span> 9</span><span>//</span><span>-----------------------------------【全局函数声明部分】-------------------------------------
</span><span> 10</span><span>//</span><span> 描述:全局函数声明,防止“未声明的标识”系列错误
</span><span> 11</span><span>//</span><span>------------------------------------------------------------------------------------------------</span><span> 12</span><span>/*</span><span>*
</span><span> 13</span><span>LRESULT:本质类型是long,4个字节长度,含义是有符号的消息处理结果,定义过程:_W64 long→LONG_PTR→LRESULT
</span><span> 14</span><span>CALLBACK: _stdcall调用,这里起名为CALLBACK表示是一个回调函数
</span><span> 15</span><span>HWND hwnd:HWND类型的hwnd,表示需要处理消息的窗口句柄,
</span><span> 16</span><span>WPARAM类型的wParam和lParam,用于表示消息的附加信息,lParam本质是long类型,用于表示消息的参数
</span><span> 17</span><span>*/</span><span> 18</span> LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ); <span>//</span><span>窗口过程函数
</span><span> 19</span><span> 20</span><span> 21</span><span>//</span><span>-----------------------------------【WinMain( )函数】--------------------------------------
</span><span> 22</span><span>//</span><span> 描述:Windows应用程序的入口函数,我们的程序从这里开始
</span><span> 23</span><span>//</span><span>------------------------------------------------------------------------------------------------</span><span> 24</span><span> 25</span><span>int</span> WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, <span>int</span><span> nShowCmd)
</span><span> 26</span><span>{
</span><span> 27</span><span>//</span><span>【1】窗口创建四步曲之一:开始设计一个完整的窗口类</span><span> 28</span> WNDCLASSEX wndClass = { <span>0</span> }; <span>//</span><span>用WINDCLASSEX定义了一个窗口类,{0}用来初始化结构体</span><span> 29</span> wndClass.cbSize = <span>sizeof</span>( WNDCLASSEX ) ; <span>//</span><span>设置结构体的字节数大小</span><span> 30</span> wndClass.style = CS_HREDRAW | CS_VREDRAW; <span>//</span><span>设置窗口的样式</span><span> 31</span> wndClass.lpfnWndProc = WndProc; <span>//</span><span>设置指向窗口过程函数的指针</span><span> 32</span> wndClass.cbClsExtra = <span>0</span>; <span>//</span><span>窗口类的附加内存,取0就可以了</span><span> 33</span> wndClass.cbWndExtra = <span>0</span>; <span>//</span><span>窗口的附加内存,依然取0就行了</span><span> 34</span> wndClass.hInstance = hInstance; <span>//</span><span>指定包含窗口过程的程序的实例句柄。</span><span> 35</span> wndClass.hIcon=(HICON)::LoadImage(NULL,L<span>"</span><span>icon.ico</span><span>"</span>,IMAGE_ICON,<span>0</span>,<span>0</span>,LR_DEFAULTSIZE|LR_LOADFROMFILE); <span>//</span><span>本地加载自定义ico图标</span><span> 36</span> wndClass.hCursor = LoadCursor( NULL, IDC_ARROW ); <span>//</span><span>指定窗口类的光标句柄。</span><span> 37</span> wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); <span>//</span><span>为hbrBackground成员指定一个灰色画刷句柄 </span><span> 38</span> wndClass.lpszMenuName = NULL; <span>//</span><span>用一个以空终止的字符串,指定菜单资源的名字。</span><span> 39</span> wndClass.lpszClassName = L<span>"</span><span>ForTheDreamOfGameDevelop</span><span>"</span>; <span>//</span><span>用一个以空终止的字符串,指定窗口类的名字。
</span><span> 40</span><span> 41</span><span>//</span><span>【2】窗口创建四步曲之二:注册窗口类</span><span> 42</span><span>if</span>( !RegisterClassEx( &wndClass ) ) <span>//</span><span>设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口</span><span> 43</span><span>return</span> -<span>1</span><span>;
</span><span> 44</span><span> 45</span><span>//</span><span>【3】窗口创建四步曲之三:正式创建窗口</span><span> 46</span><span>/*</span><span>*
</span><span> 47</span><span> 参数1:是窗口类的名称,对应wndClass.lpszClassName
</span><span> 48</span><span> 参数2:窗口左上角标题栏的名字
</span><span> 49</span><span> 参数3:指定窗口样式;参数4:指定窗口的水平位置
</span><span> 50</span><span> 参数5:竖直位置;参数6,7:宽度,高度;参数8:指定被创建窗口的父窗口句柄,一般设为NULL
</span><span> 51</span><span> 参数9:指定窗口菜单的资源句柄;参数10:HINSTANCE类型,用于指定窗口所属的应用程序实例句柄,也就是应用程序的实例ID
</span><span> 52</span><span> 参数11:一般设为NULL
</span><span> 53</span><span>*/</span><span> 54</span> HWND hwnd = CreateWindow( L<span>"</span><span>ForTheDreamOfGameDevelop</span><span>"</span>,WINDOW_TITLE, <span>//</span><span>喜闻乐见的创建窗口函数CreateWindow</span><span> 55</span><span> WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH,
</span><span> 56</span><span> WINDOW_HEIGHT, NULL, NULL, hInstance, NULL );
</span><span> 57</span><span> 58</span><span>//</span><span>【4】窗口创建四步曲之四:窗口的移动、显示与更新</span><span> 59</span> MoveWindow(hwnd,<span>250</span>,<span>80</span>,WINDOW_WIDTH,WINDOW_HEIGHT,<span>true</span>); <span>//</span><span>调整窗口显示时的位置,使窗口左上角位于(250,80)处</span><span> 60</span> ShowWindow( hwnd, nShowCmd ); <span>//</span><span>调用ShowWindow函数来显示窗口,第二个参数用于指定窗口的显示状态</span><span> 61</span> UpdateWindow(hwnd); <span>//</span><span>对窗口进行更新,就像我们买了新房子要装修一样
</span><span> 62</span><span> 63</span><span>//</span><span>【5】消息循环过程</span><span> 64</span> MSG msg = { <span>0</span> }; <span>//</span><span>定义并初始化msg</span><span> 65</span><span>while</span>( msg.message != WM_QUIT ) <span>//</span><span>使用while循环,如果消息不是WM_QUIT消息,就继续循环</span><span> 66</span><span> {
</span><span> 67</span><span>/*</span><span>*
</span><span> 68</span><span> *参数1:LPMSG类型,指向消息结构体。从消息队列中取出的消息信息将保存在该结构体中
</span><span> 69</span><span> *参数2:指定接收属于哪一个窗口的消息,设为NULL表示用于接收属于调用线程的所有窗口的窗口消息、
</span><span> 70</span><span> *参数3,4:指定要获取消息的最小值和最大值,都设为0表示接收所有消息
</span><span> 71</span><span> *参数5:指定取出消息后是否从消息队列中移除
</span><span> 72</span><span>*/</span><span> 73</span><span>if</span>( PeekMessage( &msg, <span>0</span>, <span>0</span>, <span>0</span>, PM_REMOVE ) ) <span>//</span><span>查看应用程序消息队列,有消息时将队列中的消息派发出去。</span><span> 74</span><span> {
</span><span> 75</span> TranslateMessage( &msg ); <span>//</span><span>将虚拟键消息转换为字符消息</span><span> 76</span> DispatchMessage( &msg ); <span>//</span><span>分发一个消息给窗口程序。</span><span> 77</span><span> }
</span><span> 78</span><span> }
</span><span> 79</span><span> 80</span><span>//</span><span>【6】窗口类的注销</span><span> 81</span> UnregisterClass(L<span>"</span><span>ForTheDreamOfGameDevelop</span><span>"</span>, wndClass.hInstance); <span>//</span><span>程序准备结束,注销窗口类</span><span> 82</span><span>return</span><span>0</span><span>;
</span><span> 83</span><span>}
</span><span> 84</span><span> 85</span><span> 86</span><span>//</span><span>-----------------------------------【WndProc( )函数】--------------------------------------
</span><span> 87</span><span>//</span><span> 描述:窗口过程函数WndProc,对窗口消息进行处理
</span><span> 88</span><span>//</span><span>------------------------------------------------------------------------------------------------</span><span> 89</span><span>LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
</span><span> 90</span><span>{
</span><span> 91</span><span>switch</span>( message ) <span>//</span><span>switch语句开始</span><span> 92</span><span> {
</span><span> 93</span><span>case</span> WM_PAINT: <span>//</span><span> 若是客户区重绘消息</span><span> 94</span> ValidateRect(hwnd, NULL); <span>//</span><span> 更新客户区的显示</span><span> 95</span><span>break</span>; <span>//</span><span>跳出该switch语句</span><span> 96</span><span> 97</span><span>case</span> WM_KEYDOWN: <span>//</span><span> 若是键盘按下消息</span><span> 98</span><span>if</span> (wParam == VK_ESCAPE) <span>//</span><span> 如果被按下的键是ESC</span><span> 99</span> DestroyWindow(hwnd); <span>//</span><span> 销毁窗口, 并发送一条WM_DESTROY消息</span><span>100</span><span>break</span>; <span>//</span><span>跳出该switch语句</span><span>101</span><span>102</span><span>case</span> WM_DESTROY: <span>//</span><span>若是窗口销毁消息</span><span>103</span> PostQuitMessage( <span>0</span> ); <span>//</span><span>向系统表明有个线程有终止请求。用来响应WM_DESTROY消息</span><span>104</span><span>break</span>; <span>//</span><span>跳出该switch语句</span><span>105</span><span>106</span><span>default</span>: <span>//</span><span>若上述case条件都不符合,则执行该default语句</span><span>107</span><span>return</span> DefWindowProc( hwnd, message, wParam, lParam ); <span>//</span><span>调用缺省的窗口过程</span><span>108</span><span> }
</span><span>109</span><span>110</span><span>return</span><span>0</span>; <span>//</span><span>正常退出</span><span>111</span> }
9. 命名规范的建议
作者强烈推荐了《代码大全》这本砖头书,哪天有时间的话研读一下。
原文:http://www.cnblogs.com/f91og/p/7142125.html
【说明】:本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:254677821)!