問題描述
對了,我看過這個帖子: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
返回則在普通函數返回之后exit
以 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.但是,main
參數編碼的事實上的Windows 標準是Windows ANSI,它不支持一般的Windows 文件名(例如,對于挪威語 Windows 安裝,文件名帶有希臘或西里爾字符).因此,Microsoft 選擇使用名為 wmain
的特定于 Windows 的啟動函數來擴展 C 和 C++ 語言,該函數具有編碼為 UTF-16 的基于寬字符的參數strong>,可以代表任何文件名.
wmain
函數可以有 其中一個簽名,對應于main
的標準簽名:
int wmain()int wmain( int argc, wchar_t* argv[] )
還有一些不是特別有用的.
即,wmain
是 main
的基于寬字符的直接替換.
WinMain
基于 char
的函數是在 1980 年代初期在 Windows 中引入的:
int 回調 WinMain(HINSTANCE 實例,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow);
其中 CALLBACK
、HINSTANCE
和 LPSTR
由
標頭(LPSTR
只是 char*
).
參數:
hInstance
參數值是可執行文件的內存映像的基地址,主要用于從可執行文件加載資源,也可以從中獲取GetModuleHandle
API 函數,hPrevInstance
參數始終為 0,lpCmdLine
參數也可以從GetCommandLine
API 函數中獲取,加上一些奇怪的邏輯來跳過命令行的程序名稱部分,和nCmdShow
參數值也可以從GetStartupInfo
API 函數中獲取,但在現代 Windows 中,頂級窗口的首次創建會自動執行此操作,因此它沒有任何實際用途.
因此,WinMain
函數與標準的 main
有相同的缺點,加上一些(特別是冗長和非標準),并且沒有自己的優點,所以它真的莫名其妙,除非可能是供應商鎖定的事情.然而,使用 Microsoft 工具鏈,它使鏈接器默認為 GUI 子系統,有些人認為這是一個優勢.但是,例如GNU 工具鏈它沒有這樣的效果,所以不能依賴這個效果.
wWinMain
基于 wchar_t
的函數是 WinMain
的寬字符變體,與 wmain
是標準 main
的寬字符變體:
int WINAPI wWinMain(HINSTANCE 實例,HINSTANCE hPrevInstance,PWSTR lpCmdLine,int nCmdShow);
其中 WINAPI
與 CALLBACK
相同,PWSTR
只是 wchar_t*
.
沒有充分的理由使用任何非標準函數,除了最不為人所知和最不支持的函數,即 wmain
,然后只是為了方便:這避免使用 GetCommandLine
和 CommandLineToArgvW
API 函數來獲取 UTF-16 編碼的參數.
為避免 Microsoft 鏈接器起作用(GNU 工具鏈的鏈接器不會),只需將 LINK
環境變量設置為 /entry:mainCRTStartup
,或指定該選項直接地.這是 Microsoft 運行時庫入口點函數,經過一些初始化后,它會調用標準的 main
函數.其他的啟動函數都有對應的入口函數,以同樣的系統方式命名.
使用標準main
函數的示例.
常用源代碼:
foo.cpp
#undef UNICODE#define UNICODE#include int main(){MessageBox( 0, L"按確定", L"嗨", MB_SETFOREGROUND );}
在下面的示例中(首先使用 GNU 工具鏈,然后使用 Microsoft 工具鏈),該程序首先構建為控制臺子系統程序,然后構建為GUI 子系統程序強>.控制臺子系統程序,或簡稱為控制臺程序,是需要控制臺窗口的程序.這是我使用過的所有 Windows 鏈接器的默認子系統(當然不是很多),可能適用于所有 Windows 鏈接器時期.
對于控制臺程序,Windows 會在需要時自動創建一個控制臺窗口.任何 Windows 進程,不管子系統如何,都可以有一個關聯的控制臺窗口,最多一個.此外,Windows 命令解釋器等待控制臺程序完成,以便程序的文本呈現完成.
相反,GUI 子系統程序不需要控制臺窗口.命令解釋器不等待 GUI 子系統程序,批處理文件除外.對于這兩種程序,避免完成等待的一種方法是使用 start
命令.從 GUI 子系統程序顯示控制臺窗口文本的一種方法是重定向其標準輸出流.另一種方法是從程序代碼中顯式地創建一個控制臺窗口.
程序的子系統編碼在可執行文件的標頭中.它不會在 Windows 資源管理器中顯示(除了在 Windows 9x 中可以快速查看"一個可執行文件,它顯示的信息與 Microsoft 的 dumpbin
工具現在顯示的信息幾乎相同).沒有對應的C++概念.
main
使用 GNU 工具鏈.
<前>[D:開發測試]> g++ foo.cpp[D:開發測試]> objdump -x a.exe |找到/i "subsys"主要子系統版本 4次要子系統版本 0子系統 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:開發測試]> g++ foo.cpp -mwindows[D:開發測試]> objdump -x a.exe |找到/i "subsys"主要子系統版本 4次要子系統版本 0子系統 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:開發測試]> _main
與微軟的工具鏈:
<前>[D:開發測試]> 設置 LINK=/entry:mainCRTStartup[D:開發測試]> cl foo.cpp user32.lib文件[D:開發測試]> dumpbin/headers foo.exe |找到/i "subsys"6.00 子系統版本3 子系統(Windows CUI)[D:開發測試]> cl foo.cpp/link user32.lib/subsystem:windows文件[D:開發測試]> dumpbin/headers foo.exe |找到/i "subsys"6.00 子系統版本2 子系統(Windows GUI)[D:開發測試]> _<小時>
使用 Microsoft wmain
函數的示例.
以下主要代碼對于 GNU 工具鏈和 Microsoft 工具鏈演示是通用的:
bar.cpp
#undef UNICODE#define UNICODE#include #include <字符串>//std::wstring#include //std::wostringstream使用命名空間標準;int wmain( int argc, wchar_t* argv[] ){wostringstream 文本;文本<
wmain
使用 GNU 工具鏈.
GNU 工具鏈不支持微軟的 wmain
功能:
這里的鏈接錯誤信息,關于WinMain
,是因為GNU工具鏈確實支持那個函數(大概是因為太多古老的代碼使用它),并搜索它作為未能找到標準 main
后的最后手段.
然而,添加一個具有標準 main
的模塊是微不足道的,該模塊調用 wmain
:
wmain_support.cpp
extern int wmain( int, wchar_t** );#undef UNICODE#define UNICODE#include //GetCommandLine, CommandLineToArgvW, LocalFree#include //退出失敗int main(){結構參數{國際n;wchar_t** p;~Args() { if( p != 0 ) { ::LocalFree( p );} }Args(): p( ::CommandLineToArgvW( ::GetCommandLine(), &n ) ) {}};args args;如果( args.p == 0 ){返回 EXIT_FAILURE;}返回 wmain( args.n, args.p );}
現在,
<前>[D:開發測試]> g++ bar.cpp wmain_support.cpp[D:開發測試]> objdump -x a.exe |找到/i子系統"主要子系統版本 4次要子系統版本 0子系統 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:開發測試]> g++ bar.cpp wmain_support.cpp -mwindows[D:開發測試]> objdump -x a.exe |找到/i子系統"主要子系統版本 4次要子系統版本 0子系統 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:開發測試]> _wmain
使用 Microsoft 的工具鏈.
如果沒有指定入口點并且存在 wmain
函數(不清楚如果標準 main
也存在,近年來我沒有檢查過):
對于諸如 wmain
之類的非標準啟動函數,最好明確指定入口點,以便非常清楚其意圖:
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 theGetModuleHandle
API function,the
hPrevInstance
argument is always 0,the
lpCmdLine
argument can alternatively be obtained from theGetCommandLine
API function, plus a bit of weird logic to skip the program name part of the command line, andthe
nCmdShow
argument value can alternatively be obtained from theGetStartupInfo
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 est] > g++ foo.cpp [D:dev est] > 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 est] > g++ foo.cpp -mwindows [D:dev est] > 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 est] > _
main
with Microsoft's toolchain:
[D:dev est] > set LINK=/entry:mainCRTStartup [D:dev est] > cl foo.cpp user32.lib foo.cpp [D:dev est] > dumpbin /headers foo.exe | find /i "subsys" 6.00 subsystem version 3 subsystem (Windows CUI) [D:dev est] > cl foo.cpp /link user32.lib /subsystem:windows foo.cpp [D:dev est] > dumpbin /headers foo.exe | find /i "subsys" 6.00 subsystem version 2 subsystem (Windows GUI) [D:dev est] > _
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:
";
for( int i = 1; i < argc; ++i )
{
text << "
[" << 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 est] > 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 est] > _
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 est] > g++ bar.cpp wmain_support.cpp [D:dev est] > 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 est] > g++ bar.cpp wmain_support.cpp -mwindows [D:dev est] > 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 est] > _
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 est] > set link=/entry:mainCRTStartup [D:dev est] > 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 est] > set link= [D:dev est] > cl bar.cpp user32.lib bar.cpp [D:dev est] > _
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 est] > cl bar.cpp /link user32.lib /entry:wmainCRTStartup bar.cpp [D:dev est] > dumpbin /headers bar.exe | find /i "subsystem" 6.00 subsystem version 3 subsystem (Windows CUI) [D:dev est] > cl bar.cpp /link user32.lib /entry:wmainCRTStartup /subsystem:windows bar.cpp [D:dev est] > dumpbin /headers bar.exe | find /i "subsystem" 6.00 subsystem version 2 subsystem (Windows GUI) [D:dev est] > _
這篇關于C++ 中的 WINMAIN 和 main()(擴展)的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!