developer tip

C ++ (확장)의 WINMAIN 및 main ()

copycodes 2021. 1. 9. 10:11
반응형

C ++ (확장)의 WINMAIN 및 main ()


맞아요,이 게시물을 보았습니다 : C ++에서 WinMain, main 및 DllMain의 차이점

나는 이제 그것이 WINMAIN창 응용 프로그램과 main()콘솔에 사용 된다는 것을 알고 있습니다. 그러나 게시물을 읽는 것은 그 차이가 정확히 무엇인지 정확히 알려주지 않습니다.

프로그램을 시작하기 위해 서로 다른 주전원 기능을 분리하는 이유는 무엇입니까? 성능 문제 때문입니까? 아니면 무엇입니까?


기능에 대해.

C 및 C ++ 표준에서는 모든 프로그램 ( "호스팅 된"C 또는 C ++ 구현 용)에 main프로그램의 시작 함수 역할을하는 라는 함수가 있어야 합니다 . main함수는 로컬이 아닌 정적 변수를 0으로 초기화 한 후 호출 되며 반드시 그런 것은 아니지만 (!, 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결과 값을 인수로 사용하여 일반 함수 return 이 호출됩니다 . 표준은 사용할 수있는 세 가지 값을 정의합니다. 0 (성공을 나타냄), EXIT_SUCCESS(성공을 나타내며 일반적으로 0으로 정의 됨) 및 EXIT_FAILURE(실패를 나타냄) 두 개의 명명 된 상수는 <stdlib.h>또한 다음을 선언하는 헤더에 의해 정의됩니다 . exit함수.

main인수는 표현하기위한 것입니다 명령 줄 인수를 프로세스를 시작하는 데 사용되는 명령. argc(인수 개수)는 argv(인수 값) 배열 의 항목 수입니다 . 이러한 항목에 추가로 argv[argc]0이 보장됩니다. argc> 0 인 경우 -보장되지 않습니다! – 그러면 argv[0]빈 문자열에 대한 포인터 또는 "프로그램 호출에 사용 된 이름"에 대한 포인터가 보장됩니다. 이 이름은 경로를 포함 할 수 있으며 실행 파일의 이름 일 수 있습니다.

은 Using mainC와 C ++는 * nix에서 비롯 때문에 명령 줄 인수를 얻기 위해 인수하는 것은, * nix에서 스크립트에서 잘 작동합니다. 그러나 인수 인코딩에 대한 사실상의 Windows 표준 mainWindows ANSI입니다 . 이는 일반적인 Windows 파일 이름 (예 : 노르웨이어 Windows 설치의 경우 그리스어 또는 키릴 문자가있는 파일 이름)을 지원하지 않습니다. 따라서 Microsoft는 모든 파일 이름을 나타낼 수있는 UTF-16으로wmain 인코딩 된 와이드 문자 기반 인수를 갖는 라는 Windows 관련 시작 함수를 사용하여 C 및 C ++ 언어를 확장하기로 결정했습니다 .

wmain기능을 가질 수 이러한 서명 중 하나를 위한 표준 서명에 해당 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>( LPSTRis just char*).

인수 :

  • hInstance인수 값, 그것은 주로 실행 자원으로부터 부하에 사용되는 실행 파일의 화상 메모리의 기본 주소이며,이 대안에서 얻을 수 GetModuleHandleAPI 함수

  • hPrevInstance인수는 항상 0입니다

  • lpCmdLine인수는 대안에서 얻을 수 있습니다 GetCommandLineAPI 기능, 플러스 이상한 논리의 비트는 명령 줄의 프로그램 이름 부분을 생략하고,

  • nCmdShow인수 값은 교대에서 얻을 수있다 GetStartupInfoAPI의 기능을하지만, 현대 창문이있는 최상위 윈도우의 첫 번째 창조는 자동으로 그래서 어떤 실제 사용의 아니에요 않습니다.

따라서이 WinMain함수는 standard와 동일한 단점 main과 일부 (특히 장황함과 비표준)의 단점을 가지고 있으며 자체적 인 장점이 없기 때문에 벤더 종속 항목을 제외하고는 실제로 설명 할 수 없습니다. 그러나 Microsoft 도구 체인을 사용하면 링커가 GUI 하위 시스템으로 기본 설정되어 일부는 이점으로 간주합니다. 그러나 예를 들어 GNU 툴체인에서는 그러한 효과가 없으므로이 효과는 신뢰할 수 없습니다.

기반 기능의 다양한 문자 변종 과 동일한 방식으로, 표준의 다양한 특성 변이체이다 :wWinMain wchar_tWinMainwmainmain

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

WINAPI과 동일 CALLBACK하고, PWSTR단순히 wchar_t*.

가장 덜 알려지고 가장 적게 지원되는 기능을 제외하고는 비표준 함수를 사용할 이유가 없습니다. 즉 wmain, 편의상 GetCommandLineCommandLineToArgvWAPI 함수를 사용하여 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 프로세스에는 연결된 콘솔 창이 있고 최대 하나만있을 수 있습니다. 또한 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 | / i "subsys"찾기
MajorSubsystem 버전 4
MinorSubsystemVersion 0
하위 시스템 00000003 (Windows CUI)
[544] (초 -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000004 __major_subsystem_version__
[612] (초 -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000003 __subsystem__
[636] (초 -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"찾기
MajorSubsystem 버전 4
MinorSubsystemVersion 0
하위 시스템 00000002 (Windows GUI)
[544] (초 -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000004 __major_subsystem_version__
[612] (초 -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000002 __subsystem__
[636] (초 -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]
> _

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) :`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 "서브 시스템"찾기
MajorSubsystem 버전 4
MinorSubsystemVersion 0
하위 시스템 00000003 (Windows CUI)
[13134] (초 -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000004 __major_subsystem_version__
[13576] (초 -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000003 __subsystem__
[13689] (초 -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 "서브 시스템"찾기
MajorSubsystem 버전 4
MinorSubsystemVersion 0
하위 시스템 00000002 (Windows GUI)
[13134] (초 -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000004 __major_subsystem_version__
[13576] (초 -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000002 __subsystem__
[13689] (초 -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) : 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]
> _

According to @RaymondChen

The name WinMain is just a convention

Although the function WinMain is documented in the Platform SDK, it's not really part of the platform. Rather, WinMain is the conventional name for the user-provided entry point to a Windows program.

The real entry point is in the C runtime library, which initializes the runtime, runs global constructors, and then calls your WinMain function (or wWinMain if you prefer a Unicode entry point).

DllMain and WinMain is different in their prototypes itself. WinMain accepts commandline argument while the other one talks about how it's attached to the process.

As per MSDN documentation

By default, the starting address is a function name from the C run-time library. The linker selects it according to the attributes of the program, as shown in the following table.

  • mainCRTStartup (or wmainCRTStartup) An application using /SUBSYSTEM:CONSOLE; calls main (or wmain)

  • WinMainCRTStartup (or wWinMainCRTStartup) An application using /SUBSYSTEM:WINDOWS; calls WinMain (or wWinMain), which must be defined with __stdcall

  • _DllMainCRTStartup A DLL; calls DllMain, which must be defined with __stdcall, if it exists


A standard C program is passed 2 parameters by the command line at start up:

int main( int argc, char** argv ) ;
  • char** argv is an array of strings (char*)
  • int argc is the number of char* in argv

The booting function WinMain that programmers have to write for a windows program is slightly different. WinMain takes 4 parameters that are passed to the program by Win O/S at start up:

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.

See my article How to create a basic window in C for more


I vaguely recall reading somewhere that Windows programs have a main() function. It is just hidden in a header or library somewhere. I believe this main() function initializes all of the variables needed by WinMain() and then calls it.

Of course, I'm a WinAPI noob, so I hope others who are more knowledgeable will correct me if I am wrong.


I had an exe using _tWinMain and Configuration Properties.Linker.System.Subsystem: Windows (/SUBSYSTEM:WINDOWS). Later I wanted it to support cmdline args and print to the console so I added:

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

but that only worked by printing in another console window that went away so was not that useful. Below is the way I changed it to work with Console (/SUBSYSTEM:CONSOLE) in such a way as I could go back and forth, if I needed to.

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
  UNREFERENCED_PARAMETER(argc);
  UNREFERENCED_PARAMETER(argv);
  UNREFERENCED_PARAMETER(envp);
  return (_tWinMain(NULL, NULL, ::GetCommandLineW(), 0));
}

ReferenceURL : https://stackoverflow.com/questions/13871617/winmain-and-main-in-c-extended

반응형