WINMAIN i main () w C++ (Rozszerzony)

Racja, spojrzałem na ten post: różnica między WinMain, main i DllMain w C++

Teraz wiem, że WINMAIN jest używany w aplikacjach okienkowych i main() w konsolach. Ale czytanie postu nie mówi mi, dlaczego dokładnie, jaka jest różnica.

Jaki jest sens rozdzielania różnych funkcji sieciowych, aby uruchomić program? Czy to z powodu problemów z wydajnością? Albo o co chodzi?
Author: Community, 2012-12-14

4 answers

O funkcjach.

Standard C i C++ wymaga, aby każdy program (dla "hostowanej" implementacji C lub C++) miał funkcję o nazwie main, która służy jako funkcja startowa programu . Funkcja main jest wywoływana po zerowej inicjalizacji nielokalnych zmiennych statycznych i prawdopodobnie, ale niekoniecznie (!, C++11 §3.6.2/4) wywołanie to następuje po dynamicznej inicjalizacji takich zmiennych. Może mieć jedną z następujących podpisy:
int main()
int main( int argc, char* argv[] )

Plus możliwe sygnatury zdefiniowane przez implementację (C++11 §3.6.1 / 2) z wyjątkiem tego, że typem wyniku musi być int.

Jako jedyna taka funkcja w C++ main ma domyślny wynik wartość, mianowicie 0. Jeśli zwraca main, to po wywołaniu zwykłej funkcji zwraca exit z wartością wyniku main jako argumentem. Standard definiuje trzy wartości, które gwarantowane mogą być użyte: 0( oznacza sukces), EXIT_SUCCESS (również wskazuje na sukces i jest typowo definiowane jako 0), oraz EXIT_FAILURE (oznacza awarię), gdzie dwie nazwane stałe są zdefiniowane przez nagłówek <stdlib.h>, który również deklaruje funkcję exit.

Argumenty main są przeznaczone do reprezentowania argumentów linii poleceń dla polecenia użytego do uruchomienia procesu. argc (liczba argumentów) jest liczbą pozycji w tablicy argv (wartości argumentów). Oprócz tych pozycji argv[argc] jest gwarantowane 0. If argc > 0-co nie jest gwarantowane! – wtedy argv[0] jest gwarantowany albo jako wskaźnik do pustego łańcucha znaków, albo jako wskaźnik do "nazwy użytej do wywołania programu". Ta nazwa może zawierać ścieżkę i może być nazwą pliku wykonywalnego.

Użycie argumentów main w celu uzyskania argumentów linii poleceń działa dobrze w *nix, ponieważ C i C++ pochodzą z *nix. Jednak standardem de facto Windows do kodowania argumentów main jest Windows ANSI , który nie obsługuje ogólne nazwy plików systemu Windows(takie jak, dla instalacji norweskiego systemu Windows, nazwy plików ze znakami greckimi lub cyrylicą). Dlatego Microsoft zdecydował się rozszerzyć języki C i c++ o specyficzną dla systemu Windows funkcję uruchamiania o nazwie wmain, który ma argumenty oparte na szerokich znakach zakodowane jako UTF-16 , które mogą reprezentować dowolną nazwę pliku.

Funkcja wmain może mieć jeden z tych podpisów, odpowiadający standardowym podpisom dla main:

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

Plus kilka innych, które nie są szczególnie przydatne.

TJ., wmain jest bezpośrednim zamiennikiem dla main.

The WinMain char funkcja oparta została wprowadzona w systemie Windows na początku lat 80.]}

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

Gdzie CALLBACK, HINSTANCE i LPSTR są zdefiniowane przez nagłówek <windows.h> (LPSTR jest po prostu char*).

Argumenty:

  • Wartość argumentu hInstance jest adresem bazowym obrazu pamięci plik wykonywalny jest używany głównie do ładowania zasobów z pliku wykonywalnego i może być alternatywnie uzyskany z funkcji API GetModuleHandle,

  • Argument hPrevInstance jest zawsze 0,

  • Argument lpCmdLine może być alternatywnie uzyskany z funkcji API GetCommandLine, plus trochę dziwnej logiki, aby pominąć część nazwy programu w wierszu poleceń i

  • Wartość argumentu nCmdShow można alternatywnie uzyskać z API GetStartupInfo funkcja, ale w nowoczesnych oknach pierwsze stworzenie okna najwyższego poziomu robi to automatycznie, więc nie ma to żadnego praktycznego zastosowania.

Tak więc, funkcja WinMain ma te same wady, co standardowa main, plus niektóre (w szczególności słowność i niestandardowość), i nie ma własnych zalet, więc jest naprawdę niewytłumaczalna, z wyjątkiem prawdopodobnie jako rzeczy lock-in dostawcy. Jednak z Microsoft tool chain sprawia, że linker domyślnie do podsystemu GUI, które niektórzy widzą jako korzyść. Ale np. w GNU toolchain nie ma takiego efektu, więc nie można na nim polegać.

The wWinMain wchar_t funkcja bazująca jest szerokim wariantem znaku WinMain, podobnie jak wmain jest szerokim wariantem znaku standardowego main:

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

Gdzie WINAPI jest tym samym co CALLBACK, A PWSTR jest po prostu wchar_t*.

Nie ma powodu, aby używać żadnej z niestandardowych funkcji, z wyjątkiem najmniej znanych i najmniej wspieranych z ich, a mianowicie wmain, a potem dla wygody: że pozwala to uniknąć użycia funkcji API GetCommandLine i CommandLineToArgvW do odbioru zakodowanych argumentów UTF-16.

Aby uniknąć działania linkera Microsoft (linker GNU toolchain nie działa), po prostu ustaw zmienną środowiskową LINK na /entry:mainCRTStartup, lub określ tę opcję bezpośrednio. Jest to funkcja punktu wejścia Microsoft runtime library, która po pewnej inicjalizacji wywołuje standardową funkcję main. Pozostałe funkcje startowe mają odpowiednie funkcje punktu wejścia nazwane w ten sam sposób systematyczny.


Przykłady użycia standardowej funkcji main.

Wspólny kod źródłowy:

foo.cpp

#undef UNICODE
#define UNICODE
#include <windows.h>

int main()
{
    MessageBox( 0, L"Press OK", L"Hi", MB_SETFOREGROUND );
}

W poniższych przykładach (najpierw z GNU toolchain, a następnie z Microsoft toolchain) ten program jest najpierw zbudowany jako console subsystem program , a następnie jako GUI subsystem program. Program podsystem konsoli, lub w skrócie po prostu konsola program , to taki, który wymaga okna konsoli. Jest to domyślny podsystem dla wszystkich linkerów systemu Windows, których używałem(co prawda nie za wiele), prawdopodobnie dla wszystkich linkerów systemu Windows.

Dla konsoli program Windows tworzy okno konsoli automatycznie w razie potrzeby. Każdy proces Windows, niezależnie od podsystemu, może mieć powiązane okno konsoli, a co najwyżej jedno. Również interpreter poleceń Windows czeka na zakończenie programu konsolowego, tak aby prezentacja tekstu programu zakończona.

Odwrotnie, program podsystemu GUI to taki, który nie wymaga okna konsoli. Interpreter poleceń nie czeka na program podsystemu GUI, z wyjątkiem plików wsadowych. Jednym ze sposobów uniknięcia oczekiwania na zakończenie, dla obu rodzajów programów, jest użycie polecenia start. Jednym ze sposobów prezentacji tekstu okna konsoli z programu podsystemu GUI jest przekierowanie jego standardowego strumienia wyjściowego. Innym sposobem jest jawne utworzenie okna konsoli z kod programu.

Podsystem programu jest zakodowany w nagłówku programu wykonywalnego. Nie jest pokazywany przez Eksploratora Windows (z wyjątkiem tego, że w Windows 9x można było "szybki podgląd" pliku wykonywalnego, który prezentował prawie te same informacje, co narzędzie Microsoft dumpbin teraz robi). Nie ma odpowiedniej koncepcji C++.

main z GNU toolchain.

[D:\dev\test]
> g++ foo.cpp

[D:\dev\test]
> objdump -x a.exe | find /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 | find /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]
> _

[[13]} z toolchainem Microsoftu:

[D:\dev\test]
> set LINK=/entry:mainCRTStartup

[D:\dev\test]
> cl foo.cpp user32.lib
foo.cpp

[D:\dev\test]
> dumpbin /headers foo.exe | find /i "subsys"
            6.00 subsystem version
               3 subsystem (Windows CUI)

[D:\dev\test]
> cl foo.cpp /link user32.lib /subsystem:windows
foo.cpp

[D:\dev\test]
> dumpbin /headers foo.exe | find /i "subsys"
            6.00 subsystem version
               2 subsystem (Windows GUI)

[D:\dev\test]
> _

Przykłady użycia Microsoftu wmain funkcja.

[88]} poniższy główny kod jest wspólny zarówno dla demonstracji GNU toolchain, jak i Microsoft toolchain:]}

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 Z GNU toolchain.

[[88]} GNU toolchain nie obsługuje funkcji Microsoftu 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): undefined reference to `WinMain
@16'
collect2.exe: error: ld returned 1 exit status

[D:\dev\test]
> _

Komunikat o błędzie linku, o WinMain, wynika z tego, że GNU toolchain obsługuje tę funkcję (prawdopodobnie dlatego, że tak wiele starożytnego kodu jej używa) i szuka jej jako ostatnia deska ratunku po nie znalezieniu standardu main.

Jednak trywialne jest Dodanie modułu ze standardem main, który wywołuje 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 );
}

Teraz,

[D:\dev\test]
> g++ bar.cpp wmain_support.cpp

[D:\dev\test]
> objdump -x a.exe | find /i "subsystem"
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 | find /i "subsystem"
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 Z toolchainem Microsoftu.

Z toolchain Microsoftu linker automatycznie wnioskuje punkt wejściowy wmainCRTStartup, jeśli nie podano punktu wejściowego, a funkcja wmain jest obecna (nie jest jasne, co się stanie, jeśli standard main jest również obecny, nie sprawdzałem tego w ostatnich latach):

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

Z niestandardową funkcją uruchamiania, taką jak wmain, prawdopodobnie najlepiej jest wyraźnie określić punkt wejścia, aby być bardzo jasnym o intencji:

[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]
> _
 145
Author: Cheers and hth. - Alf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-15 17:35:34

Według @ RaymondChen

Nazwa WinMain jest tylko konwencją

Chociaż funkcja WinMain jest udokumentowana w Platform SDK, to nie jest częścią platformy. WinMain jest raczej konwencjonalnym nazwa dla podanego przez użytkownika punktu wejścia do programu Windows.

Prawdziwy punkt wejścia znajduje się w bibliotece uruchomieniowej C, która inicjalizuje runtime, uruchamia globalne konstruktory, a następnie wywołuje WinMain funkcja (lub wWinMain jeśli wolisz punkt wejścia Unicode).

DllMain i WinMain różnią się w swoich prototypach. WinMain akceptuje argument linii poleceń, podczas gdy drugi mówi o tym, jak jest dołączony do procesu.

Wg MSDN documentation

Domyślnie, adres startowy jest nazwą funkcji z biblioteki C run-time. Linker wybiera go zgodnie z atrybutami programu, jak pokazano poniżej stolik.

  • mainCRTStartup (LUB wmainCRTStartup) aplikacji wykorzystującej /SUBSYSTEM:CONSOLE; wywołuje main (lub wmain)

  • WinMainCRTStartup (lub wWinMainCRTStartup) aplikacji wykorzystującej /SUBSYSTEM:WINDOWS; wywołania WinMain (lub wWinMain), które muszą być zdefiniowane przez __stdcall

  • _DllMainCRTStartup biblioteka DLL; wywołuje DllMain, która musi być zdefiniowana przez __stdcall, jeśli istnieje

 8
Author: sarat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-14 03:37:21

Standardowy program C jest przekazywany 2 parametrami przez linię poleceń przy uruchomieniu:

int main( int argc, char** argv ) ;
  • char** argv jest tablicą ciągów (char*)
  • int argc jest liczbą char* w argv

Funkcja rozruchowa WinMain, którą programiści muszą pisać dla programu windows, jest nieco inna. WinMain pobiera 4 parametry, które są przekazywane do programu przez Win O / s przy starcie:

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.

Zobacz mój artykuł Jak utworzyć podstawowe okno w C aby dowiedzieć się więcej

 2
Author: bobobobo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-14 02:03:31

Przypominam sobie, że gdzieś czytałem, że programy Windows mają funkcję main(). Jest po prostu ukryty w nagłówku lub gdzieś w bibliotece. Wierzę, że ta funkcja main() inicjalizuje wszystkie zmienne potrzebne przez WinMain(), a następnie ją wywołuje.

Oczywiście, jestem noobem WinAPI, więc mam nadzieję, że inni, którzy są bardziej kompetentni, poprawią mnie, jeśli się mylę.

 1
Author: Code-Apprentice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-14 02:09:45