そうです、私はこの投稿を見ました:C ++でのWinMain、mainとDllMainの違い
私は今、それWINMAIN
がウィンドウアプリケーションとmain()
コンソールに使用されていることを知っています。しかし、投稿を読んでも、なぜ正確に違いがあるのかはわかりません。
プログラムを開始するために異なるメイン機能を分離することのポイントは何ですか?パフォーマンスの問題が原因ですか?またはそれは何ですか?
そうです、私はこの投稿を見ました: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[] )
さらに、結果タイプが。でなければならないことを除いて、可能な実装定義の署名(C++11§3.6.1/ 2)int
。
C ++のこのような関数main
は、デフォルトの結果値、つまり0のみを持っているためです。returnsの場合main
、通常の関数の後にreturnexit
が呼び出され、main
結果の値が引数になります。この標準では、使用が保証されている3つの値が定義されています。0(成功を示します)、EXIT_SUCCESS
(成功を示し、通常は0として定義されます)、およびEXIT_FAILURE
(失敗を示します)。2つの名前付き定数は、<stdlib.h>
ヘッダーによって定義され、exit
働き。
引数は、プロセスの開始に使用されるコマンドのコマンドライン引数main
を表すことを目的としています。(引数数)は、(引数値)配列内の項目の数です。これらのアイテムに加えて、0であることが保証されています。> 0の場合–これは保証されません。–次に、空の文字列へのポインタ、または「プログラムの呼び出しに使用される名前」へのポインタのいずれかであることが保証されます。この名前にはパスが含まれる場合があり、実行可能ファイルの名前である場合もあります。argc
argv
argv[argc]
argc
argv[0]
CおよびC++は*nixで作成されているため、引数を使用しmain
てコマンドライン引数を取得することは*nixで正常に機能します。ただし、引数のエンコードに関する事実上のWindows標準はWindows ANSIであり、一般的なWindowsファイル名(ノルウェー語のWindowsインストールの場合、ギリシャ語またはキリル文字のファイル名など)をサポートしていません。そのため、MicrosoftはCおよびC ++言語を、と呼ばれるWindows固有の起動関数で拡張することを選択しました。この関数には、任意のファイル名を表すことができるUTF-16としてエンコードされたワイド文字ベースの引数があります。main
wmain
wmain
関数は、次の標準シグニチャに対応するこれらのシグニチャの1つをmain
持つことができます。
int wmain()
int wmain( int argc, wchar_t* argv[] )
さらに、特に有用ではないものがいくつかあります。
つまり、wmain
の直接ワイド文字ベースの置換ですmain
。
ベースの関数は、1980年代初頭にWindowsで導入されました。WinMain
char
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
);
ここCALLBACK
で、、HINSTANCE
およびはヘッダーLPSTR
によって定義されます(はちょうど)。<windows.h>
LPSTR
char*
引数:
引数値は、hInstance
実行可能ファイルのメモリイメージのベースアドレスであり、主に実行可能ファイルからリソースをロードするために使用されます。あるいは、GetModuleHandle
API関数から取得することもできます。
hPrevInstance
引数は常に0 、
または、 API関数lpCmdLine
から引数を取得することもできGetCommandLine
ます。さらに、コマンドラインのプログラム名の部分をスキップするための奇妙なロジックを少し追加することもできます。
nCmdShow
引数値はAPI関数から取得することもできますがGetStartupInfo
、最近の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
に設定するか、そのオプションを直接指定します。これは、初期化後に標準main
関数を呼び出すMicrosoftランタイムライブラリのエントリポイント関数です。他のスタートアップ関数には、同じ体系的な方法で名前が付けられた対応するエントリポイント関数があります。
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プロセスには、関連付けられたコンソールウィンドウを含めることができます。最大で1つです。また、Windowsコマンドインタープリターは、コンソールプログラムプログラムが終了するのを待って、プログラムのテキスト表示が終了するようにします。
逆に、GUIサブシステムプログラムは、コンソールウィンドウを必要としないプログラムです。コマンドインタプリタは、バッチファイルを除いて、GUIサブシステムプログラムを待機しません。両方の種類のプログラムで完了待機を回避する1つの方法は、start
コマンドを使用することです。GUIサブシステムプログラムからコンソールウィンドウテキストを表示する1つの方法は、その標準出力ストリームをリダイレクトすることです。もう1つの方法は、プログラムのコードからコンソールウィンドウを明示的に作成することです。
プログラムのサブシステムは、実行可能ファイルのヘッダーにエンコードされています。dumpbin
Windows Explorerには表示されません(ただし、Windows 9xでは、Microsoftのツールとほぼ同じ情報を表示する実行可能ファイルを「クイックビュー」できます)。対応するC++の概念はありません。
main
GNUツールチェーンを使用します。[D:\ dev \ test] > g ++ foo.cpp [D:\ dev \ test] > objdump -x a.exe | /i"subsys"を検索 MajorSubsystemVersion 4 MinorSubsystemVersion 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:\ dev \ test] > g ++ foo.cpp -mwindows [D:\ dev \ test] > objdump -x a.exe | /i"subsys"を検索 MajorSubsystemVersion 4 MinorSubsystemVersion 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:\ 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ツールチェーンがその機能をサポートしており(おそらく非常に多くの古いコードがそれを使用しているため)、標準を見つけられなかった後の最後の手段としてそれを検索するため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 サブシステム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 サブシステム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] > set link = / entry:mainCRTStartup [D:\ dev \ test] > cl bar.cpp user32.lib bar.cpp LIBCMT.lib(crt0.obj):エラーLNK2019:関数___tmainCRTStartupで参照されている未解決の外部シンボル_main bar.exe:致命的なエラーLNK1120:1つの未解決の外部 [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 | /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関数はPlatformSDKに文書化されていますが、実際にはプラットフォームの一部ではありません。むしろ、WinMainは、Windowsプログラムへのユーザー提供のエントリポイントの従来の名前です。
実際のエントリポイントはCランタイムライブラリにあり、ランタイムを初期化し、グローバルコンストラクターを実行してから、WinMain関数(Unicodeエントリポイントが必要な場合はwWinMain)を呼び出します。
DllMainとWinMainは、プロトタイプ自体が異なります。WinMainはコマンドライン引数を受け入れ、もう1つはそれがプロセスにどのように関連付けられているかについて話します。
MSDNのドキュメントによる
デフォルトでは、開始アドレスはCランタイムライブラリの関数名です。次の表に示すように、リンカはプログラムの属性に従ってそれを選択します。
mainCRTStartup
(または)呼び出しmainwmainCRTStartup
を使用するアプリケーション
(または)/SUBSYSTEM:CONSOLE;
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
char*
argvの数ですWinMain
プログラマーがWindowsプログラム用に作成しなければならない 起動機能は少し異なります。WinMain
起動時にWinO/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 CRTは、5つのシンボル、、、、およびmainCRTStartup
を公開します。wmainCRTStartup
wWinMainCRTStartup
_DllMainCRTStartup
WinMainCRTStartup
beforeは、ユニコードバージョンを意味します。w
つまり、プロセスに渡される(そしてPPBに格納される)コマンドライン(コマンドと引数の文字列)は、ASCII(UTF-8またはANSIなど)ではなくPEB->ProcessParameters->CommandLine
UTF-16(つまりWCHAR
(ワイド文字))です。 Windows-1252、Windows-850など)
/DLL
orオプションが指定されていない場合、MSVCリンカはサブシステムとエントリポイントを選択します。つまり、オブジェクトファイルの1つのシンボルテーブルに、またはが存在/SUBSYSTEM
するかどうかに基づいて、エントリアドレスをこれら5つの関数の1つにします。エントリポイントは、を介してMSVCリンカーによって静的にリンクされます。これには、そのエントリ関数によって実際に使用されるシンボルのみがさらに含まれます。main
WinMain
DllMain
libcmt.lib
mainCRTStartup
PPBを呼び出すGetStartupInfo()
/アクセスして、stdin/outハンドルとコマンドライン引数を取得します。_init term()
と同様に、とも呼ばれ_init_atexit()
ます。呼び出します。設定されたルーチンが呼び出された後、main
の戻り値main
がに渡されます。ExitProcess()
atexit
コンパイラオプションを使用して作成された.pdbのデバッグシンボルは、や/Zi
などのシンボルを表示するためにIDA Proにロードする必要があります。これは、デバッグシンボルなしでは、それぞれととして表されます。mainCRTstartup
main
start
sub_??????
参照:
どこかでWindowsプログラムにmain()
機能があることを読んだことをぼんやりと覚えています。ヘッダーまたはライブラリのどこかに隠されているだけです。このmain()
関数は、に必要なすべての変数を初期化してWinMain()
から呼び出すと思います。
もちろん、私はWinAPIの初心者なので、私が間違っている場合は、より知識のある他の人が私を訂正してくれることを願っています。
_tWinMainとConfigurationProperties.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));
}
メインvsWinMain
私が多くのリンクから読んだように:
WinMain()
C
Windowsアプリケーションのエントリポイント関数です。エントリポイントとして機能する通常DOS/console
ベースのアプリケーションと同様に、Windowsでは代わりに使用できます。プロセスの作成中にシステムによって呼び出される関数です。main()
C
WinMain()
WinMain()
最初の引数は、現在のプロセスのインスタンスハンドルです。
次は前のインスタンスです。
コマンドライン引数は次の引数として提供されます。
最後に、シェルはメインウィンドウのshow/display属性を渡します。
注:WinMainは、成功をゼロとして返し、エラーをゼロ以外として返します。