WINMAIN和C++中的main()(扩展)

Dan*_*nny 54 c++ windows program-entry-point winmain

是的,我看过这篇文章:WinMain,main和Cll中的DllMain之间的区别

我现在知道它WINMAIN用于窗口应用程序和main()控制台.但阅读这篇文章并没有真正告诉我为什么到底有什么区别.

我的意思是将不同的电源功能分离到程序的开始是什么意思?是因为性能问题?或者它是什么?

Che*_*Alf 166

关于功能.

C和C++标准要求任何程序(用于"托管"C或C++实现)具有一个被调用的函数main,该函数用作程序的启动函数.在非局部静态变量的零初始化main之后调用该函数,并且可能但不一定(!,C++11§3.6.2/ 4)此调用在这些变量的动态初始化之后发生.它可以具有以下签名之一:

int main()
int main( int argc, char* argv[] )
Run Code Online (Sandbox Code Playgroud)

加上可能的实现定义的签名(C++11§3.6.1/ 2),但结果类型必须是int.

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

main参数意在表示所述命令行参数用于启动过程中的命令.argc(参数计数)是argv(参数值)数组中的项数.除了这些项目argv[argc]保证是0.如果argc> 0 - 这是不保证!- 然后argv[0]保证要么是指向空字符串的指针,要么是指向"用于调用程序的名称"的指针.此名称可能包含路径,它可能是可执行文件的名称.

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

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

int wmain()
int wmain( int argc, wchar_t* argv[] )
Run Code Online (Sandbox Code Playgroud)

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

即,wmain是基于直接广泛字符的替代品main.

该基于函数是在Windows介绍,在80年代初:WinMain char

int CALLBACK WinMain(
    HINSTANCE   hInstance,
    HINSTANCE   hPrevInstance,
    LPSTR       lpCmdLine,
    int         nCmdShow
    );
Run Code Online (Sandbox Code Playgroud)

其中CALLBACK,HINSTANCELPSTR由所述被定义<windows.h>报头(LPSTR只是char*).

参数:

  • hInstance参数值是可执行的存储器图像的基地址,它主要用于从可执行加载资源,并且它可以替代地从所获得的GetModuleHandleAPI函数,

  • hPrevInstance参数始终为0,

  • 所述lpCmdLine参数可以替代地从所获得的GetCommandLineAPI函数,加上有点怪异逻辑的跳过命令行的程序名的一部分,和

  • nCmdShow参数值或者可从所获得的GetStartupInfoAPI函数,但与现代的Windows一个顶层窗口的第一个创造不自动因此它的任何实际用途的不是.

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

的基于功能是一个宽字符变体,以相同的方式作为是标准的宽字符变体:wWinMain wchar_tWinMainwmainmain

int WINAPI wWinMain(
    HINSTANCE   hInstance,
    HINSTANCE   hPrevInstance,
    PWSTR       lpCmdLine,
    int         nCmdShow
    );
Run Code Online (Sandbox Code Playgroud)

在哪里WINAPI是一样的CALLBACK,而且PWSTR很简单wchar_t*.

没有充分的理由使用任何非标准函数,除了最不知道和最不受支持的函数,即wmain,为了方便起见:这避免了使用GetCommandLineCommandLineToArgvWAPI函数来获取UTF-16编码的参数.

要避免Microsoft链接器执行(GNU工具链的链接器没有),只需将LINK环境变量设置为/entry:mainCRTStartup,或直接指定该选项.这是Microsoft运行时库入口点函数,在初始化之后调用标准main函数.其他启动函数具有以相同系统方式命名的相应入口点函数.


使用标准main功能的示例.

常见源代码:

    Foo.cpp中

#undef UNICODE
#define UNICODE
#include <windows.h>

int main()
{
    MessageBox( 0, L"Press OK", L"Hi", MB_SETFOREGROUND );
}
Run Code Online (Sandbox Code Playgroud)

在下面的示例中(首先使用GNU工具链,然后使用Microsoft工具链),该程序首先构建为控制台子系统程序,然后作为GUI子系统程序构建.控制台子系统程序,或简称为控制台程序,是需要控制台窗口的程序.这是我使用的所有Windows链接器的默认子系统(诚然不是很多),可能适用于所有Windows链接器期间.

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

相反,GUI子系统程序是不需要控制台窗口的程序.除批处理文件外,命令解释程序不等待GUI子系统程序.对于两种程序,避免完成等待的一种方法是使用该start命令.从GUI子系统程序呈现控制台窗口文本的一种方法是重定向其标准输出流.另一种方法是从程序代码中显式创建一个控制台窗口.

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

main 使用GNU工具链.

[D:\dev\test]
> g++ foo.cpp

[D:\dev\test]
> objdump -x a.exe | find /i "subsys"
MajorSubsystemVersion   4
MinorSubsystemVersion   0
Subsystem               00000003        (Windows CUI)
[544](sec -1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000004 __major_subsystem_version__
[612](sec -1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000003 __subsystem__
[636](sec -1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000000 __minor_subsystem_version__

[D:\dev\test]
> g++ foo.cpp -mwindows

[D:\dev\test]
> objdump -x a.exe | find /i "subsys"
MajorSubsystemVersion   4
MinorSubsystemVersion   0
Subsystem               00000002        (Windows GUI)
[544](sec -1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000004 __major_subsystem_version__
[612](sec -1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000002 __subsystem__
[636](sec -1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000000 __minor_subsystem_version__

[D:\dev\test]
> _

main 使用微软的工具链:

[D:\dev\test]
> set LINK=/entry:mainCRTStartup

[D:\dev\test]
> cl foo.cpp user32.lib
foo.cpp

[D:\dev\test]
> dumpbin /headers foo.exe | find /i "subsys"
            6.00 subsystem version
               3 subsystem (Windows CUI)

[D:\dev\test]
> cl foo.cpp /link user32.lib /subsystem:windows
foo.cpp

[D:\dev\test]
> dumpbin /headers foo.exe | find /i "subsys"
            6.00 subsystem version
               2 subsystem (Windows GUI)

[D:\dev\test]
> _

使用Microsoft的wmain功能的示例.

以下主要代码对于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 );
}
Run Code Online (Sandbox Code Playgroud)

wmain 使用GNU工具链.

GNU工具链不支持Microsoft的wmain功能:

[D:\dev\test]
> g++ bar.cpp
d:/bin/mingw/bin/../lib/gcc/i686-pc-mingw32/4.7.1/../../../libmingw32.a(main.o):main.c:(.text.startup+0xa3): undefined reference to `WinMain
@16'
collect2.exe: error: ld returned 1 exit status

[D:\dev\test]
> _

这里的链接错误消息WinMain是因为GNU工具链确实支持功能(可能是因为很多古代代码使用它),并且在未能找到标准之后将其作为最后的手段进行搜索main.

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

    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 );
}
Run Code Online (Sandbox Code Playgroud)

现在,

[D:\dev\test]
> g++ bar.cpp wmain_support.cpp

[D:\dev\test]
> objdump -x a.exe | find /i "subsystem"
MajorSubsystemVersion   4
MinorSubsystemVersion   0
Subsystem               00000003        (Windows CUI)
[13134](sec -1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000004 __major_subsystem_version__
[13576](sec -1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000003 __subsystem__
[13689](sec -1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000000 __minor_subsystem_version__

[D:\dev\test]
> g++ bar.cpp wmain_support.cpp -mwindows

[D:\dev\test]
> objdump -x a.exe | find /i "subsystem"
MajorSubsystemVersion   4
MinorSubsystemVersion   0
Subsystem               00000002        (Windows GUI)
[13134](sec -1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000004 __major_subsystem_version__
[13576](sec -1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000002 __subsystem__
[13689](sec -1)(fl 0x00)(ty   0)(scl   2) (nx 0) 0x00000000 __minor_subsystem_version__

[D:\dev\test]
> _

wmain 使用微软的工具链.

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

[D:\dev\test]
> set link=/entry:mainCRTStartup

[D:\dev\test]
> cl bar.cpp user32.lib
bar.cpp
LIBCMT.lib(crt0.obj) : error LNK2019: unresolved external symbol _main referenced in function ___tmainCRTStartup
bar.exe : fatal error LNK1120: 1 unresolved externals

[D:\dev\test]
> set link=

[D:\dev\test]
> cl bar.cpp user32.lib
bar.cpp

[D:\dev\test]
> _

但是,使用非标准的启动功能wmain,最好明确指定入口点,以便明确意图:

[D:\dev\test]
> cl bar.cpp /link user32.lib /entry:wmainCRTStartup
bar.cpp

[D:\dev\test]
> dumpbin /headers bar.exe | find /i "subsystem"
            6.00 subsystem version
               3 subsystem (Windows CUI)

[D:\dev\test]
> cl bar.cpp /link user32.lib /entry:wmainCRTStartup /subsystem:windows
bar.cpp

[D:\dev\test]
> dumpbin /headers bar.exe | find /i "subsystem"
            6.00 subsystem version
               2 subsystem (Windows GUI)

[D:\dev\test]
> _

  • @jvriesem:“WinMain”只有一个有效签名,“wWinMain”也是如此。但是,“wmain”与标准“main”类似,只是具有宽字符串参数。这意味着它支持像标准 `main` 一样的 `int wmain()`、`int wmain( int, wchar_t** )` 和 `int wmain( int, wchar_t**, wchar** )` 作为扩展。3ʳᵈ 参数通常称为“envp”,是指向环境变量字符串的指针,其中根据 [当前 Microsoft 文档](https://docs.microsoft.com/en-us/)“此数组由 NULL 条目终止” cpp/cpp/main-function-command-line-args?view=vs-2019)。现在愚蠢的SO拒绝更多的文字,所以。 (2认同)

sar*_*rat 8

据@RaymondChen说

WinMain这个名字只是一个惯例

尽管在Platform SDK中记录了WinMain函数,但它并不是平台的一部分.相反,WinMain是用户提供的Windows程序入口点的常规名称.

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

DllMain和WinMain的原型本身不同.WinMain接受命令行参数,而另一个讨论它是如何附加到进程的.

根据MSDN文档

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

  • mainCRTStartup(或wmainCRTStartup)使用/SUBSYSTEM:CONSOLE;主要(或wmain)呼叫的应用程序

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

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