Exporter des fonctions à partir d'une DLL avec dllexport

je voudrais un exemple simple d'exportation d'une fonction à partir D'une DLL C++ windows.

j'aimerais voir l'en-tête, le fichier cpp et le fichier def (si c'est absolument nécessaire).

j'aimerais que le nom exporté soit non décorée . Je voudrais utiliser la convention d'appel la plus standard (__stdcall?). Je voudrais l'utilisation _ _ _ declspec (dllexport) et ne pas avoir à utiliser un fichier DEF.

par exemple:

  //header
  extern "C"
  {
   __declspec(dllexport) int __stdcall foo(long bar);
  }

  //cpp
  int __stdcall foo(long bar)
  {
    return 0;
  }

j'essaie d'éviter le linker ajouté souligne et/ou des nombres (byte counts?) pour le nom.

ça me va de ne pas prendre en charge dllimport et dllexport en utilisant le même en-tête. Je ne veux aucune information sur l'exportation des méthodes de Classe C++, juste des fonctions globales de style c.

mise à JOUR

non compris la Convention d'appel (et l'utilisation de " C " externe) me donne les noms d'exportation comme je veux, mais qu'est-ce que cela signifie? Est-ce que quelle que soit la convention d'appel par défaut je reçois ce que pinvoke (.NET), declare (vb6), et GetProcAddress attendraient? (Je suppose que pour GetProcAddress, cela dépend du pointeur de fonction que l'appelant a créé).

je veux que cette DLL pour être utilisé sans un fichier d'en-tête, donc je n'ai pas vraiment besoin de beaucoup de fantaisie #définit l'en-tête utilisable par l'appelant.

je suis D'accord avec une réponse étant que je dois utiliser un fichier DEF.

82
demandé sur Aardvark 2009-02-11 21:28:23

4 réponses

si vous voulez exporter du C simple, utilisez un projet C et non C++. Les DLLs C++ s'appuient sur le nom-mangling pour tous les isms C++(namespaces etc...). Vous pouvez compiler votre code en C en allant dans les paramètres de votre projet sous C/C++->avancé, il y a une option "compiler sous" qui correspond aux commutateurs du compilateur /TP et /TC.

Exporter/Importer DLL Libs in VC++

ce que vous voulez vraiment faire est de définir une macro conditionnelle dans un en-tête qui sera inclus dans tous les fichiers source de votre projet DLL:

#ifdef LIBRARY_EXPORTS
#    define LIBRARY_API __declspec(dllexport)
#else
#    define LIBRARY_API __declspec(dllimport)
#endif

puis sur une fonction que vous voulez exporter vous utilisez LIBRARY_API :

LIBRARY_API int GetCoolInteger();

dans votre projet de construction de bibliothèque Créer un définir LIBRARY_EXPORTS cela va causer vos fonctions à être exportées pour votre DLL construire.

depuis LIBRARY_EXPORTS ne sera pas défini dans un projet consommant la DLL, lorsque ce projet inclut le fichier d'en-tête de votre Bibliothèque toutes les fonctions seront importées à la place.

si votre bibliothèque doit être multiplate-forme, vous pouvez définir LIBRARY_API comme rien quand elle n'est pas sous Windows:

#ifdef _WIN32
#    ifdef LIBRARY_EXPORTS
#        define LIBRARY_API __declspec(dllexport)
#    else
#        define LIBRARY_API __declspec(dllimport)
#    endif
#elif
#    define LIBRARY_API
#endif

lorsque vous utilisez dllexport/dllimport vous n'avez pas besoin d'utiliser des fichiers DEF, si vous utilisez des fichiers DEF vous n'avez pas besoin d'utiliser dllexport / dllimport. Les deux méthodes accomplissent la même tâche de façons différentes, je crois que dllexport / dllimport est la méthode recommandée parmi les deux.

l'Exportation de unmangled les fonctions d'une DLL C++ pour LoadLibrary/PInvoke

si vous avez besoin de ceci pour utiliser LoadLibrary et GetProcAddress, ou peut-être faire PInvoke de .NET vous pouvez utiliser extern "C" en ligne avec votre dllexport. Et puisque nous utilisons GetProcAddress au lieu de dllimport, nous n'avons pas besoin de faire la danse ifdef d'en haut, juste un simple dllexport:

Le Code:

#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)

EXTERN_DLL_EXPORT int getEngineVersion() {
  return 1;
}

EXTERN_DLL_EXPORT void registerPlugin(Kernel &K) {
  K.getGraphicsServer().addGraphicsDriver(
    auto_ptr<GraphicsServer::GraphicsDriver>(new OpenGLGraphicsDriver())
  );
}

et voici ce que les exportations ressemble avec Dumpbin /exportations de:

  Dump of file opengl_plugin.dll

  File Type: DLL

  Section contains the following exports for opengl_plugin.dll

    00000000 characteristics
    49866068 time date stamp Sun Feb 01 19:54:32 2009
        0.00 version
           1 ordinal base
           2 number of functions
           2 number of names

    ordinal hint RVA      name

          1    0 0001110E getEngineVersion = @ILT+265(_getEngineVersion)
          2    1 00011028 registerPlugin = @ILT+35(_registerPlugin)

donc ce code fonctionne très bien:

m_hDLL = ::LoadLibrary(T"opengl_plugin.dll");

m_pfnGetEngineVersion = reinterpret_cast<fnGetEngineVersion *>(
  ::GetProcAddress(m_hDLL, "getEngineVersion")
);
m_pfnRegisterPlugin = reinterpret_cast<fnRegisterPlugin *>(
  ::GetProcAddress(m_hDLL, "registerPlugin")
);
106
répondu joshperry 2016-05-27 02:34:32

Pour C++:

je viens de faire face à la même question et je pense qu'il est intéressant de mentionner un problème apparaît quand on utilise à la fois __stdcall (ou WINAPI ) et extern "C" :

Comme vous le savez "151970920 supprime l'effet de la décoration de sorte que, au lieu de :

__declspec(dllexport) int Test(void)                        --> dumpbin : ?Test@@YaHXZ

vous obtenez un nom de symbole non décoré:

extern "C" __declspec(dllexport) int Test(void)             --> dumpbin : Test

cependant le _stdcall (=macro WINAPI, qui change la convention d'appel) décore aussi les noms de sorte que si nous utilisons les deux nous obtenons:

   extern "C" __declspec(dllexport) int WINAPI Test(void)   --> dumpbin : _Test@0

et le bénéfice de extern "C" est perdu parce que le symbole est décoré (avec _ @bytes)

notez que ce seulement se produit pour l'architecture x86 parce que la convention __stdcall est ignorée sur x64 ( msdn : on x64 architectures, par convention, les arguments sont passés dans les registres lorsque c'est possible, et les arguments suivants sont passés sur la pile .).

C'est particulièrement délicat si vous ciblez à la fois les plateformes x86 et x64.


deux solutions

  1. utilisez un fichier de définition. Mais cela vous oblige à maintenir l'état de la fichier def.

  2. la manière la plus simple : définir la macro (voir msdn ) :

#define EXPORTATION commentaire(linker, "/EXPORT:" __FONCTION__ "=" __ FUNCDNAME__)

et ensuite inclure le pragma suivant dans le corps de fonction:

#pragma EXPORT

Exemple Complet:

 int WINAPI Test(void)
{
    #pragma EXPORT
    return 1;
}

ceci exportera la fonction non décorée pour les cibles x86 et x64 tout en conservant la convention __stdcall pour x86. Le __declspec(dllexport) n'est pas requis dans ce cas.

17
répondu Malick 2017-02-01 14:04:31

j'avais exactement le même problème, ma solution était d'utiliser le fichier de définition de module (.def) au lieu de __declspec(dllexport) pour définir les exportations ( http://msdn.microsoft.com/en-us/library/d91k01sh.aspx ). Je n'ai aucune idée pourquoi cela fonctionne, mais il ne

3
répondu Rytis I 2013-09-23 14:34:09

je pense que _naked pourrait obtenir ce que vous voulez, mais il empêche également le compilateur de générer le code de gestion de la pile pour la fonction. extern "C" cause c style name decoration. Enlevez ça et ça devrait vous débarrasser de vos "s". Le linker n'ajoute pas les underscores, le compilateur ajoute. stdcall fait ajouter la taille de la pile d'arguments.

pour plus, voir: http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx

La grande question est: pourquoi voulez-vous faire? Quel est le problème avec les noms déformés?

-1
répondu Rob K 2009-02-11 18:40:03