说实话,单纯学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,然后像填空题一样,一个个设置,我们来看看这个结构体各个成员的意义:
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
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创建一个重叠式窗口,弹出式窗口或子窗口。并显示窗口和更新窗口。
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WIN32));
// 主消息循环:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
这是一个消息循环,用来接收用户的各种消息,比如左键按下的消息,键盘输入的消息,鼠标移动的消息,这些都会被他接收,
TranslateMessage(&msg);
DispatchMessage(&msg);
DispatchMessage函数是干什么用的呢?来看看度娘:
该函数分发一个消息给窗口程序。通常消息从GetMessage函数获得。消息被分发到回调函数(过程函数),作用是消息传递给操作系统,然后操作系统去调用我们的回调函数,也就是说我们在窗体的过程函数中处理消息。
这是while循环,只要条件为真(即GetMessage函数返回值),就会一直循环下去,除非程序销毁,即GetMessage返回值为false。
在此附上源代码(注:博主用的是vs2013,vs2012应该也可以打开,望大家体谅)
传送门:http://download.csdn.net/detail/u011304823/7298951
最后
以上就是欢呼大树最近收集整理的关于3D游戏之路--第四篇—win32 编程(1)的全部内容,更多相关3D游戏之路--第四篇—win32内容请搜索靠谱客的其他文章。
发表评论 取消回复