从C库访问外部变量

huangapple go评论106阅读模式
英文:

Accessing an external variable from a C library

问题

在上述示例中,您的目标是使LibB能够访问ProgA中声明为external的全局变量newtestimport。您遇到的问题是编译时出现了“undefined reference”错误,这是因为全局变量newtestimport没有在lib_b.c中定义。

要解决这个问题,您可以采用以下步骤:

  1. newtestimport的定义从prog_a.c移到lib_b.c。这样newtestimport将在lib_b.c中定义,LibB就能够访问它了。

    // file `lib_b.c`
    #include <stdio.h>
    #include "header.h"
    
    test_import_t newtestimport = {
        .some_field = 42
    };
    
    test_import_t *timp;
    
    void initialize_lib_b() {
        timp = &newtestimport;
        int some_field = timp->some_field;
        printf("Result from function: %d\n", some_field);
    }
    
  2. 更新CMakeLists.txt以确保正确链接prog_alib_b

    cmake_minimum_required(VERSION 3.24)
    project(dynamic-library-2 C)
    
    set(CMAKE_C_STANDARD 23)
    
    add_library(lib_b SHARED lib_b.c)
    
    set_target_properties(lib_b PROPERTIES PREFIX "" OUTPUT_NAME "lib_b")
    
    add_executable(prog_a prog_a.c)
    
    target_link_libraries(prog_a lib_b)
    

现在,newtestimport将在lib_b.c中定义,LibB将能够访问它,而编译错误也应该不再存在。

英文:

I am currently learning C and am trying to understand the possibilities of dynamic libraries.
My current question is, if I have a simple "Hello World" application in C called "ProgA", and this program dynamically loads a shared library with some example code called "LibB", can LibB access a global variable in ProgA, which was declared as external?

Given is the following example code for demonstration of the problem:

file header.h

#ifndef TEST_H
#define TEST_H

typedef struct test_import_s {
    int some_field;
} test_import_t;

extern test_import_t newtestimport;

#endif

file prog_a.c

#include &lt;stdio.h&gt;
#include &lt;windows.h&gt;
#include &quot;header.h&quot;

test_import_t newtestimport = {
    .some_field = 42
};

int main()
{
    HINSTANCE hinstLib;
    typedef void (*FunctionPointer)();

    newtestimport.some_field = 42;

    hinstLib = LoadLibrary(&quot;lib_b.dll&quot;);
    if (hinstLib != NULL)
    {
        FunctionPointer initialize_lib_b;
        initialize_lib_b = (FunctionPointer)GetProcAddress(hinstLib, &quot;initialize_lib_b&quot;);

        if (initialize_lib_b != NULL)
        {
            initialize_lib_b();
        }

        FreeLibrary(hinstLib);
    }

    return 0;
}

file lib_b.c

#include &lt;stdio.h&gt;
#include &quot;header.h&quot;

test_import_t *timp;

void initialize_lib_b() {
    timp = &amp;newtestimport;
    int some_field = timp-&gt;some_field;
    printf(&quot;Result from function: %d\n&quot;, some_field);
}

file CMakeLists.txt

cmake_minimum_required(VERSION 3.24)
project(dynamic-library-2 C)

set(CMAKE_C_STANDARD 23)

add_library(lib_b SHARED lib_b.c)

set_target_properties(lib_b PROPERTIES PREFIX &quot;&quot; OUTPUT_NAME &quot;lib_b&quot;)

add_executable(prog_a prog_a.c)

target_link_libraries(prog_a lib_b)

In the above example, the headerfile header.h defines the struct test_import_t and an external variable newtestimport using this struct. In the C file of the main program prog_a.c one property of this struct is assigned the value 42. It then dynamically loads the library lib_b.c using the Windows API and executes a function in it. The function then should access the variable newtestimport of the main program and print out the value of the variable (42).

This example does not work. The compiler throws the following error:

====================[ Build | prog_a | Debug ]==================================
C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-03.8617.54\bin\cmake\win\x64\bin\cmake.exe --build C:\Users\user1\projects\learning-c\cmake-build-debug --target prog_a -j 9
[1/2] Linking C shared library dynamic-library-2\lib_b.dll
FAILED: dynamic-library-2/lib_b.dll dynamic-library-2/liblib_b.dll.a
cmd.exe /C &quot;cd . &amp;&amp; C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-03.8617.54\bin\mingw\bin\gcc.exe -fPIC -g  -Wl,--export-all-symbols -shared -o dynamic-library-2\lib_b.dll -Wl,--out-implib,dynamic-library-2\liblib_b.dll.a -Wl,--major-image-version,0,--minor-image-version,0 dynamic-library-2/CMakeFiles/lib_b.dir/lib_b.c.obj  -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 &amp;&amp; cd .&quot;
C:\Users\user1\AppData\Local\JetBrains\Toolbox\apps\CLion\ch-03.8617.54\bin\mingw\bin/ld.exe: dynamic-library-2/CMakeFiles/lib_b.dir/lib_b.c.obj:lib_b.c:(.rdata$.refptr.newtestimport[.refptr.newtestimport]+0x0): undefined reference to `newtestimport&#39;
collect2.exe: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.

How can the example be fixed to accomplish the described goal?

答案1

得分: 0

Windows DLLs是自包含的,不可以有类似于newtestimport的未定义引用,除非这些引用被另一个DLL满足。

如何修复示例以实现所描述的目标?

最佳修复方法是将newtestimport的地址传递给需要它的函数(在这里是initialize_lib_b())。

如果由于某种原因无法这样做,下一个最佳选择是在另一个DLL中将newtestimport定义为dllexport变量,例如lib_c.dll

然后,主可执行文件和lib_b.dll都将链接到lib_c.lib,并且都将使用来自lib_c.dll的该变量。

附言:全局变量是一种“代码异味”和错误的重要来源。尽量在可能的情况下避免使用它们,在您的示例中似乎没有使用它们的充分理由。

英文:

Windows DLLs are self-contained, and can not have undefined references similar to newtestimport, unless these references are satisfied by another DLL.

> How can the example be fixed to accomplish the described goal?

The best fix is to pass the address of newtestimport into the function that needs it (initialize_lib_b() here).

If for some reason you can't do that, your next best option is to define the newtestimport as a dllexport variable in another DLL, e.g. lib_c.dll.

Then both the main executable and lib_b.dll would be linked against lib_c.lib, and would both use that variable from lib_c.dll.

P.S. Global variables are a "code smell" and a significant source of bugs. You should avoid them whenever possible, and in your example there doesn't seem to be any good reason to use them.

huangapple
  • 本文由 发表于 2023年2月6日 17:27:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/75359450.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定