本系列文章记录我学习VC++的过程。
一直以来我对自己的定位是C++工程师,但到目前为止我对C++的了解还停留在C阶段,只会用C的特性加上面向对象的知识写一点控制台程序,要我写真正能应用到企业上的实用性应用就无能为力了。虽然实习的时候靠着学校学的基础知识成功应聘了C++开发工程师,但现在做的却是Flex方向的开发工作。
所以我要用这个暑假全面攻克C++,包括VC++,C++11新特性及STL基础。
话不多说,现在开始VC++的HelloWorld!先贴代码。
LRESULT CALLBACK WinProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
/*主程序*/
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPreInstance,
LPSTR lpCmdLine,
int nCmdShow) {
//(1)设计窗口类
WNDCLASS wndcls;
wndcls.cbClsExtra = 0; //窗口类附加内存
wndcls.cbWndExtra = 0; //窗口附加内存
wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); //窗口背景刷
wndcls.hCursor = LoadCursor(NULL, IDC_CROSS); //窗口光标
wndcls.hIcon = LoadIcon(NULL, IDI_ERROR); //窗口图标
wndcls.hInstance = hInstance; //窗口句柄
wndcls.lpfnWndProc = WinProc; //窗口处理过程
wndcls.lpszClassName = _T("myWindow"); //窗口类
wndcls.lpszMenuName = NULL; //窗口菜单
wndcls.style = CS_HREDRAW | CS_VREDRAW; //窗口类样式
//(2)注册窗口类
RegisterClass(&wndcls);
//(3)创建窗口
HWND hwnd;
hwnd=CreateWindow(_T("myWindow"), _T("Hello"), WS_OVERLAPPEDWINDOW, 100, 100, 960, 640, NULL, NULL, hInstance, NULL);
//(4)显示及刷新窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
//(5)开始消息循环
MSG msg;
BOOL bRet;
while (bRet=GetMessage(&msg, NULL, 0, 0)) {
//如果指定消息属于当前创建的窗口,那么必须做一个错误判断
//因为窗口被销毁后获取消息就会得到一个错误,不作判断的话会陷入死循环
//当然,指定消息所属的窗口为空,判断错误就可有可无了
if (bRet == -1) {
return -1;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WinProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case WM_CHAR:
CHAR szChar[20];
//wprintf(szChar, _T("%d"), wParam);
sprintf_s(szChar, "按键代码 is %c", wParam);
MessageBoxA(hWnd, szChar, "按键", 0);
break;
case WM_LBUTTONDOWN:
{
MessageBox(hWnd, _T("鼠标左键按下!"), _T("提示"), 0);
HDC hDc;
hDc = GetDC(hWnd);
TextOut(hDc, 0, 0, _T("Vc++ 程序设计!"), _tcslen(_T("Vc++ 程序设计!")));
ReleaseDC(hWnd, hDc);
}
break;
case WM_PAINT:
{
HDC hDc;
PAINTSTRUCT ps;
hDc = BeginPaint(hWnd, &ps); //该函数会发送WM_ERASEBKGND消息,使用窗口画刷擦除窗口
TextOut(hDc, 0, 0, _T("你好,世界!"), _tcslen(_T("你好,世界!")));
EndPaint(hWnd, &ps);
}
break;
case WM_CLOSE:
if (MessageBox(hWnd, _T("你真的要退出吗?"), _T("提示"), 0) == IDOK) {
DestroyWindow(hWnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
Windows应用程序中的几个重要概念:
1、句柄(HANDLE):句柄是Windows操作系统用来区分每个应用的唯一标识。句柄的类型有很多种,有窗口句柄(HWND),图标句柄(HICON),光标句柄(HCURSOR)和画刷句柄(HBRUSH)等等。
2、窗口:窗口是Windows程序最基本的元素,每个Windows程序都至少有一个窗口,称为主窗口。窗口是Windows应用程序与用户进行交互的接口。
3、消息:消息是Windows应用程序一个非常重要的概念,Windows应用程序和操作之间的通信全是消息。操作系统会把用户操作等消息封闭成一个消息放入消息队列中,应用程序从消息队列中取出消息进行响应。这就是操作系统给应用程序发送消息,即操作系统调用应用程序中一个专门处理消息的函数,称为窗口过程。
4、消息队列:操作系统发送的消息可以不进消息队列,也可以进消息队列,进消息的队列的消息等待应用程序取出进行响应。不进消息队列的消息由操作系统直接调用程序的窗口过程进行处理。无论进不进消息队列,消息的处理最终都是调用窗口过程。
一、主函数WinMain
函数原型为下
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPreInstance,
LPSTR lpCmdLine,
int nCmdShow);
这几个参数由操作系统在应用程序启动时赋予,用不我们手动指定。
hInstance是当前应用程序句柄。
hPreInstance是此类应用程序的上一个运行句柄,win32程序中该参数为空。
lpCmdLine是应用程序启动时带的命令参数,如用记事本打开一个文件时,文件名就是记事本带的参数。
nCmdShow指定窗口启动时的显示方式,如最大化,最小化,隐藏等。
二、窗口过程函数(消息处理的地方)
窗口过程函数用于处理发送给窗口的消息,是一个回调函数。应用程序接收到消息后使用DispathcMessage(&msg)将消息回传给系统,系统则调用窗口过程函数对消息进行处理。函数原型为下
LRESULT CALLBACK WindowProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
Hwnd是窗口的句柄。
uMsg是消息的类型,是Win api定义好的一些常量,以WM_开头,如WM_LBUTTONDOWN表示按下鼠标左键。
wParam和LParam是消息带的参数,这两个宏分别代表不同类型的参数。查看它们的定义:
WPARAM= UINT_PTR=unsigned int
LPARAM=LONG_PTR=long
从定义可以看出WPARAM其实是无符号整型,LPARAM是长整型。windows对很多数据类型进行重定义,主要是为了更直观地看出参数的作用。这个函数之所以要带这两个参数,是因为不同的消息带的参数可能不同,有些是无符号的,有些是有符号的。(这里说到long和unsigned int,就随便说说c++中这几个整型的区别。一直以来我都以为long比int长,后来才知道其实不是这么回事。int通常代表机器证书的自然长度,可能是16位也可以是32位,C++引入short和long就是为了直观地表示字节长度。short是16位,而long在16位和32位系统中是32位,在64位系统中是64位。long long则是128位,即8字节。具体的请看下表。)
类型 | 16位系统/字节 | 32位系统/字节 | 64位系统/字节 |
char | 1 | 1 | 1 |
char* | 2 | 4 | 8 |
short | 2 | 2 | 2 |
int | 2 | 4 | 4 |
long | 4 | 4 | 8 |
long long | 8 | 8 | 8 |
所以,VC给我们定义这些将很多数据类型重定义成直观的名称还是很有用的,让我们不用再纠结到底该用哪种数据类型。
返回值LRESULT也是LONG_PTR类型,即long类型。
再看一下CALLBACK的定义:
#define CALLBACK __stdcall
这是一个宏定义,CALLBACK的意思是告诉程序这个函数的参数调用方式是怎样的,这种修饰符称为调用约定。具体的请见
简单说_stdcall就是函数参数由右向左进栈,函数返回时由函数自己而不是调用者清理参数。这种调用约定的函数参数个数是固定的,因此被调用者可以用ret n的方式自己清理。
三、创建窗口
1、设计窗口
设计窗口其实就是定义一个窗口类,并设计窗口类的各个属性。
WNDCLASS wndcls;
wndcls.cbClsExtra = 0; //窗口类附加内存
wndcls.cbWndExtra = 0; //窗口附加内存
wndcls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); //窗口背景刷
wndcls.hCursor = LoadCursor(NULL, IDC_CROSS); //窗口光标
wndcls.hIcon = LoadIcon(NULL, IDI_ERROR); //窗口图标
wndcls.hInstance = hInstance; //窗口句柄
wndcls.lpfnWndProc = WinProc; //窗口处理过程
wndcls.lpszClassName = _T("myWindow"); //窗口类
wndcls.lpszMenuName = NULL; //窗口菜单
wndcls.style = CS_HREDRAW | CS_VREDRAW; //窗口类样式
2、注册窗口类
RegisterClass(&wndcls);
HWND hwnd;
hwnd=CreateWindow(_T("myWindow"), _T("Hello"), WS_OVERLAPPEDWINDOW, 100, 100, 960, 640, NULL, NULL, hInstance, NULL);
4、显示及刷新窗口
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
5、开始消息循环。