cdecl ou stdcall sur Windows?

je suis en train de développer une bibliothèque C++ Pour Windows qui sera distribuée comme DLL. Mon but est de maximiser l'interopérabilité binaire; plus précisément, les fonctions de ma DLL doivent être utilisables à partir du code compilé avec plusieurs versions de MSVC++ et MinGW sans avoir à recompiler la DLL. Cependant, je ne sais pas quelle convention est la meilleure., cdecl ou stdcall .

parfois j'entends des affirmations comme "la convention de calling C est la seule garantie d'être les mêmes compilateurs accross", ce qui contraste avec des affirmations comme il y a quelques variations dans l'interprétation de cdecl , en particulier dans la façon de retourner les valeurs ". Cela ne semble pas empêcher certains développeurs de bibliothèques (comme libsndfile ) d'utiliser la convention d'appel C dans les DLLs qu'ils distribuent, sans aucun problème visible.

sur le d'autre part, la Convention d'appel stdcall semble être bien définie. D'après ce qu'on m'a dit, tous les compilateurs Windows sont fondamentalement tenus de le suivre parce que c'est la convention utilisée pour Win32 et COM. Ceci est basé sur l'hypothèse qu'un compilateur Windows sans support Win32/COM ne serait pas très utile. Un grand nombre de snippets de code affichés sur les forums déclarent des fonctions comme stdcall mais je ne semble pas trouver un seul poste qui explique clairement pourquoi .

il y a trop d'informations contradictoires là-bas, et chaque recherche que je fais me donne des réponses différentes qui ne m'aident pas vraiment à décider entre les deux. Je cherche une explication claire, détaillée et argumentée quant à savoir pourquoi je devrais choisir l'un plutôt que l'autre (ou pourquoi les deux sont équivalents).

notez que cette question ne s'applique pas seulement aux fonctions "classiques", mais aussi aux appels de fonction de membre virtuel, puisque la plupart des codes clients vont s'interfacer avec ma DLL à travers "interfaces", classes virtuelles pures (suivant les modèles décrits par exemple ici et ).

66
demandé sur Community 2011-06-28 22:07:57

3 réponses

je viens de faire quelques tests dans le monde réel (compiler des DLLs et des applications avec MSVC++ et MinGW, puis les mélanger). Comme il semble, j'ai eu de meilleurs résultats avec la Convention d'appel cdecl .

plus spécifiquement: le problème avec stdcall est que MSVC++ mangles names in the DLL export table, même en utilisant extern "C" . Par exemple foo devient _foo@4 . Cela ne se produit qu'en utilisant __declspec(dllexport) , pas en utilisant un fichier DEF; cependant, Les fichiers DEF sont un problème de maintenance à mon avis, et je ne veux pas les utiliser.

le changement de nom MSVC++ pose deux problèmes:

  • utilisant GetProcAddress sur la DLL devient légèrement plus compliqué;
  • MinGW par défaut ne prépare pas un undescore aux noms décorés (par exemple MinGW utilisera foo@4 au lieu de _foo@4 ), ce qui complique le lien. Aussi, il introduit le risque de voir apparaître dans la nature des "versions non soulignées" des DLLs et des applications qui sont incompatibles avec les "versions soulignées".

j'ai essayé la cdecl convention: l'interopérabilité entre MSVC++ et MinGW fonctionne parfaitement, out-of-the-box, et les noms de séjour non décoré dans le DLL table d'exportation. Il fonctionne même pour les méthodes virtuelles.

Pour ces raisons, cdecl est un gagnant clair pour moi.

66
répondu Etienne Dechamps 2011-07-09 13:48:35

la plus grande différence dans les deux conventions d'appel est que " _ _cdecl " place le fardeau de l'équilibrage de la pile après un appel de fonction sur l'appelant, ce qui permet des fonctions avec des quantités variables d'arguments. La convention" _ _ _ stdcall "est" plus simple " par nature, mais elle est moins souple à cet égard.

aussi, je crois que les langues gérées utilisent la convention stdcall par défaut, donc toute personne utilisant P / Invoke dessus devrait explicitement indiquez la Convention d'appel si vous utilisez cdecl.

donc, si toutes vos signatures de fonction doivent être définies statiquement, je pencherais probablement vers stdcall, sinon cdecl.

15
répondu Brandon Moretz 2011-06-28 18:25:54

en termes de sécurité, la convention __cdecl est" plus sûre " parce que c'est l'appelant qui doit désallouer la pile. Ce qui peut se passer dans une bibliothèque __stdcall , c'est que le développeur peut avoir oublié de désallouer la pile correctement, ou qu'un attaquant peut injecter du code en corrompant la pile de la DLL (par exemple par L'intermédiaire de L'API hooking) qui n'est alors pas vérifiée par l'appelant. Je n'ai pas d'exemples de sécurité CVS qui montrent que mon intuition est correcte.

7
répondu user2471214 2014-06-13 13:49:29