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.
4 answers
O funkcjach.
Standard C i C++ wymaga, aby każdy program (dla "hostowanej" implementacji C lub C++) miał funkcję o nazwiemain
, 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 APIGetModuleHandle
, Argument
hPrevInstance
jest zawsze 0,-
Argument
lpCmdLine
może być alternatywnie uzyskany z funkcji APIGetCommandLine
, plus trochę dziwnej logiki, aby pominąć część nazwy programu w wierszu poleceń i Wartość argumentu
nCmdShow
można alternatywnie uzyskać z APIGetStartupInfo
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] > _
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.
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
(LUBwmainCRTStartup
) aplikacji wykorzystującej/SUBSYSTEM:CONSOLE;
wywołuje main (lubwmain
)WinMainCRTStartup
(lubwWinMainCRTStartup
) aplikacji wykorzystującej/SUBSYSTEM:WINDOWS;
wywołaniaWinMain
(lubwWinMain
), które muszą być zdefiniowane przez__stdcall
-
_DllMainCRTStartup
biblioteka DLL; wywołujeDllMain
, która musi być zdefiniowana przez__stdcall
, jeśli istnieje
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
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ę.
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