win32可执行程序
win32可执行程序分为两类:基于控制台和基于窗口的,基于控制台的程序它的入口函数是传统的main,而基于窗口的程序它的入口函数是WinMain。这里以基于窗口的应用程序为例。
win32项目入口函数
在win64位系统下通过vs2015建立win32项目 程序主函数为:
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
该函数的功能是被系统调用,作为一个32位应用程序的入口点。
WinMain函数会:
- 初始化应用程序
- 显示主窗口
- 进入一个消息接收一发送循环(这个循环是应用程序执行的其余部分的顶级控制结构。)
调用约定APIENTRY
调用约定(Calling convention)决定以下内容:
- 函数参数的压栈顺序
- 由调用者还是被调用者把参数弹出栈
- 以及产生函数修饰名的方法
//vs中的宏定义
//采用__stdcall 方式的调用约定
#elif (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED)//如果定义了_STDCALL_SUPPORTED
#define CALLBACK __stdcall
#define WINAPI __stdcall //WINAPI <---__stdcall
#define WINAPIV __cdecl
#define APIENTRY WINAPI //APIENTRY <---WINAPI
#define APIPRIVATE __stdcall
#define PASCAL __stdcall
__stdcall
- 按从右至左的顺序压参数入栈,
- 由被调用者把参数弹出栈。
- 对于“C”函数或者变量,修饰名以下划线为前缀,然后是函数名,然后是符号“@”及参数的字节数
- 所有的Win32 API函数都遵循该约定
_cdecl
- 按从右至左的顺序压参数入栈
- 由调用者把参数弹出栈
- 对于“C”函数或者变量,修饰名是在函数名前加下划线。
_fastcall
- 头两个DWORD类型或者占更少字节的参数被放入ECX和EDX寄存器,其他剩下的参数按从右到左的顺序压入栈。
- 由被调用者把参数弹出栈.
- 对于“C”函数或者变量,修饰名以“@”为前缀,然后是函数名,接着是符号“@”及参数的字节数
函数名wWinMain
这里的函数名为wWinMain,与之相对应的还有WinMain和_tWinMain,区别在于不同>的编码要使用不同的版本:wWinMain是unicode版,WinMain是ANSI版。
- wWinMain 第一个w表示宽字节
- WinMain 是窄字节版
- _tWinMain 前面的t表示可变
推荐使用_tWinMain,因为它可以自动选择最适合的版本。
具体看tWinMain的宏定义:
#ifdef _UNICODE
#define _tmain wmain
#define _tWinMain wWinMain //如果是_UNICODE 就选择wWinMain
#else /* ndef _UNICODE */
#define _tmain main
#define _tWinMain WinMain //否则就选择WinMain
参数hInstance
它表示当前应用程序的实例句柄.
那么进程自身的句柄为什么要从外部传进来呢?
- 因为进程的创建是由操作系统完成的,操作系统创建完进程后再将创建的进程的句柄饭回来。
为什么main函数不用从外部传进来呢?
- C语言出现的时候还没操作系统, 所以在设计上没有进行传递hInstance
main函数如何获取当前程序的实例句柄?
- 可以通过GetInstance API来获取hInstance
参数hPrevInstance
hPrevInstance表示应用程序的先前实例的句柄。
同一个程序打开两次,出现两个窗口第一次打开的窗口就是先前实例的窗口。
但是 在Win32下并不能通过判断hPrevInstance是否为NULL来判断一个程序的另一个实例是否存在
因为在Win32中,每一个进程都有一个独立的4G地址空间,从0到2G属于进程私有,对其他进程来说是不可见的所以对于一个32位程序,该参数总为NULL。
参数lpCmdLine
lpCmdLine MSDN解释
Pointer to a null-terminated string specifying the command line for the application, excluding the program name. To retrieve the entire command line, use the GetCommandLine function.
大致意思为:指向一个应用程序命令中除了程序名的以空字符结束的字符串的指针。使用GetCommandLine函数可以获取整个命令行.
相对于main使用两个参数argv和argc来传递命令行参数,WinMain就是除去可执行文件字符串(加上后续空格符)的整个命令行作为一个字符串,直接给了lpCmdLine。
参数nCmdShow
指明窗口如何显示
SW_HIDE: 隐藏窗口并且激活另外一个窗口。
SW_SHOW: 激活一个窗口并以原来的尺寸和位置显示窗口。
- SW_SHOWMAXIMIZED:激活窗口并且将其最大化。
- SW_SHOWMINIMIZED:激活窗口并将其最小化(以图标显示)
- SW_MINIMIZE:最小化指定的窗口,并且激活在系统表中的顶层窗口。
- SW_RESTORE:激活并显示窗口。如果窗口已经最小化或最大化,将恢复到原来的尺寸和位置显示窗口
- SW_SHOWMINNOACTIVE:将一个窗口显示为图标。激活窗口维持活动状态。
- SW_SHOWNA:以窗口的当前状态显示窗口。激活窗口保持活动状态。
- SW_SHOWNOACTIVATE:以窗口的最近一次的尺寸和位置显示窗口。不激活窗口。