当前位置:首页 > 操作系统 > Windows

《逐梦旅程 WINDOWS游戏编程之从零开始》读书笔记1——创建窗口

步骤:

  • 窗口类的设计
  • 窗口类的注册
  • 窗口的正式创建
  • 窗口的显示与更新

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).aspxWindows编程 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)!

相关教程推荐

其他课程推荐