繁体   English   中英

在进程和DLL之间共享全局/静态变量

[英]Sharing a global/static variable between a process and DLL

我想在进程和进程调用的dll之间共享静态/全局变量。 exe和dll位于相同的内存地址空间中。 我不希望变量在其他进程之间共享。


详述问题:

假设a.cpp中存在静态/全局变量x exe foo.exe和dll bar.dll都有一个a.cpp ,因此变量x在两个图像中。

现在, foo.exe动态加载(或静态) bar.dll 然后,问题是变量x是否由exe和dll共享。

在Windows中,这两个人从不共享x :exe和dll将有一个单独的x副本。 但是,在Linux中,exe和dll共享变量x

不幸的是,我想要Linux的行为。 我首先考虑在Windows上使用pragma data_seg 但是,即使我正确设置了共享数据段, foo.exebar.dll也不会共享x 回想一下, bar.dll被加载到的地址空间foo.exe 但是,如果我运行另一个foo.exe实例,则共享x 但是,我不希望x被不同的进程共享。 因此,使用data_seg失败了。

我可以通过在exe和dll之间创建一个唯一的名称来使用内存映射文件,我现在正在尝试。


两个问题:

  1. 为什么Linux和Windows的行为不同? 任何人都可以解释更多吗?
  2. 在Windows上解决此问题最简单的方法是什么?

要获得主程序和dll共享相同x的linux的行为,可以从dll或主程序中导出该变量。 另一个模块必须导入该变量。

您可以通过使用DEF文件( 请参阅microsoft的文档 ),或使用带有__declspec(dllexport)的变量来定义它的用途,并在其使用的任何其他模块中使用__declspec(dllimport)请参阅microsoft的文档 )。 这与在Windows中的模块之间共享任何函数,对象或变量的方式相同。

如果您希望程序在运行时加载库,但主程序可能必须在加载库之前使用该变量,程序应该导出变量,并且dll应该导入它。 这里有一点鸡和蛋的问题,因为dll取决于主程序,主程序取决于dll。 http://www.lurklurk.org/linkers/linkers.html#wincircular

我已经编写了一个示例,说明如何使用Microsoft的编译器和mingw(windows中的gcc)来完成此操作,包括程序和库可以相互链接的所有不同方式(静态,在程序启动时加载dll,加载dll在运行期间)

main.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

main.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
}

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

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_importing.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

构建mingw显式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 ..

构建mingw显式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 ..

构建mingw显式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 ..

建立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 ..

构建msvc显式both.bat

@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 ..

构建msvc显式dll.bat

@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 ..

构建msvc显式main.bat

@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 ..

构建msvc implicit.bat

@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 ..

构建msvc static.bat

@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 ..

首先,我发现这篇文章是关于动态链接库的非常有趣和简洁的阅读(这篇文章仅针对Linux,但这些概念肯定也适用于Windows,你可能会对你的不同行为有所了解看到)。 特别是静态和动态加载之间的根本区别。

我认为你想要或正在尝试实现的是“跨模块单例”模式。 如果您阅读了这个帖子的答案,我不知道我怎么能比Ben Voigt回答那个帖子更好地回答你的问题。 我之前(实际上几次)使用他描述的方法实现了一个跨模块单例,它就像一个魅力。

当然,你将无法保持只有全局变量坐在cpp文件中的清洁。 您将不得不使用静态指针和一些访问器函数和引用计数。 但它可以工作。 我不太确定怎么可能避免foo.exe和foo.exe共享同一个全局数据实例一个bar.dll,我从来没有这样做过,也无法真正想办法对不起

如果foo.exe总是加载bar.dll,那么你可以在bar.dll中实现该变量并将其导出。 例如,某些文件b.cpp只编译成bar.dll,而不是编译成foo.exe:

__declspec(dllexport) int x;

然后将其导入到编译到foo.exe的源文件c.cpp中:

__declspec(dllimport) int x;

但是,如果有时foo.exe没有加载bar.dll,那么这将无法正常工作。 另外,我是从内存中写这个,所以可能会有一些语法错误,但希望它足以指出你正确的方向。

我无法回答为什么它与Linux不同。

我发现这是一个非常有趣的问题,我花时间编写了一个关于如何使用DLL在多个DLL之间共享数据(隐式或显式链接)的广泛教程,同时也确保数据不在不同进程之间共享相同的可执行文件。

您可以在此处找到完整的文章:http: //3dgep.com/?p = 1759


我发现这个问题很有效的解决方案是创建一个“公共”或“共享”的dll,它定义了你想要在多个DLL之间共享的所有数据和方法(但不能在进程间共享)。

假设您要定义一个可以从主应用程序代码(EXE)访问的单例类,但您还希望访问共享中的单例实例(隐式或显式链接的DLL)。 首先,您需要在“common”DLL中声明单例类:

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

编译CommonDLL项目时,必须通过使用__declspec(dllexport)修饰类来导出类declaratoin,当您使用DLL时(例如在应用程序中),需要通过使用__declspec(dllimport)修饰类来导入类定义__declspec(dllimport)

通过使用__declspec(dllexport)说明符修改类来导出类时,所有类的方法和数据(甚至是私有数据)都从DLL导出,并可由隐式链接到公共DLL的任何DLL或EXE使用。

MySingleton类的定义可能如下所示:

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

编译公共dll时,将生成两个文件:

  1. Common.DLL文件,它是共享库,用于定义DLL使用的方法和数据。
  2. Common.LIB文件,用于声明从DLL导出的方法和成员的存根。

如果您将应用程序与导出的LIB文件链接,则DLL文件将在运行时隐式链接(只要在DLL搜索路径中找到DLL文件),您就可以访问CommonDLL.DLL中定义的单例。文件。

此外,任何链接到CommonDLL.LIB文件的共享库(例如插件)在应用程序动态加载时都可以访问相同的单例实例。

有关此解决方案(包括源代码示例)的完整说明,请查看我发布的以下文章“使用动态链接库(DLL)创建插件”:

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

GCC和Visual Studio之间的区别在于,在Linux上,它隐式允许代码查看来自其他动态链接(共享)库的符号,而无需程序员必须执行任何特殊操作。 所有符号都可在共享(动态链接)库中使用,以供动态链接程序在程序运行时解析。 在Windows上,您必须专门从DLL导出符号,并将其显式导入到正在使用它的程序或库中。 (通常这是通过一个宏(#define)来完成的,它在构建dll本身时扩展为在头文件中包含dllexport声明,但是当使用dll的其他程序包含头文件时,它会扩展为具有dllimport相反。在我看来,这是一个痛苦的问题,GCC的行为更容易,因为你不需要做任何特别的事情来获得你通常想要的行为。

在较新版本的GCC上,如果需要,可以在构建动态(共享)库时将默认值设置为隐藏符号。

感谢您提供各种解决方案。 我查看了这些选项并决定使用共享内存实现交叉模块单例,它也适用于我。 我使用Qt QSharedMemory来完成我的任务,但我使用Win32 CreateFileMapping等编写的原型。

我已经看到了这个问题的很多答案,因为它有点棘手而且不清楚我想带来以下场景。 我们希望在DLL和主程序之间共享一个全局变量,并允许从DLL和主程序中的不同模块访问此变量。

变量是BOOL,指示程序是否应继续运行或停止。 变量名是ShouldRun ;

在主程序中我们需要:

__declspec(dllexport)  bool ShouldRun;

在DLL的主模块中我们需要放:

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

在DLL项目中的任何其他模块中,我们将使用:

extern  "C" BOOL ShouldRun;

如果我正确理解你的问题,你将a.cpp静态链接到foo.exe和bar.dll,所以你得到2个x实例。

如果您创建了第三个dll(例如a.dll),并且您将foo.exe和bar.dll动态链接到a.dll,您将获得所需的行为:

  1. foo.exe加载a.dll并创建x。
  2. bar.dll被加载并看到a.dll已加载并且不再加载它们,它们共享x。
  3. 另一个进程加载a.dll,它获得自己的x。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM