WINMAIN和main()在C ++(扩展)

对了,我看了这篇文章: WinMain,main和DllMain在C ++中的区别

我现在知道WINMAIN用于窗口应用程序, main()用于控制台。 但是阅读这篇文章并没有真正告诉我为什么有什么不同。

我的意思是把不同的主要function分开来启动一个程序是什么意思? 这是由于性能问题? 或者是什么?

关于function。

C和C ++标准要求任何程序(用于“托pipe”C或C ++实现)具有称为main的函数,该函数充当程序的启动functionmain函数在非局部静态variables的零初始化之后被调用,并且可能但不一定(!,C ++ 11§3.6.2/ 4)这个调用在这些variables的dynamic初始化之后发生。 它可以具有以下签名之一:

 int main() int main( int argc, char* argv[] ) 

加上可能的实现定义签名(C ++ 11§3.6.1/ 2),除了结果types必须是int

因为在C ++ main唯一的这样的函数有一个默认的结果值,即0.如果main函数在普通函数返回exit之后返回,那么以main结果值作为参数被调用。 标准定义了三个保证可以使用的值:0(表示成功), EXIT_SUCCESS (也表示成功,通常定义为0)和EXIT_FAILURE (表示失败),其中两个命名常量由<stdlib.h>定义<stdlib.h>标题,它也声明了exit函数。

main参数旨在表示用于启动进程的命令的命令行参数argc (参数计数)是argv (参数值)数组中的项目数。 除了这些项目argv[argc]保证是0.如果argc > 0 – 这是不能保证! – 然后argv[0]保证是一个指向空string的指针,或者指向“用于调用程序的名字”的指针。 这个名字可能包含一个path,它可能是可执行文件的名字。

使用main参数获取命令行参数在* nix中工作正常,因为C和C ++源于* nix。 但是, main参数编码事实上的 Windows标准是Windows ANSI ,它不支持常规的Windows文件名(例如挪威语Windows安装,希腊语或西里尔文字符的文件名)。 因此,微软select使用一个名为wmain的Windows特定启动函数来扩展C和C ++语言,该函数具有以UTF-16编码的基于宽字符的参数,该参数可以表示任何文件名。

wmain函数可以具有这些签名之一 ,对应于main的标准签名:

 int wmain() int wmain( int argc, wchar_t* argv[] ) 

再加上一些不是特别有用的东西。

也就是说, wmain是一个基于直接宽字符的main替代品。

WinMain基于char的函数在20世纪80年代早期被Windows引入:

 int CALLBACK WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ); 

CALLBACKHINSTANCELPSTR<windows.h>头文件定义( LPSTR只是char* )。

参数:

  • hInstance参数值是可执行文件内存映像的基址,主要用于从可执行文件中加载资源,也可以从GetModuleHandle API函数中获取,

  • hPrevInstance参数始终为0,

  • lpCmdLine参数也可以从GetCommandLine API函数获得,再加上一些奇怪的逻辑来跳过命令行的程序名部分,

  • 也可以从GetStartupInfo API函数获得nCmdShow参数值,但是对于现代Windows,首先创build顶层窗口会自动执行,所以没有任何实际用途。

因此, WinMain函数与标准main函数有一样的缺点,加上一些(特别是冗长和非标准的),并没有它自己的优点,所以除了可能作为一个供应商locking的东西,它真的是莫名其妙的。 但是,使用Microsoft工具链,它使链接器默认为GUI子系统,有些人认为这是一个优势。 但是,例如GNU工具链,它没有这样的效果,所以这个效果不能被依赖。

基于wWinMain wchar_t的函数是WinMain一个宽字符变体,就像wmain是标准main的宽字符变体一样:

 int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR lpCmdLine, int nCmdShow ); 

WINAPICALLBACK相同,而PWSTR只是wchar_t*

没有很好的理由使用任何非标准的函数,除了最不为人所知和最不受支持的函数,即wmain ,然后只是为了方便:避免使用GetCommandLineCommandLineToArgvW API函数来获取UTF-16编码参数。

为了避免微软链接器动作(GNU工具链的链接器不),只需将LINK环境variables设置为/entry:mainCRTStartup ,或者直接指定该选项。 这是Microsoft运行时库入口点函数,它在一些初始化之后调用标准的main函数。 其他启动function具有相同的系统方式命名相应的入口点function。


使用标准mainfunction的例子。

常用的源代码:

Foo.cpp中

 #undef UNICODE #define UNICODE #include <windows.h> int main() { MessageBox( 0, L"Press OK", L"Hi", MB_SETFOREGROUND ); } 

在下面的示例中(首先使用GNU工具链,然后使用Microsoft工具链),首先将该程序构build为控制台子系统程序 ,然后作为GUI子系统程序 。 一个控制台子系统程序,简而言之就是一个控制台程序 ,需要一个控制台窗口。 这是我使用的所有Windows链接器(默认不是很多)的默认子系统,可能适用于所有Windows链接器时期。

对于控制台程序,Windows会根据需要自动创build一个控制台窗口 。 任何Windows进程,无论子系统,都可以有一个关联的控制台窗口,最多一个。 此外,Windows命令解释程序等待控制台程序程序完成,以便程序的文本显示已完成。

相反,GUI子系统程序是不需要控制台窗口的程序。 命令解释程序不会等待GUI子系统程序,batch file除外。 一种避免完成等待的方法,对于这两种程序,都是使用start命令。 从GUI子系统程序呈现控制台窗口文本的一种方法是redirect其标准输出stream。 另一种方法是从程序代码中显式创build一个控制台窗口。

程序的子系统编码在可执行文件的头部。 它不会被Windows资源pipe理器显示出来(除了在Windows 9x中可以“快速查看”一个可执行文件,这个可执行文件与微软的dumpbin工具现在提供的信息几乎相同)。 没有相应的C ++概念。

main用GNU工具链。

 [d:\ dev的\testing]
 > g ++ foo.cpp

 [d:\ dev的\testing]
 > objdump -x a.exe |  find/我“subsys”
 MajorSubsystem版本4
 MinorSubsystemVersion 0
子系统00000003(Windows CUI)
 (秒-1)(f1x00)(ty0)(sc12)(nx0)0x00000004 __major_subsystem_version__
 (秒-1)(f1x00)(ty0)(sc12)(nx0)0x00000003 __subsystem__
 (秒-1)(f1x00)(ty0)(sc12)(nx0)0x00000000__minor_subsystem_version__

 [d:\ dev的\testing]
 > g ++ foo.cpp -mwindows

 [d:\ dev的\testing]
 > objdump -x a.exe |  find/我“subsys”
 MajorSubsystem版本4
 MinorSubsystemVersion 0
子系统00000002(Windows GUI)
 (秒-1)(f1x00)(ty0)(sc12)(nx0)0x00000004 __major_subsystem_version__
 (秒-1)(f1x00)(ty0)(sc12)(nx0)0x00000002 __subsystem__
 (秒-1)(f1x00)(ty0)(sc12)(nx0)0x00000000__minor_subsystem_version__

 [d:\ dev的\testing]
 > _

main用微软的工具链:

 [d:\ dev的\testing]
 > set LINK = / entry:mainCRTStartup

 [d:\ dev的\testing]
 > cl foo.cpp user32.lib
 Foo.cpp中

 [d:\ dev的\testing]
 > dumpbin / headers foo.exe |  find/我“subsys”
             6.00子系统版本
                3子系统(Windows CUI)

 [d:\ dev的\testing]
 > cl foo.cpp / link user32.lib / subsystem:windows
 Foo.cpp中

 [d:\ dev的\testing]
 > dumpbin / headers foo.exe |  find/我“subsys”
             6.00子系统版本
                2子系统(Windows GUI)

 [d:\ dev的\testing]
 > _

使用微软的wmainfunction的例子。

以下主要代码对于GNU工具链和Microsoft工具链演示都是常见的:

bar.cpp

 #undef UNICODE #define UNICODE #include <windows.h> #include <string> // std::wstring #include <sstream> // std::wostringstream using namespace std; int wmain( int argc, wchar_t* argv[] ) { wostringstream text; text << argc - 1 << L" command line arguments:\n"; for( int i = 1; i < argc; ++i ) { text << "\n[" << argv[i] << "]"; } MessageBox( 0, text.str().c_str(), argv[0], MB_SETFOREGROUND ); } 

使用GNU工具链。

GNU工具链不支持微软的wmainfunction:

 [d:\ dev的\testing]
 > g ++ bar.cpp
 d:/ bin中/ mingw的/ bin中/../ LIB / GCC / i686的-PC-的mingw32 / 4.7.1 /../../../ libmingw32.a(main.o),此:main.c中:(。 text.startup + 0xa3):对WinMain的未定义引用
 @ 16'
 collect2.exe:错误:ld返回1退出状态

 [d:\ dev的\testing]
 > _

这里的关于WinMain的链接错误消息是因为GNU工具链支持function(可能是因为古代代码使用了这么多),并且在找不到标准main之后作为最后的手段进行search。

但是,添加一个调用wmain的标准main模块是微不足道的:

wmain_support.cpp

 extern int wmain( int, wchar_t** ); #undef UNICODE #define UNICODE #include <windows.h> // GetCommandLine, CommandLineToArgvW, LocalFree #include <stdlib.h> // EXIT_FAILURE int main() { struct Args { int n; wchar_t** p; ~Args() { if( p != 0 ) { ::LocalFree( p ); } } Args(): p( ::CommandLineToArgvW( ::GetCommandLine(), &n ) ) {} }; Args args; if( args.p == 0 ) { return EXIT_FAILURE; } return wmain( args.n, args.p ); } 

现在,

 [d:\ dev的\testing]
 > g ++ bar.cpp wmain_support.cpp

 [d:\ dev的\testing]
 > objdump -x a.exe |  find/我“子系统”
 MajorSubsystem版本4
 MinorSubsystemVersion 0
子系统00000003(Windows CUI)
 (秒-1)(f1x00)(ty0)(sc12)(nx0)0x00000004 __major_subsystem_version__
 (秒-1)(f1x00)(ty0)(sc12)(nx0)0x00000003 __subsystem__
 (秒-1)(f1x00)(ty0)(sc12)(nx0)0x00000000__minor_subsystem_version__

 [d:\ dev的\testing]
 > g ++ bar.cpp wmain_support.cpp -mwindows

 [d:\ dev的\testing]
 > objdump -x a.exe |  find/我“子系统”
 MajorSubsystem版本4
 MinorSubsystemVersion 0
子系统00000002(Windows GUI)
 (秒-1)(f1x00)(ty0)(sc12)(nx0)0x00000004 __major_subsystem_version__
 (秒-1)(f1x00)(ty0)(sc12)(nx0)0x00000002 __subsystem__
 (秒-1)(f1x00)(ty0)(sc12)(nx0)0x00000000__minor_subsystem_version__

 [d:\ dev的\testing]
 > _

微软的工具链。

使用微软的工具链,如果没有指定入口点并且存在一个wmain函数,链接器会自动推断wmainCRTStartup入口点(目前还不清楚如果标准main也存在,会发生什么,我近几年没有检查过):

 [d:\ dev的\testing]
 > set link = / entry:mainCRTStartup

 [d:\ dev的\testing]
 > cl bar.cpp user32.lib
 bar.cpp
 LIBCMT.lib(crt0.obj):错误LNK2019:无法parsing的外部符号_main在函数中引用___tmainCRTStartup
 bar.exe:致命错误LNK1120:1无法parsing的外部

 [d:\ dev的\testing]
 > set link =

 [d:\ dev的\testing]
 > cl bar.cpp user32.lib
 bar.cpp

 [d:\ dev的\testing]
 > _

如果使用非标准的启动函数(如wmain ),则可能最好是明确指定入口点,以便非常明确地expression意图:

 [d:\ dev的\testing]
 > cl bar.cpp / link user32.lib / entry:wmainCRTStartup
 bar.cpp

 [d:\ dev的\testing]
 > dumpbin / headers bar.exe |  find/我“子系统”
             6.00子系统版本
                3子系统(Windows CUI)

 [d:\ dev的\testing]
 > cl bar.cpp / link user32.lib / entry:wmainCRTStartup / subsystem:windows
 bar.cpp

 [d:\ dev的\testing]
 > dumpbin / headers bar.exe |  find/我“子系统”
             6.00子系统版本
                2子系统(Windows GUI)

 [d:\ dev的\testing]
 > _

根据@RaymondChen

WinMain这个名字只是一个约定

虽然WinMain函数在Platform SDK中有logging,但它并不是平台的一部分。 而WinMain是用户提供的Windows程序入口点的传统名称。

真正的入口点在C运行时库中,它初始化运行时,运行全局构造函数,然后调用你的WinMain函数(或wWinMain,如果你喜欢Unicode入口点)。

DllMain和WinMain在原型本身是不同的。 WinMain接受命令行参数,而另一个则说明它是如何附着在进程上的。

根据MSDN文档

默认情况下,起始地址是C运行时库中的函数名称。 链接器根据程序的属性select它,如下表所示。

  • mainCRTStartup (或wmainCRTStartup )使用/SUBSYSTEM:CONSOLE;的应用程序/SUBSYSTEM:CONSOLE; 调用main(或wmain

  • WinMainCRTStartup (或wWinMainCRTStartup )使用/SUBSYSTEM:WINDOWS;的应用程序/SUBSYSTEM:WINDOWS; 调用必须用__stdcall定义的WinMain (或wWinMain

  • _DllMainCRTStartup一个DLL; 调用DllMain ,必须用__stdcall定义,如果存在的话

标准C程序在启动时由命令行传递2个参数:

 int main( int argc, char** argv ) ; 
  • char** argv是一个string数组( char*
  • int argc是argv中char*的个数

程序员必须为Windows程序编写的启动函数WinMain略有不同。 WinMain在启动时需要4个由Win O / S传递给程序的参数:

 int WINAPI WinMain( HINSTANCE hInstance, // HANDLE TO AN INSTANCE. This is the "handle" to YOUR PROGRAM ITSELF. HINSTANCE hPrevInstance,// USELESS on modern windows (totally ignore hPrevInstance) LPSTR szCmdLine, // Command line arguments. similar to argv in standard C programs int iCmdShow ) // Start window maximized, minimized, etc. 

查看我的文章如何在C中创build基本窗口以获取更多信息

我隐约可以回想起Windows程序有main()函数的地方。 它只是隐藏在某处的标题或库中。 我相信这个main()函数初始化WinMain()所需的所有variables,然后调用它。

当然,我是一个WinAPI noob,所以我希望如果我错了,那些更有知识的人会纠正我。