Sharing a global/static variable between a process and DLL

前端 未结 8 1567
借酒劲吻你
借酒劲吻你 2020-11-27 04:16

I\'d like to share a static/global variable only between a process and a dll that is invoked by the process. The exe and dll are in the same memory address space. I don\'t w

相关标签:
8条回答
  • 2020-11-27 04:40

    I have seen many answers to this question and since it is a bit tricky and unclear I would like to bring the following scenario. We want to share a global variable between a DLL and a main program, and also allow access to this variable from different modules in the DLL and in the main program.

    The variable is a BOOL indicating if the program should continue running or stop. The variable name is ShouldRun;

    In the main program we need to put:

    __declspec(dllexport)  bool ShouldRun;
    

    In the DLL's main module we need to put:

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

    In any other module inside the DLL project we will use:

    extern  "C" BOOL ShouldRun;
    
    0 讨论(0)
  • 2020-11-27 04:42

    I found this to be such an interesting question that I took the time to write an extensive tutorial on how to use DLL's to share data among multiple DLLs (either implicitly, or explicitly linked) but also make sure the data is not shared among separate processes of the same executable file.

    You can find the full article here: http://3dgep.com/?p=1759


    A solution to this problem that I found to work quite well is to create a "common" or "shared" dll that defines all the data and methods that you want to share across multiple DLL's (but not shared among processes).

    Let's suppose you want to define a singleton class that can be accessed from the main application code (the EXE) but you also want to access the singleton instance in shared (either implicitly or explicitly linked DLL). First, you would need to declare the singleton class in the "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();
    };
    

    When compiling the CommonDLL project, you have to export the class declaratoin by decorating the class with __declspec(dllexport) and when you are using the DLL (in the application for example), the class definition needs to be imported by decorating the class with __declspec(dllimport).

    When exporting a class by decorating the class with the __declspec(dllexport) specifier, all of the class's methods and data (even private data) are exported from the DLL and usable by any DLL or EXE that implicitly links to the common DLL.

    The definition of the MySingleton class might look something like this:

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

    When compiling the common dll, two files will be produced:

    1. The Common.DLL file which is the shared library that defines the mehthods and data exported used by the DLL.
    2. The Common.LIB file which declares stubs for the methods and members exported from the DLL.

    If you link you application against the exported LIB file, then the DLL file will be implicitly linked at runtime (as long as the DLL file is found in the DLL search paths) and you will have access to the singleton defined in the CommonDLL.DLL file.

    Also, any shared library (plug-ins for example) that also links against the CommonDLL.LIB file will have access to the same singleton instances when dynamically loaded by the application.

    For a full explanation of this solution including a source code sample, check-out the following article I posted titled "Using Dynamic Link Libraries (DLL) to Create Plug-Ins":

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

    0 讨论(0)
  • 2020-11-27 04:49

    The difference between GCC and and Visual Studio is that on Linux, it implicitly allows code to see symbols from other, dynamically linked (shared) libraries, without you the programmer having to do anything special. All the symbols are available in the shared (dynamically linked) library for the dynamic linker to resolve when the program runs. On Windows, you have to specifically export the symbol from the DLL, and also explicitly import it into the program or library that's using it. (Usually this is done via a macro (#define) that expands to have the dllexport declaration in a header file when building the dll itself, but when the header file is included by some other program using the dll, it expands to have the dllimport declaration instead. In my opinion this is a pain in the neck, and GCC's behavior is easier, since you don't have to do anything special to get the behavior you usually want.

    On newer version of GCC, you can set the default to hide symbols when building a dynamic (shared) library, if you want to.

    0 讨论(0)
  • 2020-11-27 04:50

    To get the behavior of linux where both the main program and a dll share the same x, you can export that variable from either the dll, or the main program. The other module must import that variable.

    You do this by using DEF files (see microsoft's documentation), or by marking the uses with the variable with __declspec(dllexport) where it's defined, and __declspec(dllimport) in any other module it's used (see microsoft's documentation). This is the same as how any function, object, or variable is shared between modules in windows.

    In the case where you'd like a program to load a library at runtime, but the main program may have to use the variable before the library is loaded, the program should export the variable, and the dll should import it. There is a little bit of a chicken and egg problem here because the dll depends on the main program, and the main program depends on the dll. See http://www.lurklurk.org/linkers/linkers.html#wincircular

    I've written an example of how you can do this using both Microsoft's compiler and mingw (gcc in windows), including all the different ways a program and a library can link to each other (statically, dll loaded at program start, dll loaded during runtime)

    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
    

    build mingw explicit 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 ..
    

    build mingw explicit 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 ..
    

    build mingw explicit 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 ..
    

    build msvc explicit 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 ..
    

    build msvc explicit 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 ..
    

    build msvc explicit 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 ..
    

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

    build 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 讨论(0)
  • 2020-11-27 04:55

    First, I found that this article was a very interesting and a concise read on dynamic link libraries (the article is only specific to Linux, but the concepts surely apply to windows as well and you might get some insight as to the different behaviour you are seeing). Especially the fundamental difference between static and dynamic loading.

    I think what you want or are trying to implement is a "cross-module singleton" pattern. If you read the answers to this thread, I don't know how I could possibly answer your question any better than Ben Voigt answered that post. I have implemented a cross-module singleton before (a few times actually) using the method he describes, and it works like a charm.

    Of course, you will not be able to retain the cleaniness of just having the global variable sit there in the cpp file. You will have to use a static pointer and some accessor functions and reference counting. But it can work. I'm not so sure how it would be possible to avoid that foo.exe and foo.exe share the same instance of global data one bar.dll, I never had to do that and can't really think of a way to do it, sorry.

    0 讨论(0)
  • 2020-11-27 04:56

    If foo.exe always loads bar.dll then you can implement the variable in bar.dll and export it. For example, some file b.cpp compiled only into bar.dll, not into foo.exe:

    __declspec(dllexport) int x;
    

    Then import it in a source file c.cpp compiled into foo.exe:

    __declspec(dllimport) int x;
    

    However, if sometimes foo.exe doesn't load bar.dll then this won't work. Also, I'm writing this from memory and so there might be some syntactical errors, but hopefully it's enough to point you in the right direction.

    I can't answer why it's different Linux.

    0 讨论(0)
提交回复
热议问题