web-dev-qa-db-fra.com

Exportation de fonctions depuis un DLL avec dllexport

J'aimerais un exemple simple d'exportation d'une fonction à partir d'une DLL Windows C++.

J'aimerais voir l'en-tête, le fichier cpp et le fichier def (si absolument requis).

Je voudrais que le nom exporté soit non décoré . J'aimerais utiliser la convention d'appel la plus standard (__stdcall?). Je voudrais utiliser __ 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 que l'éditeur de liens ajoute des traits de soulignement et/ou des chiffres (nombre d'octets?) Au nom.

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

[~ # ~] met à jour [~ # ~]

Ne pas inclure la convention d'appel (et utiliser extern "C") me donne les noms d'exportation comme je le souhaite, mais qu'est-ce que cela signifie? Quelle que soit la convention d'appel par défaut utilisée, ce que Pinvoke (.NET), declare (vb6) et GetProcAddress attendrait (Je suppose que pour GetProcAddress, cela dépend du pointeur de fonction créé par l'appelant).

Je veux que ceci DLL soit utilisé sans fichier d’en-tête, je n’ai donc pas vraiment besoin de beaucoup de fantaisie #defines pour rendre l’en-tête utilisable par un appelant.

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

92
Aardvark

Si vous voulez des exportations C simples, utilisez un projet C et non C++. Les DLL C++ reposent sur la gestion de noms pour tous les ismes C++ (espaces de noms, etc.). Vous pouvez compiler votre code en tant que C en allant dans les paramètres de votre projet sous C/C++ -> Avancé. Il existe une option "Compiler en tant que" qui correspond aux commutateurs du compilateur/TP et/TC.

Exportation/importation 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 project:

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

Ensuite, sur une fonction que vous souhaitez exporter, vous utilisez LIBRARY_API:

LIBRARY_API int GetCoolInteger();

Dans votre projet de construction de bibliothèque, créez un define LIBRARY_EXPORTS ceci entraînera l'exportation de vos fonctions pour votre DLL build.

Puisque LIBRARY_EXPORTS ne sera pas défini dans un projet utilisant 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 n’importe quoi lorsque vous n’êtes 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 les 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 différentes manières, je pense que dllexport/dllimport est la méthode recommandée parmi les deux.

Exportation de fonctions non liées à partir d’un C++ DLL pour LoadLibrary/PInvoke

Si vous en avez besoin pour utiliser LoadLibrary et GetProcAddress, ou peut-être faire PInvoke à partir 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, il suffit d'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 à quoi ressemblent les exportations avec Dumpbin/exports:

  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 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")
);
121
joshperry

Pour C++:

Je viens de faire face au même problème et je pense qu’il est utile de mentionner qu’un problème se pose lorsqu’on utilise à la fois __stdcall (Ou WINAPI) et extern "C":

Comme vous le savez, extern "C" Supprime la décoration de sorte qu'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 modifie la convention d'appel) décore également les noms de sorte que si nous utilisons les deux, nous obtenons:

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

et l'avantage de extern "C" est perdu car le symbole est décoré (avec _bytes)

Notez que ceci seulement se produit pour une architecture x86 car la convention __stdcall Est ignorée sur x64 ( msdn : sur des architectures x64, par convention , les arguments sont passés dans les registres lorsque cela est possible, et les arguments suivants sont passés sur la pile.).

Ceci est particulièrement difficile si vous ciblez à la fois les plates-formes x86 et x64.


Deux solutions

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

  2. le plus simple: définissez la macro (voir msdn ):

#define EXPORT comment (éditeur de liens, "/ EXPORT:" __FUNCTION__ "=" __FUNCDNAME__)

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

#pragma EXPORT

Exemple complet:

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

Cela exportera la fonction non décorée pour les cibles x86 et x64 tout en préservant la convention __stdcall Pour x86. La __declspec(dllexport) n'est pas requise dans ce cas.

23
Malick

J'ai eu exactement le même problème, ma solution a été d'utiliser un fichier de définition de module (.def) au lieu de __declspec(dllexport) pour définir les exportations ( http://msdn.Microsoft.com/en-us/ bibliothèque/d91k01sh.aspx ). Je ne sais pas pourquoi ça marche, mais ça marche

3
Rytis I