右、私はこの投稿を見ました: C++でのWinMain、mainとDllMainの違い
WINMAIN
がウィンドウアプリケーションに、main()
がコンソールに使用されていることがわかりました。しかし、この投稿を読んでも、違いが何であるかを正確に説明することはできません。
プログラムを開始するために異なる主電源機能を分離する意味は何ですか?パフォーマンスの問題が原因ですか?それとも何ですか?
CおよびC++標準では、(「ホストされた」CまたはC++実装の)すべてのプログラムにmain
という関数が必要です。これは、プログラムのスタートアップ関数として機能します。 main
関数は、非ローカル静的変数のゼロ初期化の後に呼び出されます。必ずしもそうではありませんが(!、C++ 11§3.6.2/ 4)、この呼び出しは動的初期化の後に行われますそのような変数。次の署名のいずれかを使用できます。
int main()
int main( int argc, char* argv[] )
さらに、結果の型がint
でなければならないことを除いて、可能な実装定義の署名(C++ 11§3.6.1/ 2)。
C++のそのような関数はmain
にのみデフォルトの結果値、つまり0を持っているため、main
が戻った場合、通常の関数の戻り値exit
が結果としてmain
を引数として呼び出します。規格では、使用できることが保証されている3つの値を定義しています:0(成功を示す)、EXIT_SUCCESS
(成功も示し、通常0と定義されます)、およびEXIT_FAILURE
( 2つの名前付き定数は、exit
関数も宣言する<stdlib.h>
ヘッダーによって定義されます。
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としてエンコードされるワイド文字ベースの引数を持ちます。
wmain
関数には、main
の標準署名に対応する これらの署名の1つ を含めることができます。
int wmain()
int wmain( int argc, wchar_t* argv[] )
さらに、特に有用ではないものがいくつかあります。
つまり、wmain
は、main
の直接のワイド文字ベースの置換です。
WinMain
char
ベースの関数は、1980年代初期にWindowsで導入されました。
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
);
ここで、CALLBACK
、HINSTANCE
、およびLPSTR
は、<windows.h>
ヘッダーによって定義されます(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,
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 <windows.h>
int main()
{
MessageBox( 0, L"Press OK", L"Hi", MB_SETFOREGROUND );
}
以下の例では(最初にGNUツールチェーンで、次にMicrosoftツールチェーンで)、このプログラムは最初にコンソールサブシステムプログラムとして構築され、次にGUIサブシステムプログラムとして構築されます。コンソールサブシステムプログラム、または要するにコンソールプログラムは、コンソールウィンドウを必要とするもので、これは私が使用したすべてのWindowsリンカーのデフォルトのサブシステムです(確かにそれほど多くない)。すべてのWindowsリンカー期間。
コンソールプログラムの場合、Windowsは必要に応じてコンソールウィンドウを自動的に作成します。サブシステムに関係なく、すべてのWindowsプロセスは、関連付けられたコンソールウィンドウを持つことができます。また、Windowsコマンドインタープリターは、コンソールプログラムプログラムが終了するのを待って、プログラムのテキスト表示が終了します。
逆に、GUIサブシステムプログラムは、コンソールウィンドウを必要としないものです。コマンドインタープリターは、バッチファイルを除き、GUIサブシステムプログラムを待機しません。両方の種類のプログラムで完了待機を回避する1つの方法は、start
コマンドを使用することです。 GUIサブシステムプログラムからコンソールウィンドウテキストを表示する1つの方法は、その標準出力ストリームをリダイレクトすることです。別の方法は、プログラムのコードからコンソールウィンドウを明示的に作成することです。
プログラムのサブシステムは、実行可能ファイルのヘッダーにエンコードされます。 Windowsエクスプローラーには表示されません(Windows 9xでは、実行可能ファイルを「クイックビュー」できることを除き、Microsoftのdumpbin
ツールとほぼ同じ情報が表示されます)。対応するC++の概念はありません。
main
とGNUツールチェーン。[D:\ dev\test] > g ++ foo.cpp [D:\ dev\test] > objdump -x a.exe |/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 |/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] > lINK =/entry:mainCRTStartupを設定します [D:\ dev\test] > cl foo.cpp user32.lib foo.cpp [D:\ dev\test] > dumpbin/headers foo.exe |/i "subsys"を見つけます 6.00サブシステムバージョン 3サブシステム(Windows CUI) [D:\ dev\test] > cl foo.cpp/link user32.lib/subsystem:windows foo.cpp [D:\ dev\test] > dumpbin/headers foo.exe |/i "subsys"を見つけます 6.00サブシステムバージョン 2サブシステム(Windows GUI) [D:\ dev\test] > _
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): `WinMain @ 16 ' collect2.exeへの未定義の参照:エラー:ldが1つの終了ステータス [.____を返しました。 ] [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 |/i "サブシステム"を見つける 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 |/i "サブシステム"を見つける 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のツールチェーンを使用すると、エントリポイントが指定されておらずwmainCRTStartup
関数が存在する場合、リンカーはwmain
エントリポイントを自動的に推測します(標準のmain
も存在する場合はどうなるかは不明です。最近チェックしていません)。
[D:\ dev\test] > 設定link =/entry:mainCRTStartup [D:\ dev\test] > cl bar.cpp user32.lib bar.cpp LIBCMT.lib(crt0.obj):エラーLNK2019:関数___ tmainCRTStartup bar.exeで参照されている未解決の外部シンボル_main:致命的なエラー.____。] [D:\ dev\test] > リンクを設定= [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 |/i "サブシステム"を見つける 6.00サブシステムバージョン 3サブシステム(Windows CUI) [D:\ dev\test] > cl bar.cpp/link user32.lib/entry:wmainCRTStartup/subsystem:windows bar.cpp [D:\ dev\test] > dumpbin/headers bar.exe |/i "サブシステム"を見つける 6.00サブシステムバージョン 2サブシステム(Windows GUI) [D:\ dev\test] > _
@RaymondChenによると
関数WinMainはPlatform SDKに文書化されていますが、実際にはプラットフォームの一部ではありません。むしろ、WinMainは、Windowsプログラムへのユーザー提供のエントリポイントの従来の名前です。
実際のエントリポイントはCランタイムライブラリにあり、ランタイムを初期化し、グローバルコンストラクターを実行してから、WinMain関数(またはUnicodeエントリポイントが必要な場合はwWinMain)を呼び出します。
DllMainとWinMainは、プロトタイプ自体が異なります。 WinMainはコマンドライン引数を受け入れますが、他の引数はプロセスにどのように接続されているかについて話します。
デフォルトでは、開始アドレスはCランタイムライブラリの関数名です。次の表に示すように、リンカはプログラムの属性に従ってそれを選択します。
mainCRTStartup
(またはwmainCRTStartup
)_/SUBSYSTEM:CONSOLE;
_を使用するアプリケーションはmain(またはwmain
)を呼び出します
WinMainCRTStartup
(またはwWinMainCRTStartup
)_/SUBSYSTEM:WINDOWS;
_を使用するアプリケーションは、WinMain
(またはwWinMain
)を呼び出します。これは___stdcall
_で定義する必要があります
__DllMainCRTStartup
_ DLL; DllMain
を呼び出します。これは、___stdcall
_で定義する必要があります(存在する場合)
標準Cプログラムには、起動時にコマンドラインから2つのパラメーターが渡されます。
int main( int argc, char** argv ) ;
char** argv
は文字列の配列です(char*
)int argc
はargvのchar*
の数ですプログラマーがWindowsプログラム用に作成する必要があるブート関数WinMain
はわずかに異なります。 WinMain
は、起動時にWin O/Sによってプログラムに渡される4つのパラメーターを取ります。
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で基本的なウィンドウを作成する方法 詳細
Windowsプログラムにはmain()
関数があることをどこかで読んだことを漠然と思い出します。ヘッダーまたはライブラリのどこかに隠されているだけです。このmain()
関数は、WinMain()
が必要とするすべての変数を初期化してから呼び出します。
もちろん、私はWinAPI初心者なので、私が間違っていれば、より知識のある人が私を修正してくれることを願っています。
_tWinMainとConfiguration Properties.Linker.System.Subsystem:Windows(/ SUBSYSTEM:WINDOWS)を使用したexeがありました。後でコマンドライン引数をサポートし、コンソールに出力したかったので、次を追加しました:
// We need to printf to stdout and we don't have one, so get one
AllocConsole();
// Redirect pre-opened STDOUT to the console
freopen_s((FILE **)stdout, "CONOUT$", "w", stdout);
しかし、それは消えた別のコンソールウィンドウで印刷することによってのみ機能したため、それほど役に立ちませんでした。以下は、必要に応じて前後に移動できるようにコンソール(/ SUBSYSTEM:CONSOLE)で動作するように変更した方法です。
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(argv);
UNREFERENCED_PARAMETER(envp);
return (_tWinMain(NULL, NULL, ::GetCommandLineW(), 0));
}