我是靠谱客的博主 欢呼大树,这篇文章主要介绍3D游戏之路--第四篇—win32 编程(1),现在分享给大家,希望可以做个参考。

说实话,单纯学c++真的没啥用,难道你强大到能自己写一个系统吗?不然你让用户对着黑漆漆的控制台看吗,所以,我们必须要去学习一套应用程序编程接口,俗称API,以下是它的简介:

API(Application Programming Interface,应用程序编程接口)其实就是操作系统留给应用程序的一个调用接口,应用程序通过调用操作系统的 API 而使操作系统去执行应用程序的命令(动作)。

我们主要学的是windows下的编程(没办法啊,谁叫他如此强大?~),所以我们主要学习win32 API.看看他的介绍:

Win32 API即为Microsoft 32位平台的应用程序编程接口(Application Programming Interface)。所有在Win32平台上运行的应用程序都可以调用这些函数。
使用Win32 API,应用程序可以充分挖掘Windows的32位操作系统的潜力。 Mircrosoft的所有32位平台都支持统一的API,包括函数、结构、消息、宏及接口。使用 Win32 API不但可以开发出在各种平台上都能成功运行的应用程序,而且也可以充分利用每个平台特有的功能和属性。

在具体编程时,程序实现方式的差异依赖于相应平台的底层功能的不同。最显著的差异是某些函数只能在更强大的平台上实现其功能。例如,安全函数只能在Windows NT操作系统下使用。另外一些主要差别就是系统限制,比如值的范围约束,或函数可管理的项目个数等等。

__________________完美分割线________________________

我也就不再废话了,下面进入正题:

一个完整的win32程序是这样的:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// stdafx.h : 标准系统包含文件的包含文件, // 或是经常使用但不常更改的 // 特定于项目的包含文件 // #pragma once //告诉编译器,只头文件只编译一次 #include "targetver.h" #define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的信息 // Windows 头文件: #include <windows.h> //我们主要用到它~~ // C 运行时头文件 #include <stdlib.h> #include <malloc.h> #include <memory.h> #include <tchar.h> // TODO: 在此处引用程序需要的其他头文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Win32教程示例.cpp : 定义应用程序的入口点。 // #include "stdafx.h" #define MAX_LOADSTRING 100 // 全局变量: HINSTANCE hInst; // 当前实例 TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本 TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名 // 此代码模块中包含的函数的前向声明: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此放置代码。 MSG msg; HACCEL hAccelTable; // 初始化全局字符串 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_WIN32, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // 执行应用程序初始化: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32)); // 主消息循环: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam; } // // 函数: MyRegisterClass() // // 目的: 注册窗口类。 // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex); } // // 函数: InitInstance(HINSTANCE, int) // // 目的: 保存实例句柄并创建主窗口 // // 注释: // // 在此函数中,我们在全局变量中保存实例句柄并 // 创建和显示主程序窗口。 // BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // 将实例句柄存储在全局变量中 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } // // 函数: WndProc(HWND, UINT, WPARAM, LPARAM) // // 目的: 处理主窗口的消息。 // // WM_COMMAND - 处理应用程序菜单 // WM_PAINT - 绘制主窗口 // WM_DESTROY - 发送退出消息并返回 // // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc; switch (message) { case WM_COMMAND: wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // 分析菜单选择: switch (wmId) { case IDM_ABOUT: DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // TODO: 在此添加任意绘图代码... EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } // “关于”框的消息处理程序。 INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(lParam); switch (message) { case WM_INITDIALOG: return (INT_PTR)TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) { EndDialog(hDlg, LOWORD(wParam)); return (INT_PTR)TRUE; } break; } return (INT_PTR)FALSE; }
我们来分析一下这个程序

首先,他必须要包含这个库

复制代码
1
2
3
// Windows 头文件: #include <windows.h>

这是win32应用程序所必需的库,其次,我们看看他的全局变量:

复制代码
1
2
3
4
5
6
7
// 全局变量: HINSTANCE hInst; // 当前实例 TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本 TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名

这一看就明白了,不用我多讲了吧,至于这些数据类型,我们一会儿再讲。接下来是:

复制代码
1
2
3
4
5
6
7
// 此代码模块中包含的函数的前向声明: ATOM MyRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);

这是函数的前向声明,因为前面的函数想要调用后面的函数,需要把后面的函数先在前面声明一下,才可以用。接下来是(重点):

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, _In_ int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // TODO: 在此放置代码。 MSG msg; HACCEL hAccelTable; // 初始化全局字符串 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_WIN32, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); // 执行应用程序初始化: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; } hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32)); // 主消息循环: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int) msg.wParam; } M);

我们来一个个分析:

据我们所知,控制台程序都有一个main函数,而win32应用程序也不例外,他必须要有一个WinMain函数,那么这个_tWinMain是怎么回事呢?我们来追踪一下看看,

复制代码
1
2
3
4
5
6
7
8
9
10
/* Program */ #define _tmain wmain #define _tWinMain wWinMain #define _tenviron _wenviron #define __targv __wargv
我们可以看到_tWinMain就是wWinMain,那 _tWinMain和WinMain函数有什么区别呢?我们来看看:

1) main是c/c++的标准入口函数名
2) winmain是windows api窗体程序的入口函数(int winapi winmain()中winapi是__stdcall的宏 在windows.h中定义)
3) _tmain _twinmain是unicode版本函数别名 为了编译时能自动转换字符串编码

我们可以看到因为我们用的是unicode版本的win32程序,所以就把WinMain换成了_tWinMain,拿什么是unicode呢?

unicode就是统一码、万国码、单一码是一种在计算机上使用的字符编码。说白了就是可以支持很多种语言的编码(实际上我们主要的是想要中文编码)。

接下来是

复制代码
1
2
UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine);

UNREFERENCED_PARAMETER函数是什么呢?我们来看看他的作用:

告诉编译器,已经使用了该变量,不必检测警告!
在VC编译器下,如果您用最高级别进行编译,编译器就会很苛刻地指出您的非常细小的警告。当你生命了一个变量,而没有使用时,编译器就会报警告:
“warning C4100: ''XXXX'' : unreferenced formal parameter.” 
所以,为了让编译器不必检测你的警告,就使用UNREFERENCED_PARAMETER语句。比如:
int SomeFunction(int arg1, int arg2)
{
  UNREFERENCED_PARAMETER(arg2)
  ...
}

接下来是

复制代码
1
2
MSG msg; HACCEL hAccelTable;

在这里定义了两个变量,他的用途,我们一会儿再讲,

复制代码
1
2
3
// 初始化全局字符串 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_WIN32, szWindowClass, MAX_LOADSTRING);

这个函数应该很简单了,光从字面上来看就懂了:从 资源 里加载字符串资源到CString对象里。

复制代码
1
MyRegisterClass(hInstance);

在这里,我们设计一个窗口的样式,

我们来看看,这个函数的定义,

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// // 函数: MyRegisterClass() // // 目的: 注册窗口类。 // ATOM MyRegisterClass(HINSTANCE hInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WIN32)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = MAKEINTRESOURCE(IDC_WIN32); wcex.lpszClassName = szWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); return RegisterClassEx(&wcex); }

在这里我们可以看到,这个函数内定义了一个 wcex,然后像填空题一样,一个个设置,我们来看看这个结构体各个成员的意义:

1.cbSize:
WNDCLASSEX 的大小。我们可以用sizeof(WNDCLASSEX)来获得准确的值。
2.style:
从这个窗口类派生的窗口具有的风格。您可以用“or”操作符来把几个风格或到一起。
3.lpfnWndProc:
窗口处理函数的指针。
4.cbClsExtra:
指定紧跟在窗口类结构后的附加字节数。
5.cbWndExtra:
指定紧跟在窗口实例的附加字节数。如果一个应用程序在资源中用CLASS伪指令注册一个对话框类时,则必须把这个成员设成DLGWINDOWEXTRA。
6.hInstance:
本模块的实例句柄。
7.hIcon:
图标的句柄。
8.hCursor:
光标的句柄。
9.hbrBackground:
背景画刷的句柄。
10.lpszMenuName:
指向菜单的指针。
11.lpszClassName:
指向类名称的指针。
12.hIconSm:
窗口类关联的小图标。如果该值为NULL。则把hIcon中的图标转换成大小合适的小图标。
然后在最后返回时调用RegisterClassEx函数 注册一个窗口类。并将 RegisterClassEx函 的返回值返回。

复制代码
1
2
3
4
5
6
// 执行应用程序初始化: if (!InitInstance (hInstance, nCmdShow)) { return FALSE; }

这里我们来初始化一个应用程序,如果不为真,程序马上退出。
来看看InitInstance函数是怎么定义的
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // 将实例句柄存储在全局变量中 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd) { return FALSE; } ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
前两句我们定义了两个参数,并将他们储存。然后用CreateWindow创建一个重叠式窗口,弹出式窗口或子窗口。并显示窗口和更新窗口。
然后我们回到winmain函数:
复制代码
1
2
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32));
这里我们加载一个加速表,那什么是加速表呢?通俗来讲,就是一个快捷键的一个表,而hAccelTable就是用来接受这个函数的返回值。
复制代码
1
2
3
4
5
6
7
8
9
// 主消息循环: while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } }
这是一个消息循环,用来接收用户的各种消息,比如左键按下的消息,键盘输入的消息,鼠标移动的消息,这些都会被他接收,
复制代码
1
2
TranslateMessage(&msg); DispatchMessage(&msg);
TranslateMessage函数功能是: 将虚拟键消息转换为字符消息。
那什么是虚拟键呢?
复制代码
1
实际的键盘输入每按下一个件要有对应的键值。WINDOWS对每个键都定义了一个宏,就称做虚拟键,例如VK_ESC, VK_RETURN, VK_ENTER。软键盘就是一个使用虚拟键的例子,通过向窗口发送 虚拟键 消息,实际上键盘并没有按键按下。

DispatchMessage函数是干什么用的呢?来看看度娘:

该函数分发一个消息给窗口程序。通常消息从GetMessage函数获得。消息被分发到回调函数(过程函数),作用是消息传递给操作系统,然后操作系统去调用我们的回调函数,也就是说我们在窗体的过程函数中处理消息。

这是while循环,只要条件为真(即GetMessage函数返回值),就会一直循环下去,除非程序销毁,即GetMessage返回值为false。



在此附上源代码(注:博主用的是vs2013,vs2012应该可以打开,望大家体谅)

传送门:http://download.csdn.net/detail/u011304823/7298951


最后

以上就是欢呼大树最近收集整理的关于3D游戏之路--第四篇—win32 编程(1)的全部内容,更多相关3D游戏之路--第四篇—win32内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(82)

评论列表共有 0 条评论

立即
投稿
返回
顶部