winlinglin

Windows API(1) WinMain函数

谁是第一个Windows API?无可置疑,当然是WinMain Function啦。
MSDN上说:The WinMain function is the conventional name for the user-provided entry point for a Microsoft Windows-based application.
即:WinMain函数是Microsoft的一个传统函数命名,它是提供给用户的Windows应用程序的入口点。
它的函数声明如下:
int WINAPI WinMain(         
 HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
);
写惯C++ console程序的人可能会很奇怪,main函数一般不是这样写吗( int main() )?怎么会在函数声明中间加了个词(WINAPI)呢?
其实WINAPI是一个宏,MSDN上说:Calling convention for system functions. This type is declared in WinDef.h as follows: #define WINAPI __stdcall
那么WINAPI就是指_stdcall了。
在网上查了一下,_stdcall还有其他同类,_cdecl _pascal _fastcall...怎么那么多的?
_stdcall _cdecl _pascal _fastcall这些关键字是什么意思,有什么区别呢?
在网上查了一下,总结一下答案:
(1)其实它们就是关于堆栈的一些说明,首先是函数参数压栈顺序,其次是压入堆栈的内容由谁来清除,调用者还是函数自己?
  这些开关用来告诉编译器产生什么样的汇编代码。
(2)VC有两种函数调用方式   一种是__stdcall,另一种是__cdecl  
 函数的调用方式有两种一种是PASCAL调用方式(_stdcall),另一种是C调用方式(_cdecl)  
 使用PASCAL调用方式,函数在返回到调用者之前将参数从栈中删除  
 使用C调用方式,参数的删除是调用者完成的  
 WinMain函数是由系统调用的,Windows系统规定由系统调用的函数都遵守PASCAL调用方式  
 但是VC中函数的缺省调用方式是__cdecl,也就是C调用方式  
 所以在WinMain前显示的声明。  
 在Windows编程中将遇到很多声明修饰符,如CALLBACK,WINAPI,PASCAL这些在Intel CPU的计算机上都是__stdcall
(3)__cdecl是C/C++和MFC程序默认使用的调用约定,也可以在函数声明时加上__cdecl关键字来手工指定。采用__cdecl约定时,
 函数参数按照从右到左的顺序入栈,并且由调用函数者把参数弹出栈以清理堆栈。
 因此,实现可变参数的函数只能使用该调用约定。
 由于每一个使用__cdecl约定的函数都要包含清理堆栈的代码,所以产生的可执行文件大小会比较大。
 __cdecl可以写成_cdecl。
 __stdcall调用约定用于调用Win32 API函数。采用__stdcal约定时,函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,
 函数参数个数固定。由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈。
 __stdcall可以写成_stdcall。
 __fastcall约定用于对性能要求非常高的场合。__fastcall约定将函数的从左边开始的两个大小不大于
 4个字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送
 参数的堆栈。
 __fastcall可以写成_fastcall。
(4)thiscall仅仅应用于“C++”成员函数。this指针存放于CX/ECX寄存器中,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。
(5)naked call。当采用其他的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,
 退出函数时则产生代码恢复这些寄存器的内容。

·特别说明
1. 在默认情况下,采用__cdecl方式,因此可以省略.
2. WINAPI一般用于修饰动态链接库中导出函数
3. CALLBACK仅用于修饰回调函数
4. 你可能已经发现,VC下和BCB下对WINAPI的定义不同,那么你至少理解了
   为什么不能直接从BCB下调用VC的dll的一个原因了。
  
不查不知道,一查吓一跳,怎么那么多规则的?整理了一下思路,其实并不复杂。
VC默认的是_cdecl方式,Win32 API函数是用_stdcall方式的,他们都是将函数参数从右到左入栈的。
_cdecl方式的每个函数都有清理堆栈的代码,可以实现可变参数列表,但可执行文件大小比较大。_stdcall方式是调用者清理堆栈的。
_fastcall的特点是它将参数左边的两个参数放在寄存器上,比较快。其余参数还是在堆栈中,堆栈还是由函数自己清除。
其它就不太清楚了。

好,该看看函数的参数了,hInstance是当前应用程序实例的Handle.
第二个参数hPrevInstance应用程序上一个实例的Handle。MSDN说:如果你要知道应用程序是否有另一个实例,建议使用Mutex(互斥体)来实现。此时,我想到了
单例模式,用Mutex来实现只运行一个实例。
第三个参数lpCmdLine是一个字符串,是命令行参数。
第四个参数nCmdShow是一个int,指明Window应该怎么现实,Windows定义了一系列宏,来帮助记忆,以SW开头,如:SW_SHOW

最后是返回值,它是一个int。
MSDN说:If the function succeeds, terminating when it receives a WM_QUIT message, it should return the exit value contained in that message's wParam parameter. If the function terminates before entering the message loop, it should return zero.
如果它成功的话,它会一直运行,知道收到WM_QUIT消息,它应该返回消息的wParam参数的退出值。如果函数在进入消息循环前退出,它应该返回0。
 

posted on 2009-05-31 09:15 wil 阅读(1595) 评论(2)  编辑 收藏 引用 所属分类: Windows APIs

评论

# 您的博客背景看着好累呀 2009-11-01 23:29 baihonghong

您博客背景好伤眼睛呀,为什么不用点柔和一些的颜色呢  回复  更多评论   

# re: Windows API(1) WinMain函数 2010-03-18 10:56 apple

太厉害了,感觉自己还连入门都没有啊。  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


<2008年12月>
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

导航

统计

常用链接

留言簿(1)

随笔分类

随笔档案

文章分类

搜索

最新评论

阅读排行榜

评论排行榜