Partager une variable globale / statique entre un processus et DLL

j'aimerais partager statique/variable globale qu'entre un processus et un dll qui est invoquée par le processus. L'exe et la dll sont dans le même espace d'adresse de mémoire. Je ne veux pas que la variable soit partagée entre d'autres processus.


élaboration du problème:

disent qu'il y a une variable statique/globale x dans a.cpp . Les deux exe foo.exe et la dll bar.dll ont a.cpp , donc la variable x est dans les deux images.

Maintenant, foo.exe charge dynamiquement (ou statique) bar.dll . Ensuite, le problème est de savoir si la variable x est partagée par l'exe et dll, ou non.

dans Windows, ces deux gars jamais partager le x : l'exe et dll auront une copie séparée de x . Cependant, dans Linux, l'exe et dll ne partagent la variable x .

malheureusement, je veux le comportement de Linux. J'ai d'abord envisagé d'utiliser pragma data_seg sur Windows. Cependant, même si j'ai correctement configuré le segment de données partagées, foo.exe et bar.dll ne partage jamais le x . Rappelons que bar.dll est chargé dans l'espace d'adresse de foo.exe . Cependant, si j'exécute une autre instance de foo.exe , alors x est partagé. Mais je ne veux pas que x soit partagé par des processus différents. Ainsi, l'utilisation de data_seg a échoué.

je peux utiliser un fichier mappé en mémoire en faisant un nom unique entre exe et dll, ce que j'essaie maintenant.


deux questions:

  1. pourquoi le comportement de Linux et Windows est-il différent? Quelqu'un peut-il expliquer cela plus en détail?
  2. quelle serait la meilleure façon de résoudre ce problème sous Windows?
21
demandé sur minjang 2011-02-06 09:32:55

8 réponses

tout d'abord, j'ai trouvé que cet article était une lecture très intéressante et concise sur les bibliothèques de liens dynamiques (l'article est seulement spécifique à Linux, mais les concepts s'appliquent sûrement à windows aussi bien et vous pourriez obtenir un aperçu quant au comportement différent que vous voyez). Surtout la différence fondamentale entre chargement statique et dynamique.

je pense que ce que vous voulez ou essayez d'implémenter est un modèle" cross-module singleton". Si vous lisez les réponses à ce fil , Je ne sais pas comment je pourrais éventuellement répondre à votre question mieux que Ben Voigt a répondu à ce post. J'ai déjà implémenté un module cross-module singleton (quelques fois en fait) en utilisant la méthode qu'il décrit, et cela fonctionne comme un charme.

bien sûr, vous ne serez pas en mesure de conserver la propreté de simplement avoir la variable globale assis là dans le fichier cpp. Vous devrez utiliser un pointeur statique et certains fonctions d'accesseur et comptage de référence. Mais ça peut marcher. Je ne suis pas sûr qu'il soit possible d'éviter ce foo.exe et foo.exe partage la même instance de données globales une barre.dll, Je n'ai jamais eu à faire ça et ne peux pas vraiment penser à un moyen de le faire, désolé.

8
répondu Mikael Persson 2017-05-23 11:54:56

pour obtenir le comportement de linux où à la fois le programme principal et une dll partagent le même x , vous pouvez exporter cette variable soit de la dll, ou le programme principal. L'autre module doit importer cette variable.

vous faites ceci en utilisant des fichiers DEF ( voir la documentation de microsoft ), ou en marquant les utilisations avec la variable __declspec(dllexport) où il est défini, et __declspec(dllimport) dans tout autre module il est utilisé ( voir la documentation de microsoft ). C'est la même chose que la façon dont n'importe quelle fonction, objet, ou variable est partagée entre les modules dans windows.

dans le cas où vous souhaitez qu'un programme charge une bibliothèque à l'exécution, mais le programme principal peut avoir à utiliser la variable avant que la bibliothèque soit chargée, le programme devrait exporter la variable, et la dll devrait l'importer. Il ya un peu d'un problème de poulet et d'œuf ici parce que la dll dépend du programme principal, et le le programme principal dépend de la dll. Voir http://www.lurklurk.org/linkers/linkers.html#wincircular

j'ai écrit un exemple de la façon dont vous pouvez faire ceci en utilisant à la fois le compilateur de Microsoft et mingw (gcc dans windows), y compris toutes les différentes façons dont un programme et une bibliothèque peuvent se lier (statically, dll chargé au démarrage du programme, dll chargé pendant l'exécution)""

principal.h

#ifndef MAIN_H
#define MAIN_H

// something that includes this
// would #include "linkage_importing.h"
// or #include "linkage_exporting.h"
// as appropriate

#ifndef EXPLICIT_MAIN
LINKAGE int x;
#endif // EXPLICIT_MAIN

#endif // MAIN_H

principal.C

#ifdef EXPLICIT_DLL
#include "dyn_link.h"
#endif // EXPLICIT_DLL
#include <stdio.h>
#include "linkage_exporting.h"
#include "main.h"
#include "linkage_importing.h"
#include "dll.h"

FNCALL_DLL get_call_dll(void);

int main(int argc, char* argv[])
{
   FNCALL_DLL fncall_dll;
   fncall_dll = get_call_dll();
   if (fncall_dll)
   {
      x = 42;
      printf("Address of x as seen from main() in main.c: %p\n", &x);
      printf("x is set to %i in main()\n", x);
      fncall_dll();
      // could also be called as (*fncall_dll)();
      // if you want to be explicit that fncall_dll is a function pointer
      printf("Value of x as seen from main() after call to call_dll(): %i\n", x);
   }
   return 0;
}

FNCALL_DLL get_call_dll(void)
{
#ifdef EXPLICIT_DLL
   return get_ptr("dll.dll", "call_dll");
#else
   return call_dll;
#endif // EXPLICIT_DLL
}
"1519210920 les" dll".h

#ifndef DLL_H
#define DLL_H

// something that includes this
// would #include "linkage_importing.h"
// or #include "linkage_exporting.h"
// as appropriate

// declaration of type to hold a
// pointer to the function
typedef void(*FNCALL_DLL)(void);

#ifndef EXPLICIT_DLL
LINKAGE void call_dll(void);
#endif // EXPLICIT_DLL

#endif // DLL_H
"1519210920 les" dll".c

#ifdef EXPLICIT_MAIN
#include "dyn_link.h"
#endif // EXPLICIT_MAIN
#include <stdio.h>
#include "linkage_importing.h"
#include "main.h"
#include "linkage_exporting.h"
#include "dll.h"

int* get_x_ptr(void);

LINKAGE void call_dll(void)
{
   int* x_ptr;
   x_ptr = get_x_ptr();
   if (x_ptr)
   {
      printf("Address of x as seen from call_dll() in dll.c: %p\n", x_ptr);
      printf("Value of x as seen in call_dll: %i()\n", *x_ptr);
      *x_ptr = 31415;
      printf("x is set to %i in call_dll()\n", *x_ptr);
   }
}

int* get_x_ptr(void)
{
#ifdef EXPLICIT_MAIN
   return get_ptr("main.exe", "x");   // see note in dyn_link.c about using the main program as a library
#else
   return &x;
#endif //EXPLICIT_MAIN
}

dyn_link.h

#ifndef DYN_LINK_H
#define DYN_LINK_H

// even though this function is used by both, we link it
// into both main.exe and dll.dll as necessary.
// It's not shared in a dll, because it helps us load dlls :)
void* get_ptr(const char* library, const char* object);

#endif // DYN_LINK_H

dyn_link.c

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

void* get_ptr(const char* library, const char* object)
{
   HINSTANCE hdll;
   FARPROC ptr;
   hdll = 0;
   ptr = 0;

   hdll = LoadLibrary(library);
   // in a better dynamic linking library, there would be a
   // function that would call FreeLibrary(hdll) to cleanup
   //
   // in the case where you want to load an object in the main
   // program, you can use
   // hdll = GetModuleHandle(NULL);
   // because there's no need to call LoadLibrary on the
   // executable if you can get its handle by some other means.

   if (hdll)
   {
      printf("Loaded library %s\n", library);
      ptr = GetProcAddress(hdll, object);
      if (ptr)
      {
         printf("Found %s in %s\n", object, library);
      } else {
         printf("Could not find %s in %s\n", object, library);
      }
   } else {
      printf("Could not load library %s\n", library);
   }
   return ptr;
}

linkage_important.h

// sets up some macros to handle when to use "__declspec(dllexport)",
// "__declspec(dllimport)", "extern", or nothing.

// when using the LINKAGE macro (or including a header that does):
//    use "#include <linkage_importing.h>" to make the LINKAGE macro
//    do the right thing for importing (when using functions,
//    variables, etc...)
//
//    use "#include <linkage_exporting.h>" to make the LINKAGE macro
//    do the right thing for exporting (when declaring functions,
//    variables, etc).
//
//    You can include either file at any time to change the meaning of
//    LINKAGE.

// if you declare NO_DLL these macros do not use __declspec(...), only
// "extern" as appropriate

#ifdef LINKAGE
#undef LINKAGE
#endif
#ifdef NO_DLL
   #define LINKAGE extern
#else
   #define LINKAGE extern __declspec(dllimport)
#endif

linkage_exporting.h

// See linkage_importing.h to learn how this is used
#ifdef LINKAGE
#undef LINKAGE
#endif
#ifdef NO_DLL
   #define LINKAGE
#else
   #define LINKAGE __declspec(dllexport)
#endif

construire mingw explicite both.sh

#! /bin/bash
echo Building configuration where both main
echo and dll link explicitly to each other
rm -rf mingw_explicit_both
mkdir -p mingw_explicit_both/obj
cd mingw_explicit_both/obj

# compile the source code (dll created with position independent code)
gcc -c -fPIC -DEXPLICIT_MAIN ../../dll.c
gcc -c -DEXPLICIT_DLL ../../main.c
gcc -c ../../dyn_link.c

#create the dll from its object code the normal way
gcc -shared -odll.dll dll.o dyn_link.o -Wl,--out-implib,libdll.a

# create the executable
gcc -o main.exe main.o dyn_link.o

mv dll.dll ..
mv main.exe ..
cd ..

construire mingw explicite dll.sh

#! /bin/bash
echo Building configuration where main explicitly
echo links to dll, but dll implicitly links to main
rm -rf mingw_explicit_dll
mkdir -p mingw_explicit_dll/obj
cd mingw_explicit_dll/obj

# compile the source code (dll created with position independent code)
gcc -c -fPIC ../../dll.c
gcc -c -DEXPLICIT_DLL ../../main.c
gcc -c ../../dyn_link.c

# normally when linking a dll, you just use gcc
# to create the dll and its linking library (--out-implib...)
# But, this dll needs to import from main, and main's linking library doesn't exist yet
# so we create the linking library for main.o
# make sure that linking library knows to look for symbols in main.exe (the default would be a.out)
gcc -omain.exe -shared main.o -Wl,--out-implib,main.a  #note this reports failure, but it's only a failure to create main.exe, not a failure to create main.a

#create the dll from its object code the normal way (dll needs to know about main's exports)
gcc -shared -odll.dll dll.o dyn_link.o main.a -Wl,--out-implib,libdll.a

# create the executable
gcc -o main.exe main.o dyn_link.o

mv dll.dll ..
mv main.exe ..
cd ..

construire mingw explicite main.sh

#! /bin/bash
echo Building configuration where dll explicitly
echo links to main, but main implicitly links to dll
rm -rf mingw_explicit_main
mkdir -p mingw_explicit_main/obj
cd mingw_explicit_main/obj

# compile the source code (dll created with position independent code)
gcc -c -fPIC -DEXPLICIT_MAIN ../../dll.c
gcc -c ../../main.c
gcc -c ../../dyn_link.c

# since the dll will link dynamically and explicitly with main, there is no need
# to create a linking library for main, and the dll can be built the regular way
gcc -shared -odll.dll dll.o dyn_link.o -Wl,--out-implib,libdll.a

# create the executable (main still links with dll implicitly)
gcc -o main.exe main.o -L. -ldll

mv dll.dll ..
mv main.exe ..
cd ..

build mingw implicit.sh

#! /bin/bash
echo Building configuration where main and
echo dll implicitly link to each other
rm -rf mingw_implicit
mkdir -p mingw_implicit/obj
cd mingw_implicit/obj

# compile the source code (dll created with position independent code)
gcc -c -fPIC ../../dll.c
gcc -c ../../main.c

# normally when linking a dll, you just use gcc
# to create the dll and its linking library (--out-implib...)
# But, this dll needs to import from main, and main's linking library doesn't exist yet
# so we create the linking library for main.o
# make sure that linking library knows to look for symbols in main.exe (the default would be a.out)
gcc -omain.exe -shared main.o -Wl,--out-implib,main.a  #note this reports failure, but it's only a failure to create main.exe, not a failure to create main.a

# create the dll from its object code the normal way (dll needs to know about main's exports)
gcc -shared -odll.dll dll.o main.a -Wl,--out-implib,libdll.a

# create the executable (exe needs to know about dll's exports)
gcc -o main.exe main.o -L. -ldll

mv dll.dll ..
mv main.exe ..
cd ..

build mingw static.sh

#! /bin/bash
echo Building configuration where main and dll
echo statically link to each other
rm -rf mingw_static
mkdir -p mingw_static/obj
cd mingw_static/obj

# compile the source code
gcc -c -DNO_DLL ../../dll.c
gcc -c -DNO_DLL ../../main.c

# create the static library
ar -rcs dll.a dll.o

# link the executable
gcc -o main.exe main.o dll.a

mv main.exe ../
cd ..

construire msvc explicite les deux.MTD

@echo off
echo Building configuration where both main
echo and dll link explicitly to each other
rd /s /q win_explicit_both
md win_explicit_both\obj
cd win_explicit_both\obj

rem compile the source code
cl /nologo /c /DEXPLICIT_MAIN ..\..\dll.c
cl /nologo /c /DEXPLICIT_DLL ..\..\main.c
cl /nologo /c ..\..\dyn_link.c

rem create the dll from its object code the normal way
link /nologo /dll dll.obj dyn_link.obj

rem create the executable
link /nologo main.obj dyn_link.obj

move dll.dll ..\
move main.exe ..\
cd ..

construire msvc dll explicite.MTD

@echo off
echo Building configuration where main explicitly
echo links to dll, but dll implicitly links to main
rd /s /q win_explicit_dll
md win_explicit_dll\obj
cd win_explicit_dll\obj

rem compile the source code
cl /nologo /c ..\..\dll.c
cl /nologo /c /DEXPLICIT_DLL ..\..\main.c
cl /nologo /c ..\..\dyn_link.c

rem normally when linking a dll, you just use the link command
rem that creates the dll and its linking library.
rem But, this dll needs to import from main, and main's linking library doesn't exist yet
rem so we create the linking library for main.obj
rem make sure that linking library knows to look for symbols in main.exe (the default would be main.dll)
lib /nologo /def /name:main.exe main.obj

rem create the dll from its object code the normal way (dll needs to know about main's exports)
link /nologo /dll dll.obj main.lib

rem create the executable
link /nologo main.obj dyn_link.obj

move dll.dll ..\
move main.exe ..\
cd ..

construire msvc explicite principal.MTD

@echo off
echo Building configuration where dll explicitly
echo links to main, but main implicitly links to dll
rd /s /q win_explicit_main
md win_explicit_main\obj
cd win_explicit_main\obj

rem compile the source code
cl /nologo /c /DEXPLICIT_MAIN ..\..\dll.c
cl /nologo /c ..\..\main.c
cl /nologo /c ..\..\dyn_link.c

rem since the dll will link dynamically and explicitly with main, there is no need
rem to create a linking library for main, and the dll can be built the regular way
link /nologo /dll dll.obj dyn_link.obj

rem create the executable (main still links with dll implicitly)
link /nologo main.obj dll.lib

move dll.dll ..\
move main.exe ..\
cd ..

construire MSVC implicite.MTD

@echo off
echo Building configuration where main and
echo dll implicitly link to each other
rd /s /q win_implicit
md win_implicit\obj
cd win_implicit\obj

rem compile the source code
cl /nologo /c ..\..\dll.c
cl /nologo /c ..\..\main.c

rem normally when linking a dll, you just use the link command
rem that creates the dll and its linking library.
rem But, this dll needs to import from main, and main's linking library doesn't exist yet
rem so we create the linking library for main.obj
rem make sure that linking library knows to look for symbols in main.exe (the default would be main.dll)
lib /nologo /def /name:main.exe main.obj

rem create the dll from its object code the normal way (dll needs to know about main's exports)
link /nologo /dll dll.obj main.lib

rem create the executable (exe needs to know about dll's exports)
link /nologo main.obj dll.lib

move dll.dll ..\
move main.exe ..\
cd ..

construire msvc statique.MTD

@echo off
echo Building configuration where main and dll
echo statically link to each other
rd /s /q win_static
md win_static\obj
cd win_static\obj

rem compile the source code
cl /nologo /DNO_DLL /c ..\..\dll.c
cl /nologo /DNO_DLL /c ..\..\main.c

rem create the static library
lib /nologo dll.obj

rem link the executable
link /nologo main.obj dll.lib

move main.exe ..\
cd ..
11
répondu James Caccese 2011-11-07 23:37:49

Si foo.exe charge toujours bar.dll alors vous pouvez mettre en œuvre la variable dans la barre.dll et l'exporter. Par exemple, certains fichiers b.cpp compilé seulement dans la barre.dll, pas dans foo.exe:

__declspec(dllexport) int x;

puis l'importer dans un fichier source c.cpp compilé en foo.exe:

__declspec(dllimport) int x;

cependant, si parfois foo.exe ne charge pas la barre.dll alors cela ne fonctionnera pas. Aussi, j'écris ça de mémoire et il pourrait y avoir des erreurs syntaxiques, mais j'espère que il suffit de vous indiquer la bonne direction.

Je ne peux pas répondre pourquoi C'est différent Linux.

5
répondu Ciaran Keating 2011-02-06 06:45:02

j'ai trouvé que c'était une question tellement intéressante que j'ai pris le temps d'écrire un tutoriel détaillé sur la façon d'utiliser DLL pour partager des données entre plusieurs DLLs (soit implicitement, soit explicitement lié) mais aussi s'assurer que les données ne sont pas partagées entre des processus séparés du même fichier exécutable.

vous pouvez trouver l'article complet ici: http://3dgep.com/?p=1759


le problème que j'ai trouvé à travailler assez bien est de créer une dll "commune" ou "partagée" qui définit toutes les données et les méthodes que vous voulez partager à travers DLL multiple (mais pas partagée entre les processus).

supposons que vous voulez définir une classe singleton qui peut être consultée à partir du code de l'application principale (L'EXE) mais vous voulez aussi accéder à l'instance singleton dans shared (soit implicitement ou explicitement lié DLL). Tout d'abord, vous devez déclarer la classe singleton dans la DLL "commune":

// Export the class when compiling the DLL, 
// otherwise import the class when using the DLL.
class __declspec(dllexport) MySingleton 
{
public:
    static MySingleton& Instance();
};

lors de la compilation du projet CommonDLL, vous devez exporter la déclaration de classe en décorant la classe avec __declspec(dllexport) et lorsque vous utilisez la DLL (dans l'application par exemple), la définition de classe doit être importée en décorant la classe avec __declspec(dllimport) .

lors de l'exportation d'une classe en la décorant du spécificateur __declspec(dllexport) , toutes les méthodes et données de la classe (même privées) données) sont exportées de la DLL et utilisables par toute DLL ou tout EXE qui est implicitement lié à la DLL commune.

la définition de La MySingleton classe pourrait ressembler à quelque chose comme ceci:

MySingleton& MySingleton::Instance()
{
    static MySingleton instance;
    return instance;
}

lors de la compilation de la dll commune, deux fichiers seront produits:

  1. Le Commun.DLL fichier qui est la bibliothèque partagée qui définit les mehthods et les données exportées utilisés par la DLL.
  2. Le Commun.LIB fichier qui déclare les gousses pour les méthodes et les membres exportés de la DLL.

si vous liez votre application avec le fichier LIB exporté, alors le fichier DLL sera implicitement lié à l'exécution (tant que le fichier DLL se trouve dans les chemins de recherche DLL) et vous aurez accès au singleton défini dans la CommonDLL.DLL fichier.

Aussi, toute bibliothèque partagée (plugins par exemple) il y a aussi un lien avec le CommonDLL.Le fichier LIB aura accès aux mêmes instances simples lorsque chargé dynamiquement par l'application.

pour une explication complète de cette solution, y compris un exemple de code source, consultez l'article suivant que j'ai posté intitulé "Utilisation de bibliothèques de liens dynamiques (DLL) pour créer des Plug-Ins":

http://3dgep.com/?p=1759

4
répondu Jeremiah 2011-10-19 15:26:52

la différence entre GCC et Visual Studio est que sur Linux, il permet implicitement au code de voir des symboles d'autres bibliothèques, dynamiquement liées (partagées), sans que le programmeur ait à faire quoi que ce soit de spécial. Tous les symboles sont disponibles dans la bibliothèque partagée (dynamiquement liée) pour le linker dynamique à résoudre lorsque le programme tourne. Sur Windows, vous devez expressément exporter le symbole de la DLL, et aussi explicitement l'importer dans le programme ou bibliothèque de l'utiliser. (Habituellement cela se fait via une macro (#define) qui se développe pour avoir la déclaration dllexport dans un fichier d'en-tête lors de la construction de la dll elle-même, mais quand le fichier d'en-tête est inclus par un autre programme utilisant la dll, il se développe pour avoir la déclaration dllimport à la place. À mon avis, il s'agit d'une douleur dans le cou, et le comportement de GCC est plus facile, car vous n'avez pas à faire quoi que ce soit de spécial pour obtenir le comportement que vous voulez habituellement.

sur la nouvelle version de GCC, vous pouvez définir la valeur par défaut pour cacher des symboles lors de la construction d'une bibliothèque dynamique (partagée), si vous le souhaitez.

3
répondu Reed Hedges 2012-02-09 21:56:17

merci de fournir diverses solutions sur ce. j'ai examiné ces options et j'ai décidé de mettre en œuvre le module cross singleton en utilisant la mémoire partagée et cela a bien fonctionné pour moi aussi. j'ai utilisé Qt QSharedMemory pour accomplir ma tâche, mais le prototype que j'ai écrit en utilisant le Win32 CreateFileMapping & etc.

1
répondu Zeeshan 2012-06-20 12:18:36

si je comprends bien votre question, vous liez statiquement a.cpp dans foo.exe et d'un bar.dll, donc vous obtenez 2 instances de X.

Si vous avez créé un troisième dll (dire a.dll), et vous lier dynamiquement des foo.exe et d'un bar.dll a.dll, vous obtiendrez le comportement que vous désirez:

  1. foo.charges exe a.dll et crée x.
  2. bar.la dll est chargée et voit que a.dll est chargé et ne le charge pas encore une fois, ils partagent x.
  3. un autre procédé charge a.dll, it gets son propre X.
0
répondu zdan 2011-02-06 07:24:55

j'ai vu beaucoup de réponses à cette question et puisque c'est un peu délicat et peu clair je voudrais apporter le scénario suivant. Nous voulons partager une variable globale entre une DLL et un programme principal, et permettent également l'accès à cette variable à partir de différents modules dans la DLL et dans le programme principal.

la variable est un BOOL indiquant si le programme doit continuer à fonctionner ou s'arrêter. Le nom de la variable est ShouldRun ;

dans le programme principal nous devons mettre:

__declspec(dllexport)  bool ShouldRun;

dans le module principal de la DLL nous devons mettre:

extern "C" BOOL __declspec(dllexport) ShouldRun = TRUE;

dans tout autre module à l'intérieur du projet DLL, nous utiliserons:

extern  "C" BOOL ShouldRun;
0
répondu Michael Haephrati 2017-10-21 13:25:20