72

そうです、私はこの投稿を見ました:C ++でのWinMain、mainとDllMainの違い

私は今、それWINMAINがウィンドウアプリケーションとmain()コンソールに使用されていることを知っています。しかし、投稿を読んでも、なぜ正確に違いがあるのか​​はわかりません。

プログラムを開始するために異なるメイン機能を分離することのポイントは何ですか?パフォーマンスの問題が原因ですか?またはそれは何ですか?

4

7 に答える 7

200

機能について。

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の場合–これは保証されません。–次に、空の文字列へのポインタ、または「プログラムの呼び出しに使用される名前」へのポインタのいずれかであることが保証されます。この名前にはパスが含まれる場合があり、実行可能ファイルの名前である場合もあります。argcargvargv[argc]argcargv[0]

CおよびC++は*nixで作成されているため、引数を使用しmainてコマンドライン引数を取得することは*nixで正常に機能します。ただし、引数のエンコードに関する事実上のWindows標準はWindows ANSIであり、一般的なWindowsファイル名(ノルウェー語のWindowsインストールの場合、ギリシャ語またはキリル文字のファイル名など)をサポートしていません。そのため、MicrosoftはCおよびC ++言語を、と呼ばれるWindows固有の起動関数で拡張することを選択しました。この関数には、任意のファイル名を表すことができるUTF-16としてエンコードされたワイド文字ベースの引数があります。mainwmain

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>LPSTRchar*

引数:

  • 引数値は、hInstance実行可能ファイルのメモリイメージのベースアドレスであり、主に実行可能ファイルからリソースをロードするために使用されます。あるいは、GetModuleHandleAPI関数から取得することもできます。

  • hPrevInstance引数は常に0 、

  • または、 API関数lpCmdLineから引数を取得することもできGetCommandLineます。さらに、コマンドラインのプログラム名の部分をスキップするための奇妙なロジックを少し追加することもできます。

  • nCmdShow引数値はAPI関数から取得することもできますがGetStartupInfo、最近のWindowsでは、トップレベルウィンドウを最初に作成すると自動的に取得されるため、実用的ではありません。

したがって、このWinMain関数には標準と同じ欠点がありmain、さらにいくつか(特に冗長性と非標準)があり、独自の利点がないため、ベンダーロックインとしての場合を除いて、実際には説明できません。ただし、Microsoftツールチェーンを使用すると、リンカーがデフォルトでGUIサブシステムになります。これは、利点と見なされるものもあります。しかし、例えばGNUツールチェーンではそのような効果はないので、この効果は信頼できません。

ベース関数は、標準のワイド文字バリアントと同じように、のワイド文字バリアントです。wWinMain wchar_tWinMainwmainmain

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

ここWINAPIで、はと同じCALLBACKで、PWSTRは単にwchar_t*です。

非標準関数のいずれかを使用する正当な理由はありませんが、それらの中で最も知られていない、最もサポートされていない、つまりwmain、そして単に便宜上です。これにより、GetCommandLineおよびCommandLineToArgvWAPI関数を使用して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つの方法は、プログラムのコードからコンソールウィンドウを明示的に作成することです。

プログラムのサブシステムは、実行可能ファイルのヘッダーにエンコードされています。dumpbinWindows Explorerには表示されません(ただし、Windows 9xでは、Microsoftのツールとほぼ同じ情報を表示する実行可能ファイルを「クイックビュー」できます)。対応するC++の概念はありません。

mainGNUツールチェーンを使用します。

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

mainMicrosoftのツールチェーンを使用:

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

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

wmainGNUツールチェーンを使用します。

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

wmainMicrosoftのツールチェーンを使用します。

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]
> _
于 2012-12-14T03:25:39.677 に答える
11

@RaymondChenによると

WinMainという名前は単なる慣例です

WinMain関数はPlatformSDKに文書化されていますが、実際にはプラットフォームの一部ではありません。むしろ、WinMainは、Windowsプログラムへのユーザー提供のエントリポイントの従来の名前です。

実際のエントリポイントはCランタイムライブラリにあり、ランタイムを初期化し、グローバルコンストラクターを実行してから、WinMain関数(Unicodeエントリポイントが必要な場合はwWinMain)を呼び出します。

DllMainとWinMainは、プロトタイプ自体が異なります。WinMainはコマンドライン引数を受け入れ、もう1つはそれがプロセスにどのように関連付けられているかについて話します。

MSDNのドキュメントによる

デフォルトでは、開始アドレスはCランタイムライブラリの関数名です。次の表に示すように、リンカはプログラムの属性に従ってそれを選択します。

  • mainCRTStartup(または)呼び出しmainwmainCRTStartupを使用するアプリケーション (または)/SUBSYSTEM:CONSOLE;wmain

  • WinMainCRTStartup(または)呼び出し(または)wWinMainCRTStartupを使用するアプリケーション 。これは次のように定義する必要があります。/SUBSYSTEM:WINDOWS;WinMainwWinMain__stdcall

  • _DllMainCRTStartupDLL; を呼び出します。存在する場合は、DllMainで定義する必要があります__stdcall

于 2012-12-14T03:37:21.153 に答える
3

標準のCプログラムには、起動時にコマンドラインから2つのパラメーターが渡されます。

int main( int argc, char** argv ) ;
  • char** argv文字列の配列です(char*
  • int argcchar*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で基本的なウィンドウを作成する方法」を参照してください。

于 2012-12-14T02:03:31.357 に答える
2

Windows CRTは、5つのシンボル、、、、およびmainCRTStartupを公開します。wmainCRTStartupwWinMainCRTStartup_DllMainCRTStartupWinMainCRTStartup

ここに画像の説明を入力してください

beforeは、ユニコードバージョンを意味します。wつまり、プロセスに渡される(そしてPPBに格納される)コマンドライン(コマンドと引数の文字列)は、ASCII(UTF-8またはANSIなど)ではなくPEB->ProcessParameters->CommandLineUTF-16(つまりWCHARワイド文字))です。 Windows-1252、Windows-850など)

/DLLorオプションが指定されていない場合、MSVCリンカはサブシステムとエントリポイントを選択します。つまり、オブジェクトファイルの1つのシンボルテーブルに、またはが存在/SUBSYSTEMするかどうかに基づいて、エントリアドレスをこれら5つの関数の1つにします。エントリポイントは、を介してMSVCリンカーによって静的にリンクされます。これには、そのエントリ関数によって実際に使用されるシンボルのみがさらに含まれます。mainWinMainDllMainlibcmt.lib

mainCRTStartupPPBを呼び出すGetStartupInfo()/アクセスして、stdin/outハンドルとコマンドライン引数を取得します。_init term()と同様に、とも呼ばれ_init_atexit()ます。呼び出します。設定されたルーチンが呼び出された後、mainの戻り値mainがに渡されます。ExitProcess()atexit

コンパイラオプションを使用して作成された.pdbのデバッグシンボルは、や/Ziなどのシンボルを表示するためにIDA Proにロードする必要があります。これは、デバッグシンボルなしでは、それぞれととして表されます。mainCRTstartupmainstartsub_??????

参照:

于 2020-04-07T00:50:39.727 に答える
1

どこかでWindowsプログラムにmain()機能があることを読んだことをぼんやりと覚えています。ヘッダーまたはライブラリのどこかに隠されているだけです。このmain()関数は、に必要なすべての変数を初期化してWinMain()から呼び出すと思います。

もちろん、私はWinAPIの初心者なので、私が間違っている場合は、より知識のある他の人が私を訂正してくれることを願っています。

于 2012-12-14T02:09:45.863 に答える
0

_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));
}
于 2018-12-11T20:40:30.467 に答える
0

メインvsWinMain

私が多くのリンクから読んだように:

WinMain()CWindowsアプリケーションのエントリポイント関数です。エントリポイントとして機能する通常DOS/consoleベースのアプリケーションと同様に、Windowsでは代わりに使用できます。プロセスの作成中にシステムによって呼び出される関数です。main()CWinMain()WinMain()

最初の引数は、現在のプロセスのインスタンスハンドルです。

次は前のインスタンスです。

コマンドライン引数は次の引数として提供されます。

最後に、シェルはメインウィンドウのshow/display属性を渡します。

注:WinMainは、成功をゼロとして返し、エラーをゼロ以外として返します。

于 2020-01-08T10:10:35.213 に答える