WINMAIN et main() en C++ (Extended)

à droite, j'ai regardé ce post: différence entre WinMain, main et DllMain en C++

je sais maintenant que WINMAIN est utilisé pour les applications de fenêtre et main() pour les consoles. Mais lire le post ne me dit pas vraiment pourquoi exactement quelle est la différence.

je veux dire quel est l'intérêt de séparer les différentes fonctions du secteur pour démarrer un programme? Est-ce dû à des problèmes de performances? Ou c'est quoi?

42
demandé sur Community 2012-12-14 05:58:51

4 réponses

à propos des fonctions.

les normes C et c++ exigent que tout programme (pour une implémentation C ou C++" hébergée") ait une fonction appelée main , qui sert de fonction de démarrage du programme . La fonction main est appelée après zero-initialization de variables statiques non locales, et peut-être mais pas nécessairement (!, C++11 §3.6.2 / 4) Cet appel se produit après dynamique initialisation de telles variables. Elle peut porter l'une des signatures suivantes:

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

plus les signatures possibles définies par la mise en œuvre (C++11 §3.6.1/2) sauf que le type de résultat doit être int .

comme la seule fonction de ce type dans C++ main a une valeur de résultat par défaut , à savoir 0. Si main retourne alors après la fonction ordinaire retour exit est appelé avec le main résultat valeur comme argument. La norme définit trois valeurs qui peuvent être utilisées: 0 (indique le succès)), EXIT_SUCCESS ( indique également le succès, et est généralement défini comme 0), et EXIT_FAILURE (indique l'échec), où les deux constantes nommées sont définies par l'en-tête <stdlib.h> qui déclare également la fonction exit .

les arguments main sont destinés à représenter la commande arguments en ligne de commande pour la commande utilisée pour démarrer le processus. argc (argument count) est le nombre d'éléments dans le argv (valeurs d'argument) tableau. En plus de ces éléments argv[argc] est garanti d'être 0. Si argc > 0, ce qui n'est pas garanti! – puis argv[0] est garanti d'être un pointeur vers une chaîne vide, ou un pointeur vers le "nom utilisé pour appeler le programme". Ce nom peut inclure un chemin, et il peut être nom de l'exécutable.

utilisant les arguments main pour obtenir les arguments en ligne de commande fonctionne très bien dans *nix, parce que C et c++ proviennent de *nix. Cependant , la norme de facto Windows standard pour l'encodage des arguments main est Windows ANSI , qui ne supporte pas les noms de fichiers Windows généraux (tels que, pour une installation Windows Norvégienne, les noms de fichiers avec des caractères grecs ou cyrilliques). Microsoft a donc choisi D'étendre les langages C et c++ avec une fonction de démarrage spécifique à Windows appelée wmain , qui a des arguments basés sur des caractères larges encodés comme UTF-16 , qui peut représenter n'importe quel nom de fichier.

la fonction wmain peut avoir une de ces signatures , correspondant aux signatures standard pour main :

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

, plus quelques autres qui ne sont pas particulièrement utiles.

c'est-à-dire wmain est un remplacement direct basé sur les caractères larges pour main .

le WinMain char fonction basée a été introduite avec Windows, Au début des années 1980:

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

CALLBACK , HINSTANCE et LPSTR sont définis par l'en-tête <windows.h> ( LPSTR est juste char* ).

Arguments:

  • la valeur de l'argument hInstance est l'adresse de base de l'image mémoire de l'exécutable, il est principalement utilisé pour charger des ressources à partir de l'exécutable, et il peut également être obtenu à partir de la GetModuleHandle fonction API,

  • l'argument hPrevInstance est toujours 0,

  • l'argument lpCmdLine peut aussi être obtenu à partir de la fonction API GetCommandLine , plus un peu de logique étrange pour sauter la partie nom de programme de la ligne de commande, et

  • la valeur de l'argument nCmdShow peut également être obtenue à partir de la fonction API GetStartupInfo , mais avec les fenêtres modernes la première création d'une fenêtre de haut niveau fait automatiquement de sorte qu'il n'est pas d'aucune utilité pratique.

ainsi, la fonction WinMain a les mêmes inconvénients que la norme main , plus certains (en particulier la verbosité et d'être non-standard), et aucun avantage de ses propres, de sorte qu'il est vraiment inexplicable sauf peut-être comme une chose de vendeur lock-in. Cependant, avec la chaîne d'outils Microsoft, il rend le linker par défaut pour le sous-système GUI, que certains considèrent comme un avantage. Mais avec, par exemple, la chaîne D'outils GNU, il n'y a pas d'effet de ce genre, de sorte que cet effet ne peut pas être utilisé.

le wWinMain wchar_t la fonction de base est une variante de caractère large de WinMain , de la même manière que wmain est une variante de caractère large de la norme main :

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

WINAPI est la même que CALLBACK , et PWSTR est simplement wchar_t* .

Il n'y a pas de bonne raison pour utiliser toutes les fonctions sauf pour le moins connu et le moins soutenu d'entre eux, à savoir wmain , et puis juste pour la commodité: que cela évite d'utiliser les fonctions API GetCommandLine et CommandLineToArgvW pour capter les arguments encodés UTF-16.

pour éviter que le linker de Microsoft n'intervienne (ce qui n'est pas le cas du linker de la chaîne GNU), il suffit de définir la variable d'environnement LINK à /entry:mainCRTStartup , ou de spécifier cette option directement. C'est la fonction Microsoft runtime library entry point qui, après quelques l'initialisation, appelle la fonction standard main . Les autres fonctions de démarrage ont des fonctions de point d'entrée correspondantes nommées de la même manière systématique.


exemples d'utilisation de la fonction standard main .

code source commun:

foo.cpp

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

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

dans les exemples ci-dessous (d'abord avec la chaîne D'outils GNU et puis avec la chaîne D'outils de Microsoft) ce programme est d'abord construit comme un programme de sous-système de console , puis comme un programme de sous-système GUI . Un programme ou est un programme qui nécessite une fenêtre de console. C'est le sous-système par défaut pour tous les linkers Windows que j'ai utilisés (il est vrai qu'il n'y en a pas beaucoup), probablement pour toutes les périodes de Windows linkers.

pour une console le programme Windows crée une fenêtre de console automatiquement si nécessaire. Tout processus Windows, quel que soit le sous-système, peut avoir une fenêtre de console associée, et au plus une. En outre, L'interpréteur de commandes Windows attend qu'un programme de la console termine, de sorte que la présentation du texte du programme est terminée.

inversement, un sous-système GUI est un programme qui ne nécessite pas de fenêtre de console. L'interprète de commande n'attend pas une GUI programme du sous-système, sauf dans les fichiers de lots. Une façon d'éviter l'attente d'achèvement, pour les deux types de programme, est d'utiliser la commande start . Une façon de présenter le texte de la fenêtre de la console à partir d'un sous-système GUI est de rediriger son flux de sortie standard. Une autre façon consiste à créer explicitement une fenêtre de console à partir du code du programme.

le sous-système du programme est encodé dans l'en-tête de l'exécutable. Il n'est pas montré par L'Explorateur de Windows (sauf que dans Windows 9x on pourrait "quick view" un exécutable, qui présentait à peu près les mêmes informations que L'outil dumpbin de Microsoft). Il n'y a pas de concept c++ correspondant.

main avec la chaîne GNU.

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

main avec la chaîne D'outils de Microsoft:

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

exemples D'utilisation de la fonction wmain de Microsoft.

le code est commun aux deux démonstrations de la chaîne D'outils GNU et 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 avec la chaîne GNU.

la chaîne D'outils GNU ne supporte pas la fonction wmain de Microsoft:

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

le message d'erreur de lien ici, à propos de WinMain , est parce que la chaîne D'outils GNU supporte que Fonction (probablement parce que tant de code ancien l'utilise), et la recherche en dernier recours après avoir échoué à trouver un standard main .

cependant, il est trivial d'ajouter un module avec un standard main qui appelle le 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 );
}

maintenant,

[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 avec Microsoft de la chaîne.

avec la chaîne D'outils de Microsoft, le linker déduit automatiquement le point d'entrée wmainCRTStartup si aucun point d'entrée n'est spécifié et qu'une fonction wmain est présente (on ne sait pas ce qui se passe si une norme main est également présente, je n'ai pas vérifié cela au cours des dernières années):

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

avec une fonction de démarrage non standard telle que wmain il est, cependant, probablement mieux de spécifier explicitement le point d'entrée, de sorte que pour être très clair sur l'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]
> _
145
répondu Cheers and hth. - Alf 2012-12-15 17:35:34

selon @RaymondChen

le nom WinMain est juste une convention

bien que la fonction WinMain soit documentée dans la plate-forme SDK, il est pas vraiment une partie de la plateforme. WinMain est plutôt le nom du point d'entrée d'un programme Windows fourni par L'utilisateur.

le point d'entrée réel est dans la bibliothèque C runtime, qui initialise le runtime, exécute global constructors, puis appelle votre WinMain fonction (ou wWinMain si vous préférez un point D'entrée Unicode).

DllMain et WinMain est différent dans leurs prototypes. WinMain accepte l'argument de ligne de commande pendant que l'autre parle de la façon dont il est attaché au processus.

selon MSDN documentation

Par défaut, l'adresse de départ est un nom de fonction de la bibliothèque C run-time. L'éditeur de liens sélectionner selon les attributs du programme, comme indiqué dans le tableau suivant.

  • mainCRTStartup (ou wmainCRTStartup ) une application utilisant /SUBSYSTEM:CONSOLE; les appels principal (ou wmain )

  • WinMainCRTStartup (ou wWinMainCRTStartup ) une application utilisant /SUBSYSTEM:WINDOWS; appelle WinMain (ou wWinMain ), qui doit être défini par __stdcall

  • _DllMainCRTStartup a DLL; appels DllMain , qui doit être défini avec __stdcall , s'il existe

8
répondu sarat 2012-12-14 03:37:21

un programme C standard est passé 2 paramètres par la ligne de commande au démarrage:

int main( int argc, char** argv ) ;
  • char** argv est un tableau de cordes ( char* )
  • int argc est le numéro de char* dans argv

la fonction de démarrage WinMain que les programmeurs doivent écrire pour un programme windows est légèrement différente. WinMain prend 4 paramètres qui sont passés au programme par Win O / S au démarrage:

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.

Voir mon article comment créer une fenêtre de base dans C pour plus

2
répondu bobobobo 2012-12-14 02:03:31

je me souviens vaguement avoir lu quelque part que les programmes Windows ont une fonction main() . Il est juste caché dans un en-tête ou une bibliothèque quelque part. Je crois que cette fonction main() initialise toutes les variables nécessaires à WinMain() et l'appelle ensuite.

bien sûr, je suis un WinAPI noob, donc j'espère que d'autres qui sont mieux informés me corrigeront si je me trompe.

1
répondu Code-Apprentice 2012-12-14 02:09:45