关于VC++的Winmain函数(WINAPI是啥?)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于VC++的Winmain函数(WINAPI是啥?)相关的知识,希望对你有一定的参考价值。

学过C++,定义main函数时用 int main()
但VC++的Winmain函数则为int WINAPI Winmain()
int 是返回值类型,那么WINAPI是什么?
还是不理解_stdcall是什么

WinMain()函数等价于控制台程序中的main()函数。

该函数是执行开始的地方,也是为程序其余部分执行基本初始化工作的地方。

为了允许Windows传递数据,WinMain()函数有4个形参和一个int类型的返回值,其原型如下:

int WINAPI WinMain(HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine,

int nCmdShow

);

在返回类型说明符int的后面,有一个函数说明符WINAPI。WINAPI是一个Windows定义的宏,将使系统以特定于Windows
API函数的某种特殊方式处理函数名和实参。这种方式与C++通常处理函数的方式不同。具体的细节是不重要的——这只不过是Windows所要求的方式而
已,因此只需要将WINAPI宏名称放在由Windows调用的函数名前面即可。

如果确实想要了解调用约定,就查看随Visual
C++提供的文档,文档中有对调用约定的描述。WINAPI定义为__stdcall,将此修饰符置于函数名之前表明使用的是标准Windows调用约
定。这要求参数以相反的顺序被推入栈,被调用函数结束时清除栈。本章稍后将看到的CALLBACK修饰符也定义为__stdcall,因此与WINAPI
是等价的。标准C++调用约定由__cdecl修饰符指定。

Windows传递给WinMain()函数的4个参数包含着重要的数据:

hInstance属于HINSTANCE类型,是指向某个实例的句柄——
这里的实例是正在运行的程序。句柄是标识某种对象(这里是应用程序的实例)的整数值。句柄的实际整数值是多少并不重要。在任何给定时刻都可能有好几个程序
在Windows下执行,这就使相同应用程序可能有若干副本同时在活动,而这种情形需要识别出来。因此,hInstance句柄标识某个特定的副本。如果
启动某个程序的多个副本,则每个副本都有自己独特的hInstance值。正如我们很快就将看到的那样,句柄还用来标识各种其他事物。

hPrevInstance是从16位版本的Windows操作系统继承下来的,我们可以放心地对它置之不理。在当前版本的Windows中,该参数始终为空。

lpCmdLine是指向某个字符串的指针,该字符串包含启动程序的命令行字符。该指针允许挑出可能在命令行中出现的任何参数值。LPSTR类型是
另一种Windows类型,用来指定32位(long)的字符串指针,或者当以64位模式编译时,则用来指定64位的字符串指针。WinMain()的另
一个版本接收LPWSTR,用于使用Unicode。

nCmdShow决定着被创建窗口的外观。窗口可以正常显示,也可以最小化显示;例如,程序的快捷方式可能指定该程序在启动时应该最小化显示。该参
数可以是一组固定值之一,这些值是由像SW_SHOWNORMAL和SW_SHOWMINNOACTIVE这样的一些符号常量定义的。此类定义窗口显示方
式的常量还有9个,它们都以SW_开始。通常不需要检查nCmdShow的值,而是直接将其传递给负责显示应用程序窗口的Windows API函数。

程序中的WinMain()函数需要做以下4件事情:

告诉Windows该程序需要的窗口种类

创建程序窗口

初始化程序窗口

检索属于该程序的Windows消息
参考技术A 其实那是程序进入点:
正如在C程序中的进入点是函数main一样,Windows程序的进入点是WinMain,总是像这样出现:

int WINAPI WinMain ( HINSTANCE hInstance,HINSTANCE hPrevInstance, LPSTR szCmdLine,int iCmdShow)

它在WINBASE.H中声明如下:

int WINAPI
WinMain(

HINSTANCE hInstance,

HINSTANCE hPrevInstance,

LPSTR lpCmdLine,

int nShowCmd

);

WinMain函数声明为返回一个int值。WINAPI标识符在WINDEF.H定义,语句如下:

#define WINAPI __stdcall

该语句指定了一个呼叫约定,包括如何生产机械码以在堆栈中放置函数呼叫的参数。许多Windows函数呼叫声明为WINAPI。

WinMain的第一个参数被称作「执行实体句柄」。在Windows程序设计中,句柄仅是一个应用程序用来识别某些东西的数字。在这种情况下,该句柄唯一地标识该程序,还需要它在其它Windows函数呼叫中作为参数。在Windows的早期版本中,当同时运行同一程序多次时,您便创建了该程序的「多个执行实体(multiple instances)」。同一应用程序的所有执行实体共享程序和只读的内存(通常是例如菜单和对话框模板的资源)。程序通过检查hPrevInstance参数就能够确定自身的其它执行实体是否正在运行。然后它可以略过一些繁杂的工作并从前面的执行实体将某些数据移到自己的数据区域。

在32位Windows版本中,该概念已被抛弃。传给WinMain的第二个参数总是NULL(定义为0)。

WinMain的第三个参数是用于执行程序的命令列。某些Windows应用程序利用它在程序启动时将文件加载内存。WinMain的第四个参数指出程序最初显示的方式,可以是正常的或者是最大化地充满整个画面,或者是最小化显示在工作列中

参考资料:<<Windows程序程序>>

本回答被提问者和网友采纳
参考技术B 是一个宏
#define WINAPI __stdcall

具体来说,他们是关于堆栈的一些说明,首先是函数参数压栈顺序,其次是压入堆栈的内容由谁来清除,调用者还是函数自己?

stdcall的调用约定意味着:

1)参数从右向左压入堆栈;
2)函数自身修改堆栈;
3)函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸。
参考技术C _stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。 参考技术D WinMain是WIN32项目,控制台的C程序继续使用 void main(void).

WinAPI WinMain, CreateMutex, ShellExecute三个函数

WinMain函数是提供给用户的Windows应用程序入口点,其原型如下:

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR ipCmdLine, int nCmdShow);

函数有四个参数:

hInstancd: 是当前应用长须实例的Handle。

hPrevInstance: 是应用程序上一个实例的Handle。(MSDN:如果你想要知道程序是否有另一个实例,建议使用Mutex来实现,用Mutex可以实现只运行一个实例)

ipCmdLine: 字符串,是命令行参数。

nCmdShow: int型,指明windows应该怎么实现,windows定义了一系列的宏来帮助记忆,以SW开头,如:SW_SHOW。

返回值是一个int型。

 

如果函数运行成功的话,它会一直运行,知道接收到WM_QUIT消息,它应该返回消息的wParam参数的退出值。如果函数进入消息循环前退出,它应该返回0。

 

 

WINAPI 是一个宏定义:

#define WINAPI __stdcall

 __stdcall和__cdecl, __pascal, __fastcall都是一些类似的关键字,详细信息如下:

1.它们实际上是关于堆栈的一些说明,包括函数参数压栈顺序和压入堆栈的内容由谁来清除(是调用者还是自己)。而上述这些关键字用来告诉编译器产生什么样的汇编代码。

2.VC由两种函数的调用方式:__stdcall和__cdecl

前者指的是PASCAL调用方式,后者指的时C调用方式。使用PASCAL调用方式,函数在返回到调用者之间将参数从栈中删除;使用C调用方式,参数的删除是调用者完成的。

WinMain函数由系统调用,windows系统规定由系统调用的函数都要遵循PASCAL调用方式,但是VC中函数的缺省调用方式是C调用方式,所以要在WinMain前显式声明WINAPI。在Windows编程中将会遇到很多类似的声明,如:CALLBACK, WINAPI, PASCAL这些在Intel CPU的计算机上都是__stdcall。

3.__cdecl是C/C++和MFC程序默认使用的调用方式,也可以在函数声明时加上__cdecl关键字来手动指定。采用__cdecl调用方式时,函数的参数按照从右到左的顺序入栈,并且由函数调用者把参数弹出栈以清理堆栈,因此,实现可变参数的函数只能使用该调用方式,由于每一个使用__cdecl调用方式的函数都要包含清理堆栈的代码,所以产生的可执行文件的大小会比较大;__stdcall调用方式用于调用win32 API函数,采用__stdcal调用方式时,函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,函数参数个数固定,由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈;__fastcall调用方式用于对性能要求非常高,__fastcall调用方式将函数的从左边开始的两个大小不大于4个字节(DWORD)的参数分别放在ECX和EDX寄存器,其余的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的堆栈。

thiscall仅仅用于"C++"成员函数。this指针存放在CX/ECX寄存器中,参数从右到左压。thiscall不是关键字,因此不能被程序员指定。

nakedcall。当采用其他调用方式时,如果必要的话,进入函数时编译器会产生代码来保存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的特点是他讲参数左边的两个参数放在寄存器上,比较快。其余参数还是在堆栈中,堆栈还是由函数自己清理。

 

以上是关于关于VC++的Winmain函数(WINAPI是啥?)的主要内容,如果未能解决你的问题,请参考以下文章

WinAPI WinMain, CreateMutex, ShellExecute三个函数

VC中MFC从哪里开始运行,MFC怎么调试

VC中MFC从哪里开始运行,MFC怎么调试,很急呀

哪一位VC的高手能告诉我为啥WinMain函数中无法使用Cout或Cin?不胜感激!

为啥我在一个VC++程序中找不到主函数main啊?

关于 ReadFile() WinAPI,GetLastError 抛出错误 183。在这种情况下,“ERROR_ALREADY_EXISTS”是啥意思?