WINMAIN e main () em C ++ (estendido)

Certo, eu olhei para este post: Diferença entre WinMain, main e DllMain em C ++

Agora sei que o WINMAIN é usado para aplicativos de janela e main() para consoles. Mas ler o post realmente não me diz por que exatamente qual é a diferença.

Quero dizer, qual é o sentido de separar diferentes funções da rede para iniciar um programa? Isso é devido a problemas de desempenho? Ou o que é isso?

    Sobre as funções.

    Os padrões C e C ++ requerem que qualquer programa (para uma implementação “C” ou C ++ hospedada) tenha uma function chamada main , que serve como a function de boot do programa. A function main é chamada após a boot zero de variables ​​estáticas não-locais, e possivelmente mas não necessariamente (!, C ++ 11 §3.6.2 / 4) essa chamada acontece após a boot dinâmica de tais variables. Pode ter uma das seguintes assinaturas:

     int main() int main( int argc, char* argv[] ) 

    além de possíveis assinaturas definidas pela implementação (C ++ 11 §3.6.1 / 2), exceto que o tipo de resultado deve ser int .

    Como a única function em C ++ main tem um valor de resultado padrão , a saber 0. Se main retorna então depois da function normal return exit é chamada com o valor do resultado main como argumento. O padrão define três valores garantidos que podem ser usados: 0 (indica sucesso), EXIT_SUCCESS (também indica sucesso e é normalmente definido como 0) e EXIT_FAILURE (indica falha), onde as duas constantes nomeadas são definidas pelo header que também declara a function de exit .

    Os main argumentos pretendem representar os argumentos da linha de comando para o comando usado para iniciar o processo. argc (contagem de argumentos) é o número de itens na matriz argv (valores de argumento). Além desses itens, argv[argc] é garantido como 0. Se argc > 0 – o que não é garantido! – então argv[0] é garantido para ser um ponteiro para uma string vazia, ou um ponteiro para o “nome usado para invocar o programa”. Esse nome pode include um caminho e pode ser o nome do executável.

    Usar os argumentos main para obter os argumentos da linha de comando funciona bem no * nix, porque C e C ++ são originados com * nix. No entanto, o padrão do Windows para a codificação dos main argumentos é Windows ANSI , que não oferece suporte a nomes de arquivos gerais do Windows (como, por exemplo, para uma instalação do Windows em norueguês, nomes de arquivos com caracteres gregos ou cirílicos). Portanto, a Microsoft optou por estender as linguagens C e C ++ com uma function de boot específica do Windows chamada wmain , que possui argumentos baseados em caracteres largos codificados como UTF-16 , que podem representar qualquer nome de arquivo.

    A function wmain pode ter uma dessas assinaturas , correspondendo às assinaturas padrão da main :

     int wmain() int wmain( int argc, wchar_t* argv[] ) 

    além de mais alguns que não são especialmente úteis.

    wmain seja, o wmain é um substituto baseado em caracteres diretos para o main .

    A function baseada em char WinMain foi introduzida com o Windows, no início dos anos 80:

     int CALLBACK WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ); 

    onde CALLBACK , HINSTANCE e LPSTR são definidos pelo header ( LPSTR é apenas char* ).

    Argumentos:

    • o valor do argumento hInstance é o endereço base da imagem da memory do executável, é usado principalmente para carregar resources do executável e, alternativamente, pode ser obtido a partir da function da API GetModuleHandle ,

    • o argumento hPrevInstance é sempre 0,

    • o argumento lpCmdLine pode, alternativamente, ser obtido a partir da function GetCommandLine API, além de um pouco de lógica estranha para pular a parte do nome do programa da linha de comando, e

    • O valor do argumento nCmdShow pode, alternativamente, ser obtido a partir da function da API GetStartupInfo , mas com o Windows moderno, a primeira criação de uma janela de nível superior faz isso automaticamente para que não seja de uso prático.

    Assim, a function WinMain tem as mesmas desvantagens do padrão main , além de algumas (em particular a verbosidade e ser não-padrão), e sem vantagens próprias, então é realmente inexplicável, exceto possivelmente como uma coisa de bloqueio de fornecedor. No entanto, com a cadeia de ferramentas da Microsoft, o vinculador é padronizado para o subsistema da GUI, o que alguns vêem como uma vantagem. Mas, por exemplo, com o toolchain GNU, ele não tem esse efeito, portanto, esse efeito não pode ser invocado.

    A function baseada em wWinMain wchar_t é uma variante de caractere ampla do WinMain , da mesma forma que wmain é uma variante de caractere ampla do main :

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

    onde WINAPI é o mesmo que CALLBACK e PWSTR é simplesmente wchar_t* .

    Não há uma boa razão para usar qualquer uma das funções não-padrão, exceto a menos conhecida e menos suportada delas, ou wmain , wmain e apenas por conveniência: que isso evita o uso das funções da API GetCommandLine e CommandLineToArgvW para capturar codificação UTF-16 argumentos.

    Para evitar que o Microsoft linker atue (o vinculador do GNU toolchain não), basta definir a variável de ambiente LINK para /entry:mainCRTStartup ou especificar essa opção diretamente. Essa é a function de ponto de input da biblioteca de tempo de execução da Microsoft que, após algumas inicializações, chama a function main padrão. As outras funções de boot possuem funções de ponto de input correspondentes nomeadas da mesma maneira sistemática.


    Exemplos de uso da function main padrão.

    Código fonte comum:

    foo.cpp

     #undef UNICODE #define UNICODE #include  int main() { MessageBox( 0, L"Press OK", L"Hi", MB_SETFOREGROUND ); } 

    Nos exemplos abaixo (primeiro com o conjunto de ferramentas GNU e, em seguida, com o conjunto de ferramentas da Microsoft), esse programa é criado primeiro como um programa de subsistema de console e, em seguida, como um programa de subsistema de GUI . Um programa de subsistema de console, ou em suma apenas um programa de console , é aquele que requer uma janela de console. Este é o subsistema padrão para todos os linkers do Windows que usei (reconhecidamente não muitos), possivelmente para todos os períodos de linkers do Windows.

    Para um programa de console, o Windows cria uma janela de console automaticamente, se necessário. Qualquer processo do Windows, independentemente do subsistema, pode ter uma janela de console associada e, no máximo, um. Além disso, o interpretador de comandos do Windows aguarda a conclusão de um programa do console, para que a apresentação de texto do programa seja concluída.

    Por outro lado, um programa de subsistema de GUI é aquele que não requer uma janela de console. O interpretador de comandos não espera por um programa do subsistema da GUI, exceto em arquivos em lote. Uma maneira de evitar a espera de conclusão, para ambos os tipos de programa, é usar o comando start . Uma maneira de apresentar o texto da janela do console a partir de um programa do subsistema da GUI é redirect seu stream de saída padrão. Outra maneira é criar explicitamente uma janela do console a partir do código do programa.

    O subsistema do programa está codificado no header do executável. Ele não é mostrado pelo Windows Explorer (exceto que, no Windows 9x, era possível “visualizar rapidamente” um executável, que apresentava as mesmas informações que a ferramenta dumpbin da Microsoft agora oferece). Não existe um conceito C ++ correspondente.

    main com o conjunto de ferramentas GNU.

     [D: \ dev \ teste]
     g + foo.cpp
    
     [D: \ dev \ teste]
     > objdump -x a.exe |  encontrar / i "subsys"
     MajorSubsystemVersion 4
     MinorSubsystemVersion 0
     Subsistema 00000003 (Windows CUI)
     [544] (seg -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000004 __major_subsystem_version__
     [612] (seg -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000003 __subsistema__
     [636] (seg -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000000 __minor_subsystem_version__
    
     [D: \ dev \ teste]
     > g ++ foo.cpp -mwindows
    
     [D: \ dev \ teste]
     > objdump -x a.exe |  encontrar / i "subsys"
     MajorSubsystemVersion 4
     MinorSubsystemVersion 0
     Subsistema 00000002 (GUI do Windows)
     [544] (seg -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000004 __major_subsystem_version__
     [612] (seg -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0 x 00000002 __subsistema__
     [636] (seg -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000000 __minor_subsystem_version__
    
     [D: \ dev \ teste]
     > _
    

    main com toolchain da Microsoft:

     [D: \ dev \ teste]
     > conjunto LINK = / input: mainCRTStartup
    
     [D: \ dev \ teste]
     > cl foo.cpp user32.lib
     foo.cpp
    
     [D: \ dev \ teste]
     dumpbin / headers foo.exe |  encontrar / i "subsys"
                 6,00 versão do subsistema
                    3 subsistema (Windows CUI)
    
     [D: \ dev \ teste]
     > cl foo.cpp / link user32.lib / subsistema: janelas
     foo.cpp
    
     [D: \ dev \ teste]
     dumpbin / headers foo.exe |  encontrar / i "subsys"
                 6,00 versão do subsistema
                    2 subsistema (GUI do Windows)
    
     [D: \ dev \ teste]
     > _
    

    Exemplos de uso da function wmain da Microsoft.

    O seguinte código principal é comum para as demonstrações de toolchain GNU e toolchain da Microsoft:

    bar.cpp

     #undef UNICODE #define UNICODE #include  #include  // std::wstring #include  // 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 com o conjunto de ferramentas GNU.

    O conjunto de ferramentas GNU não suporta a function wmain da Microsoft:

     [D: \ dev \ teste]
     > g ++ bar.cpp
     d: / bin / mingw / bin /../ lib / gcc / i686-pc-mingw32 / 4.7.1 /../../../ libmingw32.a (main.o): main.c :(. text.startup + 0xa3): referência indefinida para `WinMain
     @ 16 '
     collect2.exe: erro: ld retornou 1 status de saída
    
     [D: \ dev \ teste]
     > _
    

    A mensagem de erro do link aqui, sobre WinMain , é porque o conjunto de ferramentas GNU suporta essa function (presumivelmente porque muito código antigo o usa), e procura por ele como último recurso depois de falhar em encontrar um padrão main .

    No entanto, é trivial adicionar um módulo com um padrão main que chame o wmain :

    wmain_support.cpp

     extern int wmain( int, wchar_t** ); #undef UNICODE #define UNICODE #include  // GetCommandLine, CommandLineToArgvW, LocalFree #include  // 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 ); } 

    Agora,

     [D: \ dev \ teste]
     g ++ bar.cpp wmain_support.cpp
    
     [D: \ dev \ teste]
     > objdump -x a.exe |  encontrar / i "subsistema"
     MajorSubsystemVersion 4
     MinorSubsystemVersion 0
     Subsistema 00000003 (Windows CUI)
     [13134] (seg -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000004 __major_subsystem_version__
     [13576] (seg -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000003 __subsistema__
     [13689] (seg -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000000 __minor_subsystem_version__
    
     [D: \ dev \ teste]
     g ++ bar.cpp wmain_support.cpp -mwindows
    
     [D: \ dev \ teste]
     > objdump -x a.exe |  encontrar / i "subsistema"
     MajorSubsystemVersion 4
     MinorSubsystemVersion 0
     Subsistema 00000002 (GUI do Windows)
     [13134] (seg -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000004 __major_subsystem_version__
     [13576] (seg -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000002 __subsistema__
     [13689] (seg -1) (fl 0x00) (ty 0) (scl 2) (nx 0) 0x00000000 __minor_subsystem_version__
    
     [D: \ dev \ teste]
     > _
    

    wmain com toolchain da Microsoft.

    Com o conjunto de ferramentas da Microsoft, o vinculador infere automaticamente o ponto de input wmainCRTStartup se nenhum ponto de input for especificado e uma function wmain estiver presente (não está claro o que acontece se um padrão main também estiver presente, não verificado nos últimos anos):

     [D: \ dev \ teste]
     > definir link = / input: mainCRTStartup
    
     [D: \ dev \ teste]
     > cl bar.cpp user32.lib
     bar.cpp
     LIBCMT.lib (crt0.obj): erro LNK2019: símbolo externo não resolvido _main referenciado na function ___tmainCRTStartup
     bar.exe: erro fatal LNK1120: 1 externos não resolvidos
    
     [D: \ dev \ teste]
     > definir link =
    
     [D: \ dev \ teste]
     > cl bar.cpp user32.lib
     bar.cpp
    
     [D: \ dev \ teste]
     > _
    

    Com uma function de boot não padrão, como wmain , é, no entanto, provavelmente melhor especificar o ponto de input explicitamente, de modo a ficar bem claro sobre a intenção:

     [D: \ dev \ teste]
     > cl bar.cpp / link user32.lib / entry: wmainCRTStartup
     bar.cpp
    
     [D: \ dev \ teste]
     dumpbin / headers bar.exe |  encontrar / i "subsistema"
                 6,00 versão do subsistema
                    3 subsistema (Windows CUI)
    
     [D: \ dev \ teste]
     > cl bar.cpp / link user32.lib / entry: wmainCRTStartup / subsistema: janelas
     bar.cpp
    
     [D: \ dev \ teste]
     dumpbin / headers bar.exe |  encontrar / i "subsistema"
                 6,00 versão do subsistema
                    2 subsistema (GUI do Windows)
    
     [D: \ dev \ teste]
     > _
    

    De acordo com @RaymondChen

    O nome WinMain é apenas uma convenção

    Embora a function WinMain esteja documentada no Platform SDK, ela não faz parte da plataforma. Em vez disso, WinMain é o nome convencional para o ponto de input fornecido pelo usuário para um programa do Windows.

    O ponto de input real está na biblioteca de tempo de execução C, que inicializa o tempo de execução, executa construtores globais e chama sua function WinMain (ou wWinMain se você preferir um ponto de input Unicode).

    DllMain e WinMain são diferentes em seus próprios protótipos. O WinMain aceita o argumento de linha de comando, enquanto o outro fala sobre como ele é anexado ao processo.

    De acordo com a documentação do MSDN

    Por padrão, o endereço inicial é um nome de function da biblioteca de tempo de execução C. O vinculador seleciona de acordo com os atributos do programa, conforme mostrado na tabela a seguir.

    • mainCRTStartup (ou wmainCRTStartup ) Um aplicativo usando /SUBSYSTEM:CONSOLE; chama principal (ou wmain )

    • WinMainCRTStartup (ou wWinMainCRTStartup ) Um aplicativo usando /SUBSYSTEM:WINDOWS; chama WinMain (ou wWinMain ), que deve ser definido com __stdcall

    • _DllMainCRTStartup Uma DLL; chama DllMain , que deve ser definido com __stdcall , se existir

    Um programa C padrão recebe 2 parâmetros pela linha de comando na boot:

     int main( int argc, char** argv ) ; 
    • char** argv é um array de strings ( char* )
    • int argc é o número de char* em argv

    A function de boot WinMain que os programadores têm que escrever para um programa do Windows é um pouco diferente. WinMain recebe 4 parâmetros que são passados ​​para o programa pelo Win O / S na boot:

     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. 

    Veja meu artigo Como criar uma janela básica em C para mais

    Eu vagamente me lembro de ler em algum lugar que os programas do Windows têm uma function main() . É apenas escondido em um header ou biblioteca em algum lugar. Eu acredito que essa function main() inicializa todas as variables ​​necessárias pelo WinMain() e, em seguida, chama.

    É claro que sou um noob do WinAPI, por isso espero que outros que estejam mais bem informados me corrijam se eu estiver errado.