我是靠谱客的博主 欢呼大树,这篇文章主要介绍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程序是这样的:

// 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:
在此处引用程序需要的其他头文件

// 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;
}
我们来分析一下这个程序

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

// Windows 头文件:
#include <windows.h>

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

// 全局变量:
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;
}
M);

我们来一个个分析:

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

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

接下来是

	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)
  ...
}

接下来是

	MSG msg;
HACCEL hAccelTable;

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

	// 初始化全局字符串
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_WIN32, szWindowClass, MAX_LOADSTRING);

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

MyRegisterClass(hInstance);

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

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

//
//
函数:
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函 的返回值返回。

	// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}

这里我们来初始化一个应用程序,如果不为真,程序马上退出。
来看看InitInstance函数是怎么定义的
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函数:
	hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32));
这里我们加载一个加速表,那什么是加速表呢?通俗来讲,就是一个快捷键的一个表,而hAccelTable就是用来接受这个函数的返回值。
	// 主消息循环:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
这是一个消息循环,用来接收用户的各种消息,比如左键按下的消息,键盘输入的消息,鼠标移动的消息,这些都会被他接收,
	TranslateMessage(&msg);
DispatchMessage(&msg);
TranslateMessage函数功能是: 将虚拟键消息转换为字符消息。
那什么是虚拟键呢?
 实际的键盘输入每按下一个件要有对应的键值。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内容请搜索靠谱客的其他文章。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部