web-dev-qa-db-fra.com

Compiler un DLL en C / C ++, puis l'appeler à partir d'un autre programme

Je veux faire un simple, simple DLL qui exporte une ou deux fonctions, puis essayez de l'appeler à partir d'un autre programme ... Partout où j'ai regardé jusqu'à présent, c'est pour des questions compliquées, différentes façons de relier les choses ensemble, des problèmes étranges que je n'ai même pas commencé de réaliser qu'il existe encore ... Je veux juste commencer, en faisant quelque chose comme ça:

Créez un DLL qui exporte certaines fonctions, comme,

int add2(int num){
   return num + 2;
}

int mult(int num1, int num2){
   int product;
   product = num1 * num2;
   return product;
}

Je compile avec MinGW, j'aimerais le faire en C, mais s'il y a de réelles différences en C++, j'aimerais aussi les connaître. Je veux savoir comment charger ce DLL dans un autre programme C (et C++), puis appeler ces fonctions à partir de celui-ci. Mon objectif ici, après avoir joué avec les DLL pendant un moment, est de faire un VB front-end pour le code C (++), en chargeant les DLL dans Visual Basic (j'ai Visual Studio 6, je veux juste créer des formulaires et des événements pour les objets sur ceux-ci) formulaires, qui appellent la DLL).

J'ai besoin de savoir comment appeler gcc (/ g ++) pour lui faire créer une DLL, mais aussi comment écrire (/ générer) un fichier d'export ... et ce que je peux/ne peux pas faire dans un DLL (comme, puis-je prendre des arguments par pointeur/référence à partir du front-end VB? Peut le DLL appeler une fonction théorique dans le front- Ou demandez à une fonction de prendre un "pointeur de fonction" (je ne sais même pas si c'est possible) de VB et appelez-le?) Je suis assez certain que je ne peux pas passer un variante de la DLL ... mais c'est tout ce que je sais vraiment.

mettre à jour à nouveau

D'accord, j'ai compris comment le compiler avec gcc, pour faire le DLL que j'ai couru

gcc -c -DBUILD_DLL dll.c
gcc -shared -o mydll.dll dll.o -Wl,--out-implib,libmessage.a

et puis j'ai eu un autre programme le charger et tester les fonctions, et cela a très bien fonctionné, merci beaucoup pour les conseils, mais j'ai essayé de le charger avec VB6, comme ceci

Public Declare Function add2 Lib "C:\c\dll\mydll.dll" (num As Integer) As Integer

alors je viens d'appeler add2 (text1.text) à partir d'un formulaire, mais cela m'a donné une erreur d'exécution:

"Impossible de trouver DLL point d'entrée add2 dans C:\c\dll\mydll.dll"

c'est le code que j'ai compilé pour la DLL:

#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif

EXPORT int __stdcall add2(int num){
  return num + 2;
}

EXPORT int __stdcall mul(int num1, int num2){
  return num1 * num2;
}

l'appeler depuis le programme C comme celui-ci a fonctionné, cependant:

#include<stdio.h>
#include<windows.h>

int main(){

  HANDLE ldll;
  int (*add2)(int);
  int (*mul)(int,int);

  ldll = LoadLibrary("mydll.dll");
  if(ldll>(void*)HINSTANCE_ERROR){
    add2 = GetProcAddress(ldll, "add2");
    mul = GetProcAddress(ldll, "mul");
    printf("add2(3): %d\nmul(4,5): %d", add2(3), mul(4,5));
  } else {
    printf("ERROR.");
  }

}

des idées?

résolu

Pour résoudre le problème précédent, je devais simplement le compiler comme suit:

gcc -c -DBUILD_DLL dll.c
gcc -shared -o mydll.dll dll.o -Wl,--add-stdcall-alias

et utiliser cet appel API dans VB6

Public Declare Function add2 Lib "C:\c\dll\mydll" _
    (ByVal num As Integer) As Integer

J'ai appris à ne pas oublier de spécifier explicitement ByVal ou ByRef - je revenais juste de l'adresse de l'argument que j'ai passé, il ressemblait à -3048.

56
Carson Myers

Concernant la construction d'un DLL utilisant MinGW, voici quelques instructions très brèves.

Tout d'abord, vous devez marquer vos fonctions pour l'exportation, afin qu'elles puissent être utilisées par les appelants de la DLL. Pour ce faire, modifiez-les afin qu'elles ressemblent (par exemple)

__declspec( dllexport ) int add2(int num){
   return num + 2;
}

puis, en supposant que vos fonctions se trouvent dans un fichier appelé funcs.c, vous pouvez les compiler:

gcc -shared -o mylib.dll funcs.c

L'indicateur -shared indique à gcc de créer une DLL.

Pour vérifier si l'outil DLL a réellement exporté les fonctions, saisissez l'outil gratuit Dependency Walker et utilisez-le pour examiner la DLL.

Pour un IDE qui automatisera tous les drapeaux, etc. nécessaires pour construire des DLL, jetez un œil à l'excellent Code :: Blocks , qui fonctionne très bien avec MinGW .

Edit: Pour plus de détails à ce sujet, consultez l'article Création d'un MinGW DLL pour une utilisation avec Visual Basic sur le MinGW Wiki.

24
anon

Voici comment procéder:

En .h

#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif

extern "C" // Only if you are using C++ rather than C
{    
  EXPORT int __stdcall add2(int num);
  EXPORT int __stdcall mult(int num1, int num2);
}

en .cpp

extern "C" // Only if you are using C++ rather than C
{    
EXPORT int __stdcall add2(int num)
{
  return num + 2;
}


EXPORT int __stdcall mult(int num1, int num2)
{
  int product;
  product = num1 * num2;
  return product;
}
}

La macro indique à votre module (c'est-à-dire vos fichiers .cpp) qu'il fournit les éléments de la DLL au monde extérieur. Les personnes qui incluent votre fichier .h veulent importer les mêmes fonctions, elles vendent donc EXPORT comme indiquant à l'éditeur de liens d'importer. Vous devez ajouter BUILD_DLL aux options de compilation du projet, et vous voudrez peut-être le renommer en quelque chose de bien spécifique à votre projet (au cas où une DLL utilise votre DLL).

Vous devrez peut-être également créer un fichier . Def pour renommer les fonctions et désobscurcir les noms (C/C++ modifie ces noms). Cette entrée de blog pourrait être un point de départ intéressant à ce sujet.

Le chargement de vos propres DLL personnalisées est comme le chargement de DLL système. Assurez-vous simplement que le DLL est sur votre chemin système. C:\windows\ou le répertoire de travail de votre application sont un endroit facile pour mettre votre dll.

7
Tom Leys

Il n'y a qu'une seule différence. Vous devez faire attention ou nommer mangling win C++. Mais sur Windows, vous devez faire attention à 1) diminuer les fonctions à exporter à partir du DLL 2) écrire un soi-disant fichier .def qui répertorie tous les symboles exportés.

Dans Windows lors de la compilation d'un DLL doivent utiliser

__declspec (dllexport)

mais en l'utilisant, vous devez écrire __declspec (dllimport)

Donc, la façon habituelle de le faire est quelque chose comme

#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif

La dénomination est un peu déroutante, car elle est souvent nommée EXPORT .. Mais c'est ce que vous trouverez dans la plupart des en-têtes quelque part. Donc, dans votre cas, vous écririez (avec le #define ci-dessus)

int DLL_EXPORT add .... int DLL_EXPORT mult ...

N'oubliez pas que vous devez ajouter la directive Preprocessor BUILD_DLL lors de la création de la bibliothèque partagée.

Cordialement Friedrich

5
Friedrich

La chose à surveiller lors de l'écriture de dlls C++ est le changement de nom. Si vous voulez l'interopérabilité entre C et C++, vous feriez mieux d'exporter des fonctions de style C non mutilées depuis la DLL.

Vous avez deux options pour utiliser une DLL

  • Soit utiliser un fichier lib pour lier les symboles - compiler la liaison dynamique de temps
  • Utilisez LoadLibrary() ou une fonction appropriée pour charger la bibliothèque, récupérez un pointeur de fonction (GetProcAddress) et appelez-le - liaison dynamique à l'exécution

L'exportation de classes ne fonctionnera pas si vous suivez la deuxième méthode.

4
dirkgently

Pour VB6:

Vous devez déclarer vos fonctions C en tant que __stdcall, sinon vous obtenez des erreurs de type "convention d'appel invalide". À propos de vos autres questions:

puis-je prendre des arguments par pointeur/référence à partir du frontal VB?

Oui, utilisez les modificateurs ByRef/ByVal.

Le DLL peut-il appeler une fonction théorique dans le front-end?

Oui, utilisez l'instruction AddressOf. Vous devez passer le pointeur de fonction à dll avant.

Ou demander à une fonction de prendre un "pointeur de fonction" (je ne sais même pas si c'est possible) de VB et de l'appeler?)

Oui, utilisez l'instruction AddressOf.

mise à jour (plus de questions sont apparues:)):

pour le charger dans VB, dois-je simplement faire la méthode habituelle (ce que je ferais pour charger winsock.ocx ou un autre runtime, mais trouver mon DLL à la place) ou dois-je mettre un appel API dans un module?

Vous devez détartrer la fonction API dans le code VB6, comme ci-dessous:

Private Declare Function SHGetSpecialFolderLocation Lib "Shell32" _
   (ByVal hwndOwner As Long, _
    ByVal nFolder As Long, _
    ByRef pidl As Long) As Long
3
Arvo