web-dev-qa-db-ja.com

プロセスとDLLの間でグローバル/静的変数を共有する

プロセスとプロセスによって呼び出されるdllの間でのみ静的/グローバル変数を共有したいと思います。 exeとdllは同じメモリアドレススペースにあります。変数を他のプロセス間で共有したくありません。


問題の詳細:

a.cppに静的/グローバル変数xがあるとします。 exe foo.exeとdll bar.dllの両方にa.cppがあるため、変数xは両方のイメージにあります。

現在、foo.exeは動的に(または静的に)bar.dllをロードします。次に問題は、変数xがexeとdllで共有されているかどうかです。

Windowsでは、これらの2人はneverxを共有します。exeとdllにはxの個別のコピーがあります。ただし、Linuxでは、exeとdllは変数xを共有します。

残念ながら、私はLinuxの動作を求めています。最初にWindowsでpragma data_segを使用することを検討しました。ただし、共有データセグメントを正しく設定しても、foo.exebar.dllxを共有することはありません。 bar.dllfoo.exeのアドレス空間に読み込まれていることを思い出してください。ただし、foo.exeの別のインスタンスを実行すると、xが共有されます。しかし、xを別のプロセスで共有したくない。そのため、data_segの使用に失敗しました。

Exeとdllの間に一意の名前を付けることで、メモリマップファイルを使用できます。


2つの質問:

  1. LinuxとWindowsの動作が異なるのはなぜですか?誰もがこれについてもっと説明できますか?
  2. Windowsでこの問題を解決する最も簡単な方法は何ですか?
22
minjang

まず、 この記事 は非常に興味深く、ダイナミックリンクライブラリを簡潔に読んだことがわかりました(この記事はLinuxにのみ固有ですが、概念は確かにWindowsにも適用され、洞察が得られる場合がありますあなたが見ているさまざまな行動について)。特に静的負荷と動的負荷の基本的な違い。

実装したい、または実装しようとしているのは「クロスモジュールシングルトン」パターンだと思います。 this thread への回答を読んだ場合、Ben Voigtがその投稿に回答したよりもよくあなたの質問に回答できる方法がわかりません。私は以前に(実際には数回)以前にクロスモジュールシングルトンを実装しましたが、彼が説明する方法を使用しており、それは魅力のように機能します。

もちろん、cppファイルにグローバル変数を置くだけのクリーンさを維持することはできません。静的ポインタといくつかのアクセサ関数および参照カウントを使用する必要があります。しかし、それは機能します。私はfoo.exeとfoo.exeがグローバルデータの同じインスタンスを1つのbar.dllで共有することを回避することがどのようにして可能であるかについてはよくわかりません。私はそれを行う必要がなく、実際に行う方法を考えることができません。それ、ごめんなさい。

8
Mikael Persson

メインプログラムとdllの両方が同じxを共有するLinuxの動作を取得するには、その変数をdllまたはメインプログラムからエクスポートできます。他のモジュールはその変数をインポートする必要があります。

これを行うには、DEFファイル( Microsoftのドキュメントを参照 )を使用するか、変数の定義に__declspec(dllexport)を使用して変数を使用し、任意の場所で__declspec(dllimport)を使用します使用されている他のモジュール( Microsoftのドキュメントを参照 )。これは、ウィンドウ内のモジュール間で関数、オブジェクト、または変数を共有する方法と同じです。

プログラムが実行時にライブラリをロードする必要があるが、ライブラリがロードされる前にメインプログラムが変数を使用する必要がある場合、プログラムは変数をエクスポートし、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;
}

link_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

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

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 ..
11
James Caccese

これは非常に興味深い質問であることがわかったので、DLLを使用して複数のDLL間でデータを共有する方法(暗黙的または明示的にリンク)に関する広範なチュートリアルを作成するのに時間をかけましたが、データが個別のプロセス間で共有されないようにしてください同じ実行可能ファイルの。

あなたはここで完全な記事を見つけることができます: http://3dgep.com/?p=1759


この問題を解決するには、複数のDLL間で共有するすべてのデータとメソッドを定義する「共通」または「共有」DLLを作成します(ただし、プロセス間で共有することはできません)。

メインアプリケーションコード(EXE)からアクセスできるシングルトンクラスを定義したいが、共有内のシングルトンインスタンス(暗黙的または明示的にリンクされたDLL)にもアクセスしたいとします。最初に、「共通」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)でクラスを装飾することにより、クラス宣言をエクスポートする必要があります。DLL(たとえばアプリケーションで)を使用している場合、クラス定義は、__declspec(dllimport)でクラスを装飾することによってインポートする必要があります。

__declspec(dllexport)指定子でクラスを装飾してクラスをエクスポートすると、クラスのすべてのメソッドとデータ(プライベートデータを含む)がDLLからエクスポートされ、任意の= DLLまたは共通DLLに暗黙的にリンクするEXE。

MySingletonクラスの定義は次のようになります。

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

共通DLLをコンパイルすると、2つのファイルが生成されます。

  1. Common.DLLファイルは、DLLによって使用されるエクスポートされたメソッドとデータを定義する共有ライブラリです。
  2. DLLからエクスポートされたメソッドとメンバーのスタブを宣言するCommon.LIBファイル。

エクスポートしたLIBファイルに対してアプリケーションをリンクすると、DLLファイルは実行時に暗黙的にリンクされます(DLLファイルがDLL検索パス)これで、CommonDLL.DLLファイルで定義されたシングルトンにアクセスできるようになります。

また、CommonDLL.LIBファイルにもリンクする共有ライブラリ(プラグインなど)は、アプリケーションによって動的にロードされたときに、同じシングルトンインスタンスにアクセスできます。

ソースコードのサンプルを含むこのソリューションの完全な説明については、「ダイナミックリンクライブラリ(DLL)を使用してプラグインを作成する」というタイトルの私が投稿した次の記事を確認してください。

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

5
Jeremiah

Foo.exeが常にbar.dllをロードする場合は、bar.dllに変数を実装してエクスポートできます。たとえば、一部のファイルb.cppは、foo.exeではなくbar.dllにのみコンパイルされます。

__declspec(dllexport) int x;

次に、foo.exeにコンパイルされたソースファイルc.cppにインポートします。

__declspec(dllimport) int x;

ただし、foo.exeがbar.dllを読み込まない場合は、機能しません。また、私はこれをメモリから書いているので、いくつかの構文エラーがあるかもしれませんが、うまくいけば、正しい方向を示すのに十分です。

なぜLinuxが違うのか、お答えできません。

5
Ciaran Keating

GCCとVisual Studioの違いは、Linuxでは、プログラマーが特別なことをしなくても、コードが他の動的にリンクされた(共有)ライブラリからのシンボルを暗黙的に表示できることです。すべてのシンボルは、プログラムの実行時に動的リンカーが解決するための共有(動的にリンクされた)ライブラリで使用できます。 Windowsでは、DLLからシンボルを明確にエクスポートし、それを使用しているプログラムまたはライブラリに明示的にインポートする必要があります。 (通常、これはマクロ(#define)を介して行われ、dll自体を構築するときに、ヘッダーファイルにdllexport宣言が含まれますが、ヘッダーファイルがdllを使用する他のプログラムによってインクルードされると、dllimportを含むように拡張されます宣言ではなく、私の意見では、これは首の痛みであり、GCCの動作は、通常の動作を実現するために特別なことをする必要がないため、より簡単です。

新しいバージョンのGCCでは、必要に応じて、動的(共有)ライブラリの構築時にシンボルを非表示にするようにデフォルトを設定できます。

3
Reed Hedges

これについてさまざまな解決策を提供してくれてありがとう。私はこれらのオプションを検討し、共有メモリを使用してクロスモジュールシングルトンを実装することを決定しましたが、それも私にとってはうまくいきました。 Qt QSharedMemoryを使用してタスクを達成しましたが、プロトタイプはWin32 CreateFileMappingなどを使用して作成しました。

1
Zeeshan

私があなたの質問を正しく理解している場合、a.cppをfoo.exeとbar.dllに静的にリンクしているので、xの2つのインスタンスを取得します。

3番目の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を取得します。
0
zdan

私はこの質問に対する多くの回答を見てきましたが、それは少しトリッキーで不明確なので、次のシナリオを提示したいと思います。 DLLとメインプログラムの間でグローバル変数を共有し、またDLLとメイン内の異なるモジュールからこの変数へのアクセスを許可しますプログラム。

変数は、プログラムの実行を継続するか停止するかを示すBOOLです。変数名はShouldRun;です。

メインプログラムには、次のように配置する必要があります。

__declspec(dllexport)  bool ShouldRun;

DLLのメインモジュールに以下を配置する必要があります。

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

DLLプロジェクト内の他のモジュールでは、以下を使用します。

extern  "C" BOOL ShouldRun;
0