《VC++深入详解–学习笔记》(1)Windows 内部运行机制
Filed Under (VC++ 学习笔记) by panmaoru on 30-11-2009
Windos系统提供了各种各样的函数,这些函数是windows操作系统提供给应用程序的接口 Application Progress Interface,所有主要的windows函数都在Windows.h头文件中进行了声明。
Win32 SDK(Soft Development Kit)是Windows 32平台下的软件开发包,包括API函数,帮助文档和一些辅助开发工具。
Windows是基于消息机制的,消息分标准消息,命令消息和通告消息。Windows程序中的消息又分进队消息和不进队消息,两种消息都由消息响应函数进行处理。
在VC中创建一个win32应用程序的大概流程如下:
- 编写WinMain函数
- 设计窗口类(WNDCLASS);
- 注册窗口类(RegisterClass);
- 创建窗口(CreateWindow);
- 显示并更新窗口(ShowWindow|UpdateWindow);
- 编写消息循环;
- 编写窗口过程函数。
一个WinMain函数的原型如下:
int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state );
此处WINAPI是一个_stdcall宏定义,VC有很多宏定义,这个很让人头大,需要慢慢去习惯,从网络上找了2中函数定义的说明与区别解释。
- __cdecl:这是编译器默认的函数调用转换方式,它可以处理可变参数的函数调用。参数的入栈顺序是从右向左。在函数运行结束后,由调用函数负责清理入栈的参数。在编译时,在每个函数前面加上下划线(_),没有函数名大小写的转换。即_functionname。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。
- __stdcall:函数参数从右向左入栈,被调用函数负责入栈参数的清理工作。函数名转换格式如下:_functionname@number。函数参数个数固定。
WINDOWS 的函数调用时需要用到栈( STACK ,一种先入后出的存储结构)。当函数调用完成后,栈需要清除,这里就是问题的关键,如何清除?
如果函数使用 _cdecl ,那么栈的清除工作是由调用者,用 COM 的术语来讲就是客户来完成的。这样带来了一个棘手的问题,不同的编译器产生栈的方式不尽相同,那么调用者能否正常的完成清除工作呢?答案是不能。
如果使用 __stdcall ,上面的问题就解决了,函数自己解决清除工作。所以,在跨(开发)平台的调用中,我们都使用 __stdcall (虽然有时是以 WINAPI 的样子出现)。
那么为什么还需要 _cdecl 呢?当我们遇到这样的函数如 fprintf() 它的参数是可变的,不定长的,被调用者事先无法知道参数的长度,事后的清除工作也无法正常的进行,因此,这种情况我们只能使用 _cdecl 。
到这里有一个结论,如果你的程序中没有涉及可变参数,最好使用 __stdcall 关键字,我们创建一个Win32应用程序都是用的系统API函数,参数固定,所以都用__stdcall了。
设计窗口类、册窗口类、创建窗口、显示并更新窗口通常一起完成,MSDN中有详细的说明,可以根据一些默认参数修改窗口样式。在完成窗口的设计,创建更新后,需要创建一个消息循环。
MSG msg;
while(GetMessage(&msg,NULL,0,0))//消息循环
{
TranslateMessage(&msg);//将虚拟键值消息转化为字符消息,并将字符消息投递到消息队列中
DispatchMessage(&msg);//分派一个消息窗口,对消息进行处理
}
GetMessage函数接收到的消息除了WM_QUIT外都返回非零值。
消息响应函数对消息队列中的消息事件执行处理,在窗口初始化的时候将函数的指针提供给窗口,当事件发生的时候调用函数,消息响应函数在定义前必须在窗口注册,创建等过程前事先声明。
DC:Device Contex,是一个包含设备信息的结构体,这个地方的概念不大容易理解。
Windows创建了自己的命名约定,称之为匈牙利命名法,通常在变量前面加上一些前缀字符。
| 前缀 | 含义 | 前缀 | 含义 |
| a | 数组 | fn | 函数 |
| b | 布尔型 | h | 句柄 |
| by | 无符号字节 | i | 整数 |
| c | 字符 | m_ | 类的数据成员 |
| cb | 字节计数 | n | 短整型或整数 |
| rgb | RGB颜色的长整型 | np | 近指针 |
| cx, cy | 短整型 | p | 执政 |
| dw | 无符号长整型 | l | 长整型 |
| lp | 长指针 | sz | 以0结束的字符串 |
| s | 字符串 | tm | 正文大小 |
| x.y | 无符号整型 | w | 无符号整型 |
最后,把原书的代码抄一遍,虽然是照的书本写,过程中还是出了一些错误,好不容易才调试完。VC开发环境的提示功能几乎为零,god!
#include <windows.h>
#include <stdio.h>
//申明消息响应函数
LRESULT CALLBACK WinSunProc(
HWND hwnd, // 窗口句柄
UINT uMsg, // 消息标示
WPARAM wParam, // 消息内容,参看了一些文章,并没有2中参数区别的明确说明。
LPARAM lParam); // 有待进一步研究
int WINAPI WinMain(
HINSTANCE hInstance, //当前应用程序的实例句柄
HINSTANCE hPreInstance,//NULL
LPSTR lpCmdLine,// 默认参数?
int nCmdShow)// 窗口显示方式 最大、最小、隐藏。。。
{
WNDCLASS wndcls;//1、设计一个窗口
wndcls.cbClsExtra=0;
wndcls.cbWndExtra=0;
wndcls.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wndcls.hCursor=LoadCursor(NULL,IDC_ARROW);
wndcls.hIcon=LoadIcon(NULL,IDI_WINLOGO);
wndcls.hInstance=hInstance;
wndcls.lpfnWndProc=WinSunProc; //消息处理函数
wndcls.lpszClassName="FVC";
wndcls.lpszMenuName=NULL;
wndcls.style=CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wndcls); //2、注册窗口
HWND hwnd;
hwnd=CreateWindow("FVC","www.xxx.com",//3、创建窗口
WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);
ShowWindow(hwnd,SW_SHOWNORMAL);//4、显示窗口
UpdateWindow(hwnd);//5、更新窗口
MSG msg;
while(GetMessage(&msg,NULL,0,0))//消息循环
{
TranslateMessage(&msg);//将虚拟键值消息转化为字符消息,并将字符消息投递到消息队列中
DispatchMessage(&msg);//分派一个消息窗口,对消息进行处理
}
return msg.wParam;
}
LRESULT CALLBACK WinSunProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch(uMsg)
{
case WM_CHAR:
char szchar[20];
sprintf(szchar,"char code is %d",wParam);
MessageBox(hwnd,szchar,"char",0);
break;
case WM_LBUTTONDOWN:
MessageBox(hwnd,"mouse clicked","message",0);
HDC hdc;
hdc=GetDC(hwnd);
TextOut(hdc,0,50,"out message info",strlen("out message info"));
break;
case WM_PAINT:
HDC hdc2;
PAINTSTRUCT ps;
hdc2=BeginPaint(hwnd,&ps); //WM_PAINT消息才能BeginPaint函数
TextOut(hdc2,0,0,"paint message info",strlen("paint message info"));
EndPaint(hwnd,&ps);
break;
case WM_CLOSE:
if(IDYES==MessageBox(hwnd,"finish?","message",MB_YESNO))
{
DestroyWindow(hwnd);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
return 0;
}
