C ++中的WINMAIN和main()(扩展) [英] WINMAIN and main() in C++ (Extended)

查看:122
本文介绍了C ++中的WINMAIN和main()(扩展)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对,我看了这篇文章: WinMain之间的差异, C ++中的main和DllMain

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

我的意思是将不同的主功能分开来启动程序有什么意义?是由于性能问题引起的吗?还是什么?

解决方案

关于功能.

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

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

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

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

main自变量旨在表示用于启动进程的命令的命令行自变量. argc(自变量计数)是argv(自变量值)数组中的项目数.除这些项目外,argv[argc]保证为0.不能保证! –然后确保argv[0]是指向空字符串的指针或指向用于调用程序的名称的指针.该名称可能包含路径,也可能是可执行文件的名称.

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

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

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

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

wmain是基于直接宽字符的main替代.

基于 WinMain char的功能是Windows于1980年代初期引入的:

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

其中CALLBACKHINSTANCELPSTR<windows.h>标头定义(

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

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

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

程序的子系统编码在可执行文件的标头中. Windows资源管理器未显示它(除了在Windows 9x中可以快速查看"可执行文件,该可执行文件现在提供的信息几乎与Microsoft的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与Microsoft的工具链:

[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 );
}

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工具链确实支持 that 函数(大概是因为很多古老的代码都在使用它),并在之后搜索它作为最后的手段.找不到标准的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 );
}

现在

[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的工具链.

使用Microsoft的工具链,如果未指定任何入口点并且存在wmain函数,则链接器会自动推断wmainCRTStartup入口点(尚不清楚如果还存在标准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]
> _

Right, I have looked at this post: Difference between WinMain,main and DllMain in C++

I now know that WINMAIN is used for window applications and main() for consoles. But reading the post doesn't really tell me why exactly what is the difference.

I mean what's the point of having separating different mains functions to start of a program? Is it due to performance issues? Or what is it?

解决方案

About the functions.

The C and C++ standards require any program (for a “hosted” C or C++ implementation) to have a function called main, which serves as the program's startup function. The main function is called after zero-initialization of non-local static variables, and possibly but not necessarily (!, C++11 §3.6.2/4) this call happens after dynamic initialization of such variables. It can have one of the following signatures:

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

plus possible implementation-defined signatures (C++11 §3.6.1/2) except that the result type must be int.

As the only such function in C++ main has a default result value, namely 0. If main returns then after the ordinary function return exit is called with the main result value as argument. The standard defines three values that guaranteed can be used: 0 (indicates success), EXIT_SUCCESS (also indicates success, and is typically defined as 0), and EXIT_FAILURE (indicates failure), where the two named constants are defined by the <stdlib.h> header which also declares the exit function.

The main arguments are intended to represent the command line arguments for the command used to start the process. argc (argument count) is the number of items in the argv (argument values) array. In addition to those items argv[argc] is guaranteed to be 0. If argc > 0 – which is not guaranteed! – then argv[0] is guaranteed to either be a pointer to an empty string, or a pointer to the “name used to invoke the program”. This name may include a path, and it may be the name of the executable.

Using the main arguments to obtain the command line arguments works fine in *nix, because C and C++ originated with *nix. However, the de facto Windows standard for the encoding of the main arguments is Windows ANSI, which does not support general Windows filenames (such as, for a Norwegian Windows installation, filenames with Greek or Cyrillic characters). Therefore Microsoft chose to extend the C and C++ languages with a Windows-specific startup function called wmain, which has wide character based arguments encoded as UTF-16, which can represent any filename.

The wmain function can have one of these signatures, corresponding to the standard signatures for main:

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

plus a few more that are not especially useful.

I.e., wmain is a direct wide character based replacement for main.

The WinMain char based function was introduced with Windows, in the early 1980's:

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

where CALLBACK, HINSTANCE and LPSTR are defined by the <windows.h> header (LPSTR is just char*).

Arguments:

  • the hInstance argument value is the base address of the memory image of the executable, it's primarily used to load resources from the executable, and it can alternatively be obtained from the GetModuleHandle API function,

  • the hPrevInstance argument is always 0,

  • the lpCmdLine argument can alternatively be obtained from the GetCommandLine API function, plus a bit of weird logic to skip the program name part of the command line, and

  • the nCmdShow argument value can alternatively be obtained from the GetStartupInfo API function, but with modern Windows the first creation of a top level window does that automatically so it's not of any practical use.

Thus, the WinMain function has the same drawbacks as standard main, plus some (in particular the verbosity and being non-standard), and no advantages of its own, so it's really inexplicable except possibly as a vendor lock-in thing. However, with the Microsoft tool chain it makes the linker default to the GUI subsystem, which some see as an advantage. But with e.g. the GNU toolchain it does not have such an effect so this effect cannot be relied on.

The wWinMain wchar_t based function is a wide character variant of WinMain, in the same way as wmain is a wide character variant of standard main:

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

where WINAPI is the same as CALLBACK, and PWSTR is simply wchar_t*.

There is no good reason to use any of the non-standard functions except the least known and least supported of them, namely wmain, and then just for convenience: that this avoids using the GetCommandLine and CommandLineToArgvW API functions to pick up UTF-16 encoded arguments.

To avoid the Microsoft linker acting up (the GNU toolchain's linker doesn't), just set the LINK environment variable to /entry:mainCRTStartup, or specify that option directly. This is the Microsoft runtime library entry point function that, after some initialization, calls the standard main function. The other startup functions have corresponding entry point functions named in the same systematic way.


Examples of using the standard main function.

Common source code:

    foo.cpp

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

int main()
{
    MessageBox( 0, L"Press OK", L"Hi", MB_SETFOREGROUND );
}

In the examples below (first with the GNU toolchain and then with the Microsoft toolchain) this program is first built as a console subsystem program, and then as a GUI subsystem program. A console subsystem program, or in short just a console program, is one that requires a console window. This is the default subsystem for all Windows linkers I've used (admittedly not a great many), possibly for all Windows linkers period.

For a console program Windows creates a console window automatically if needed. Any Windows process, regardless of subsystem, can have an associated console window, and at most one. Also, the Windows command interpreter waits for a console program program to finish, so that the program's text presentation has finished.

Conversely, a GUI subsystem program is one that doesn't require a console window. The command interpreter does not wait for a GUI subsystem program, except in batch files. One way to avoid the completion wait, for both kinds of program, is to use the start command. One way to present console window text from a GUI subsystem program is to redirect its standard output stream. Another way is to explicitly create a console window from the program's code.

The program's subsystem is encoded in the executable's header. It's not shown by Windows Explorer (except that in Windows 9x one could “quick view” an executable, which presented just about the same information as Microsoft's dumpbin tool now does). There is no corresponding C++ concept.

main with the GNU toolchain.

[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 with Microsoft's toolchain:

[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]
> _


Examples of using Microsoft’s wmain function.

The following main code is common to both the GNU toolchain and Microsoft toolchain demonstrations:

    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 );
}

wmain with the GNU toolchain.

The GNU toolchain doesn't support Microsoft's wmain function:

[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]
> _

The link error message here, about WinMain, is because the GNU toolchain does support that function (presumably because so much ancient code uses it), and searches for it as a last resort after failing to find a standard main.

However, it's trivial to add a module with a standard main that calls the 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 );
}

Now,

[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 with Microsoft’s toolchain.

With Microsoft's toolchain the linker automatically infers the wmainCRTStartup entry point if no entry point is specified and a wmain function is present (it's unclear what happens if a standard main is also present, I haven't checked that in recent years):

[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]
> _

With a non-standard startup function such as wmain it is, however, probably best to specify the entry point explicitly, so as to be very clear about the intention:

[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]
> _

这篇关于C ++中的WINMAIN和main()(扩展)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆