2011-02-06 212 views
20

我想共享一個靜態/全局變量,只能在進程和進程調用的dll之間共享。該exe和dll在相同的內存地址空間。我不希望變量在其他進程之間共享。共享進程和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上解決這個問題最簡單的方法是什麼?

回答

8

首先,我發現this article是一個關於動態鏈接庫的非常有趣和簡潔的閱讀(文章僅針對Linux,但這些概念肯定也適用於Windows,並且您可能會對不同的行爲你看到)。特別是靜態和動態加載的根本區別。

我認爲你想要或試圖實現的是一個「跨模塊單例」模式。如果你閱讀this thread的答案,我不知道我可以怎麼回答你的問題比Ben Voigt回覆那個帖子更好。我使用他描述的方法在實際使用幾次之前實現了一個跨模塊單身人士,它的功能就像一個魅力。

當然,您將無法保留在cpp文件中只保留全局變量的清晰度。你將不得不使用一個靜態指針和一些訪問函數和引用計數。但它可以工作。我不太確定如何避免foo.exe和foo.exe共享同一個全局數據實例barbar,我從來沒有這樣做過,也無法真正想到一個辦法它,對不起。

5

如果foo.exe總是加載bar.dll,那麼您可以在bar.dll中實現該變量並將其導出。例如,一些文件b.cpp編譯只進bar.dll,不進foo.exe的:

__declspec(dllimport) int x; 

__declspec(dllexport) int x; 

然後在編譯成foo.exe的源文件c.cpp導入

但是,如果有時foo.exe不加載bar.dll,那麼這將無法工作。另外,我從記憶中寫出這個,所以可能會有一些語法錯誤,但希望這足以讓您指向正確的方向。

我無法回答爲什麼它不同於Linux。

+0

感謝您的回答。不幸的是,foo.exe並不總是調用bar.dll。但是,x總是由foo.exe使用。 – minjang 2011-02-06 06:51:04

+0

也許這只是一個術語,但是......不需要調用DLL中的任何函數,只需要加載它。 – 2011-02-06 07:12:54

+0

你的解決方案只適用於靜態加載(即DLL與exe同時被加載,因此全局變量在執行開始之前被鏈接)。但是OP需要一個能夠與動態加載協同工作的解決方案(使用LoadLibrary()或dlopen()來隨時加載庫),在這種情況下,全局變量不能輕易鏈接,因爲進程正在運行,Linux可以做到這一點,Windows根本無法做到這一點)。 – 2011-02-06 07:17:50

0

如果我正確理解你的問題,你將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。
4

我發現這是這樣一個有趣的問題,我花時間寫一篇關於如何使用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)裝飾類出口類declaratoin,當你正在使用的DLL (例如在應用程序中),需要通過使用__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

10

要獲得主程序和dll共享的linux的行爲,您可以從dll或主程序中導出該變量。其他模塊必須導入該變量。

你這樣做,通過使用DEF文件(see microsoft's documentation),或者通過它使用的任何其他模塊(see microsoft's documentation)與__declspec(dllexport)在那裏的定義,並__declspec(dllimport)標記與變量的用途。這與任何函數,對象或變量如何在Windows中的模塊之間共享相同。

如果您希望程序在運行時加載庫,但主程序可能必須在加載庫之前使用該變量,那麼程序應該導出該變量,並且該DLL應該導入它。這裏有一點雞雞蛋問題,因爲DLL依賴於主程序,而主程序依賴於DLL。請參閱http://www.lurklurk.org/linkers/linkers.html#wincircular

我已經寫了一個例子,說明如何使用Microsoft的編譯器和mingw(gcc在windows中)來執行此操作,包括程序和庫之間可以鏈接的所有不同方式(靜態,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。ħ

​​

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

構建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 .. 
3

GCC和和Visual Studio之間的區別是,在Linux,它隱式允許代碼從其他動態鏈接(共享)庫中查看符號,而程序員不需要做任何特殊的事情。所有符號都可以在共享(動態鏈接)庫中供動態鏈接程序在程序運行時解析。在Windows上,您必須專門從DLL中導出符號,並將其顯式導入到正在使用它的程序或庫中。 (通常這是通過一個宏(#define)來完成的,該宏在構建dll本身時擴展爲在頭文件中具有dllexport聲明,但是當某個其他程序使用dll包含頭文件時,它將擴展爲具有dllimport在我看來,這是一個痛苦的脖子,GCC的行爲更容易,因爲你不需要做任何特別的事情來獲得你想要的行爲,

在新版本的GCC上,你可以如果需要,可以在構建動態(共享)庫時設置默認值以隱藏符號。

1

感謝您對此提供各種解決方案。我已經看過這些選項,並決定使用共享內存來實現交叉模塊單例,它對我也很好。 我已經使用Qt的QSharedMemory實現我的任務,但原型我寫使用Win32的CreateFileMapping &等

0

我看過很多回答這個問題,因爲它是一個有點棘手和不明確的,我想帶來以下場景。 我們希望在DLL和主程序之間共享一個全局變量,並且還允許從DLL中的不同模塊和主程序中訪問此變量。

變量是一個BOOL,指示程序是否應該繼續運行或停止。變量名稱爲ShouldRun;

在主程序中,我們需要把:

__declspec(dllexport) bool ShouldRun; 

在DLL的主要模塊,我們需要把:

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

在任何其他模塊的DLL項目中,我們將使用:

extern "C" BOOL ShouldRun; 
相關問題