2011-02-06 2 views
20

Я хотел бы поделиться статическими/глобальными переменными только между процессом и DLL, вызываемой процессом. Exe и dll находятся в одном и том же адресном пространстве памяти. Я не хочу, чтобы переменная была разделена между другими процессами.Обмен глобальной/статической переменной между процессом и DLL


Разработка проблемы:

Говорят, что существует статический/глобальная переменная x в a.cpp. И exe foo.exe, и dll bar.dll имеют a.cpp, поэтому переменная x находится на обоих изображениях.

Теперь foo.exe динамически загружает (или статически) bar.dll. Тогда проблема заключается в том, является ли переменная x разделяемой exe и dll, или нет.

В Windows, эти два парня никогда не поделиться x: ех и дллы будут иметь отдельную копию x. Тем не менее, в Linux, exe и dll делят переменную x.

К сожалению, я хочу поведение Linux. Сначала я рассмотрел использование pragma data_seg в Windows. Однако, даже если я правильно настроил общий сегмент данных, foo.exe и bar.dll никогда не делится x. Напомним, что bar.dll загружается в адресное пространство foo.exe. Однако, если я запустил другой экземпляр foo.exe, тогда общий номер x. Но я не хочу, чтобы x делился различными процессами. Таким образом, с использованием data_seg не удалось.

Возможно, он использует файл с отображением памяти, создав уникальное имя между exe и dll, которое я сейчас пытаюсь.


Два вопроса:

  1. Почему поведение Linux и Windows, отличается? Может ли кто-нибудь объяснить об этом больше?
  2. Что было бы самым простым способом решить эту проблему в Windows?

ответ

8

Во-первых, я обнаружил, что this article был очень интересным и сжатым в динамических библиотеках ссылок (статья предназначена только для Linux, но концепции, безусловно, применимы и к окнам, и вы можете получить представление о разных поведение, которое вы видите). Особенно принципиальное различие между статической и динамической нагрузкой.

Я думаю, что то, что вы хотите или пытаетесь реализовать, - это «одномодовый шаблон с поперечным модулем». Если вы прочтете ответы на this thread, я не знаю, как бы я мог ответить на ваш вопрос лучше, чем Бен Фойгт ответил на этот пост. Я реализовал кросс-модульный синглтон раньше (несколько раз на самом деле) с использованием метода, который он описывает, и он работает как шарм.

Конечно, вы не сможете сохранить чистоту просто наличия глобальной переменной в файле cpp. Вам придется использовать статический указатель и некоторые функции доступа и подсчет ссылок. Но это может сработать. Я не уверен, как можно избежать того, что foo.exe и foo.exe используют один и тот же экземпляр глобальных данных one bar.dll, я никогда не должен был этого делать и не могу действительно думать о том, как это сделать это, извините.

+0

Благодарим вас за хорошую информацию. Я смог написать подобный одноэлементный кросс-модуль, используя объект с отображением памяти (= shared memory). Благодаря! – minjang

5

Если foo.exe всегда загружает bar.dll, то вы можете реализовать эту переменную в bar.dll и экспортировать ее. Например, какой-то файл b.cpp составлен только в bar.dll, а не в foo.exe:

__declspec(dllexport) int x; 

затем импортировать его в исходном файле c.cpp скомпилированного в foo.exe:

__declspec(dllimport) int x; 

Однако, если иногда foo.exe не загружает bar.dll, это не сработает. Кроме того, я пишу это из памяти, и поэтому могут быть некоторые синтаксические ошибки, но, надеюсь, этого достаточно, чтобы указать вам в правильном направлении.

Я не могу ответить, почему это другой Linux.

+0

Спасибо за ваш ответ. К сожалению, foo.exe не всегда вызывает bar.dll. Но 'x всегда используется foo.exe. – minjang

+0

Возможно, это всего лишь терминология, но ... нет необходимости вызывать какие-либо функции в DLL, просто чтобы загрузить ее. –

+0

ваше решение применяется только к статической загрузке (т. Е. DLL загружается одновременно с exe и, таким образом, перед запуском запускаются глобальные переменные). Но OP нуждается в решении, которое работает с динамической загрузкой (используя LoadLibrary() или dlopen() для загрузки библиотеки в любое время), в этом случае глобальные переменные просто не могут быть связаны так же легко, потому что процесс выполняется (в основном , Linux может сделать это в некоторой степени, Windows не может этого сделать вообще). –

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), но вы также хотите получить доступ к экземпляру singleton в shared (либо неявно, либо явно связанную 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, вы должны экспортировать класс declaratoin украшая класс с __declspec(dllexport) и при использовании DLL (например, в приложении), определение класса необходимо импортировать, украсив класс __declspec(dllimport).

При экспорте класса путем декорирования класса спецификатором __declspec(dllexport) все методы и данные класса (даже частные данные) экспортируются из библиотеки DLL и могут использоваться любой DLL или EXE, которые неявно ссылаются на общую DLL.

Определение класса MySingleton может выглядеть примерно так:

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

При составлении общей библиотеки DLL, два файла будет производиться:

  1. The файл Common.DLL который разделяемой библиотеки, которая определяет методы и данные, экспортируемые в DLL.
  2. Файл Common.LIB, который объявляет заглушки для методов и элементов, экспортированных из DLL.

Если вы связываете вам приложение к экспортируемой LIB файла, то файл DLL будет неявно связан во время выполнения (до тех пор, пока файл DLL находится в поисковых DLL путях), и вы будете иметь доступ к синглтону определенных в файле CommonDLL.DLL.

Кроме того, любая общая библиотека (например, плагины), которая также связывается с файлом CommonDLL.LIB, будет иметь доступ к тем же экземплярам singleton, которые динамически загружаются приложением.

Для полного объяснения этого решения, включая образец исходного кода, выезд в следующей статье я опубликовал под названием "Использование Dynamic Link Libraries (DLL) для создания Plug-Ins":

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

10

Чтобы получить поведение Linux, где и основная программа, и DLL используют один и тот же x, вы можете экспортировать эту переменную из DLL или основной программы. Другой модуль должен импортировать эту переменную.

Вы можете сделать это с помощью DEF файлов (see microsoft's documentation), или путем маркировки использования с переменной с __declspec(dllexport), где она определена, и __declspec(dllimport) в любом другом модуле он используется (see microsoft's documentation). Это то же самое, что и любая функция, объект или переменная, разделяемая между модулями в окнах.

В случае, если вы хотите, чтобы программа загружала библиотеку во время выполнения, но основной программе, возможно, придется использовать переменную до загрузки библиотеки, программа должна экспортировать эту переменную, а 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 
} 

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

сборки 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 .. 
+0

Если кто-нибудь может сказать мне лучший способ сделать свалку кода для исчерпывающего примера, я бы с удовольствием это узнал. –

3

Разница между GCC и и Visual Studio является то, что на Linux, он неявно позволяет коду видеть символы из других динамически связанных (общих) библиотек, без необходимости программиста делать что-то особенное. Все символы доступны в общей (динамически связанной) библиотеке для динамического компоновщика для разрешения при запуске программы. В Windows вы должны специально экспортировать символ из DLL, а также явно импортировать его в программу или библиотеку, которая ее использует. (Обычно это делается с помощью макроса (#define), который расширяется, чтобы иметь объявление dllexport в заголовочном файле при построении самой dll, но когда заголовочный файл включен какой-либо другой программой с использованием dll, он расширяется, чтобы иметь dllimport Вместо этого, по-моему, это боль в шее, и поведение GCC проще, поскольку вам не нужно делать ничего особенного, чтобы получить такое поведение, которое вы обычно хотите.

В более новой версии GCC вы можете установите значение по умолчанию, чтобы скрыть символы при построении динамической (общей) библиотеки, если хотите.

1

Благодарим за предоставление различных решений. я рассмотрел этот вариант и решил реализовать сингл-модуль cross module с использованием общей памяти, и он отлично сработал для меня. я использовал 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; 
Смежные вопросы