在Docker上DllImport无法正常工作 - DllNotFoundException

7
我有一个使用.NET Core和C#开发,在Docker上运行的项目,需要调用几个用C++开发的DLL函数。问题是:当我在Windows上使用Visual Code运行我的项目时,代码可以平稳运行,但是当我在Linux容器上的Docker上运行时,代码尝试执行DLL函数时会抛出错误。
我已经尝试将.dll文件复制到/lib文件夹中,改为项目的父文件夹,但都没有成功。我开始怀疑问题是找不到文件,并通过一些研究发现可能与文件权限有关,因此我对.dll文件运行chmod a+wrx命令,但仍然不成功。
这是我的Dockerfile配置:
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2 AS base
WORKDIR /app
EXPOSE 80
RUN apt-get update \
    && apt-get install -y --allow-unauthenticated \
        libc6-dev \
        libgdiplus \
        libx11-dev \
     && rm -rf /var/lib/apt/lists/*
RUN apt-get update \
    && apt-get install -y poppler-utils

FROM mcr.microsoft.com/dotnet/core/sdk:2.2 AS build-env
WORKDIR /app
COPY . .
RUN dotnet restore --configfile Nuget.config -nowarn:msb3202,nu1503
RUN dotnet publish -c Release -o ./out 
    
FROM base AS final
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "MdeGateway.dll"]

这是试图访问DLL函数的代码:
[DllImport("MyDll.dll")]
private static extern int dllfunction(Int32 argc, IntPtr[] argv);

public static void CallDll(string[] args)
{
    IntPtr[] argv = ArrayToArgs(args);
    dllfunction(args.Length, argv);

    FreeMemory(args, argv);
}

当执行“dllfunction(args.Length, argv);”这一行时,会出现错误。

具体的错误消息如下:

"无法加载共享库'MyDll.dll'或其某个依赖项。为了帮助诊断加载问题,请考虑设置LD_DEBUG环境变量:libMyDll.dll: 无法打开共享对象文件: 没有那个文件或目录"

另外,如果有人能教我如何设置LD_DEBUG环境变量,我会非常感激。


2
你可以通过 docker run 步骤中的 -e "LD_DEBUG=true"(不知道 true 是否是正确的值)或者在 dockerfile 中使用 ENV LD_DEBUG=true 来设置 Docker 的环境变量。https://vsupalov.com/docker-arg-env-variable-guide/#setting-env-values - Der Kommissar
@DerKommissar:当我在Dockerfile中添加ENV LD_DEBUG=true时,出现了warning: debug option true' unknown;的警告,因此true不是该变量的有效值。在下面添加了一个答案,解决了设置LD_DEBUG的有效值的问题。 - Veverke
2个回答

7
我有一个使用.NET Core和C#开发的项目,运行在Docker上,需要调用一个用C++开发的DLL中的一些函数。问题是:当我在Windows上使用Visual Code运行我的项目时,代码可以正常运行,但是当我在Linux容器上运行Docker时,代码在尝试执行DLL函数时会抛出错误。
如果我理解正确,您有一个C++应用程序,将其编译为.dll文件(在Windows上)。您可以在Windows上通过DllImport引用此.dll文件,但无法在Linux(容器)上引用。是这样吗?
您是否知道将C++代码编译为.dll文件(共享库)是Windows特定的事情?非托管代码是架构和平台特定的。在x64上编译的非托管.dll文件无法在arm64上运行。在Windows上编译的非托管.dll文件无法在Linux上运行。
Linux(以及在docker中的Linux容器)无法使用从Windows上构建的非托管代码的.dll文件。Linux需要将非托管(C ++)代码编译成共享库(.so文件),以便DllImport(和底层的dlopen调用)可以在Linux上工作。最好在与容器运行环境相同的平台上进行编译。
mono文档涵盖了一种特定的DllImport实现,并提供了更多关于Linux如何工作的背景知识。

https://www.mono-project.com/docs/advanced/pinvoke/

(但请记住 Mono 不等于 .NET Core。它仍然可以为您提供更多的背景信息。)

是的,情况正是如此。我知道.dll是专门针对Windows的,但我认为无论平台如何,DllImport仍然能够执行。但既然事实并非如此,我必须找到一种将C++库编译成.so文件的方法。非常感谢您的帮助! - Leonardo Dias
2
只需执行添加操作,如果您引用“.so”共享库文件夹,DllImport 将在 Linux 上正常工作。 - Eduard G
谢谢你的回答,它对我很有帮助。我需要使用一个第三方库,它是作为一个C#类封装了一个C++ .dll库提供的。当我在Ubuntu上将其容器化到Docker中时,我遇到了相同的问题。我能够找到一个具有相同接口的等效.so库,并且只需重写DllImport语句中的路径,现在它可以工作了。 - Vojta Baránek

4
这并没有解决 OP 的问题,但有助于回答他的第二个问题。
此外,如果有人可以教我如何设置 LD_DEBUG 环境变量,我会很感激。我遇到了类似的问题,也很难理解该如何处理这个 LD_DEBUG 环境变量。事实证明它控制着 Unix 动态链接器的调试信息的详细程度。
按照这里的建议,在 Linux 终端中运行LD_DEBUG=help cat 将会给出所有可用于设置 LD_DEBUG 的有效选项。
这是这样一个命令的输出截图: enter image description here 其他有用的资源:
- Linux 应用程序调试技术 - 动态链接器 - Linux LS.SO man 页面 引用上面提到的 LD.SO man 页面:

LD_DEBUG (自 glibc 2.1 起) 输出有关动态链接器操作的详细调试信息。此变量的内容是一个或多个类别,由冒号、逗号或(如果值被引用)空格分隔:

          help   Specifying help in the value of this variable does
                 not run the specified program, and displays a help
                 message about which categories can be specified in
                 this environment variable.

          all    Print all debugging information (except statistics
                 and unused; see below).

          bindings
                 Display information about which definition each
                 symbol is bound to.

          files  Display progress for input file.

          libs   Display library search paths.

          reloc  Display relocation processing.

          scopes Display scope information.

          statistics
                 Display relocation statistics.

          symbols
                 Display search paths for each symbol look-up.

          unused Determine unused DSOs.

          versions
                 Display version dependencies.

          Since glibc 2.3.4, LD_DEBUG is ignored in secure-execution
          mode, unless the file /etc/suid-debug exists (the content
          of the file is irrelevant).

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接