在发布模式下延迟加载dll

6
在我在Visual Studio (C ++ 2010 Express)中构建的C项目中,我使用MatLab引擎允许用户提供一个自定义函数以在该项目内使用。问题在于此代码还需要能够在没有安装MatLab的计算机上运行,这意味着在这种情况下计算机上不会有所需的DLL。当然,只有在用户未尝试访问调用Matlab引擎的代码段时,这才有效(我已为此提供了标志)。
对于这种情况,我需要3个DLL。 libmx.dll libmex.dll libeng.dll
到目前为止,我已经能够使用LoadLibrary和GetProcAddress在运行时加载libeng.dll。 不过对于其他两个 DLL 就比较难了,除了 C 代码调用 MatLab 引擎外,该代码还经常编译为 mex 文件(MatLab 可执行文件),以允许用户从 MatLab 中调用它。编译为 mex 文件时,mex 编译器会动态链接 libmx.dll 和 libmex.dll。这意味着使用 LoadLibrary 和 GetProcAddress 对于这些 DLL 是行不通的。
现在我只是将 libmx 和 libmex LIB 添加到 Visual Studio 的链接器属性中,这很好用,但对于没有安装 MatLab 的人来说,这是不可能的。
我已经尝试使用 delayLoad,在 Debug 模式下编译可以工作,但在 Release 模式下编译时会出现构建错误。
1>C:\Program Files (x86)\MATLAB\R2012a\bin\win32\libmx.dll : fatal error LNK1107: invalid or corrupt file: cannot read at 0x2B8

如果代码中未访问使用这些DLL的部分,是否有一种完全跳过查找/加载这些DLL的方法?

这是链接器的命令行:

/OUT:"C:\Users\A.Vandenber\documents\visual studio 2010\Projects\Flash\Release\Flash.exe" /NOLOGO "C:\Program Files (x86)\MATLAB\R2012a\bin\win32\libmx.lib" "C:\Program Files (x86)\MATLAB\R2012a\bin\win32\libmex.lib" "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /DELAYLOAD:"libmex.dll" /DELAYLOAD:"libmx.dll" /MANIFEST /ManifestFile:"Release\Flash.exe.intermediate.manifest" /ALLOWISOLATION /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"C:\Users\A.Vandenber\documents\visual studio 2010\Projects\Flash\Release\Flash.pdb" /OPT:REF /OPT:ICF /PGD:"C:\Users\A.Vandenber\documents\visual studio 2010\Projects\Flash\Release\Flash.pgd" /LTCG /TLBID:1 /DYNAMICBASE /NXCOMPAT /MACHINE:X86 /ERRORREPORT:QUEUE 

你设置了哪些链接器选项?请注意,您不需要将*.dlls添加到链接器属性中,而是它们对应的.lib*s。 - CristiFati
是的,我的错。我添加了*.libs而不是.dlls属性->链接器->输入->附加依赖项,然后将.dlls*添加到属性->链接器->输入->延迟加载的DLL中。 - Alexander Vandenberghe
你重新构建了解决方案吗?因为错误在当前上下文中没有意义。或者,重新构建并发布完整的错误信息。你也使用了一些自定义的构建步骤吗? - CristiFati
我很高兴你同意这没有意义 :) 但是,是的,我已经重建了。有一些警告(没什么有趣的)和一个错误消息,就是我回答中的那个。没有自定义构建步骤... 我应该补充说,如果我将库所在的目录添加到我的PATH环境变量中,它可以工作,但目标是能够在没有库文件的情况下编译它。 - Alexander Vandenberghe
哦,等等,所以我猜如果你使用lib文件进行编译并将dll添加到DelayLoad中,你可以在没有它们的情况下运行它?那实际上会让很多意义。 - Alexander Vandenberghe
显示剩余13条评论
2个回答

2
越想越觉得这像是一个[维基百科]: XY问题

1. X(在没有MATLAB库的计算机上运行MEX文件)

根据[MathWorks]: 运行从他人处接收的MEX文件强调为我所加):

在Windows®平台上,安装用于创建MEX文件的C++编译器运行时库。
MEX文件是一种动态链接的子程序,当您调用该函数时,MATLAB解释器会加载并执行它。动态链接意味着当您调用函数时,程序会查找依赖库。MEX文件使用MATLAB运行时库和语言特定的库。MEX文件也可能使用专门的运行时库。这些库的代码不包含在MEX文件中;在运行MEX文件时,这些库必须存在于计算机上。 [MathWorks]: MATLAB Runtime 包含下载许多版本(根据您的路径,您的 版本将是 [MathWorks]: MCR Runtime - MCR_R2012a_win32_installer.exe)的链接,这些版本是免费的(我安装了3个版本来测试这种情况),并且还说明:
将编译过的MATLAB应用程序或组件在不安装MATLAB的情况下运行。
因此,很明显(对我而言)想要使用该文件的人需要安装MCR。
2. Y(使用延迟加载DLL)
VStudio长时间以来一直支持这个特性(MS.Docs: 链接器支持延迟加载的DLL)。
从未使用过MEX文件,也没有完整的问题规范,但是允许一个这样的文件在没有MATLAB.dll的情况下运行,对我来说不是一个好的设计(这意味着它还包含其他东西 - 我认为应该分开放置)。唯一有意义的情况是,MEX文件将是一个.exe文件(不知道是否可能或只是一个愚蠢的事情),并且它将具有一些--help等效项(这将很好(但不是强制性)在没有.dll的环境中运行)。但这也可以用其他方式解决(例如,像README一样的文件)

3. 最终问题

考虑到问题中存在多个(逻辑)错误:
  • 传递给链接器的 .dll 文件
  • 位于 bin 目录中的 .lib 文件
  • 最新路径(extern/lib/win64/microsoft)包含 64 位.lib,而链接器设置为输出 32 位
  • [MS.Docs]:Linker Tools Error LNK1107,这很清楚(作为问题中的错误消息)

我只能得出结论,在发布版本中,“C:\Program Files (x86)\MATLAB\R2012a\bin\win32\libmx.dll”被错误地提供给了链接器(而不是相应的 .lib)。

我稍微尝试了一下 MEX

code.c:

#include <stdio.h>
#include <conio.h>
#include <mex.h>


int main(int argc, char **argv) {
    if (argc > 1) {
        fprintf(stdout, "Argument passed: mexEvalString() returns\n", mexEvalString("n = 1;"));
    } else {
        fprintf(stdout, "Argument NOT passed: pass...\n");
    }
    fprintf(stdout, "Press a key to exit...\n");
    _getch();
    return 0;
}

注释:
  • I used fprintf because in mex.h there is a line:

    #define printf mexPrintf
    
  • Didn't know what function to use from libmx.dll, to force it being added directly (not just a dependency for libmex.dll)

  • I was able to test the Delay Laded DLLs feature in Debug and Release (when passing no arg, the program ran without adding the MEX .dlls to %PATH%).
    It's true that at runtime I got Access Violation, but that's a totally different issue
  • Needless to say that adding any of the .dlls to "Linker -> Input -> Additional Dependencies", triggered the exact same error
最后,我想提到的是,MCR R2012a(以及之后发布的一些版本)是使用VStudio 9.0 (2008)构建的。如果您使用VStudio 10.0 (2010)构建程序,则会在您的进程中加载两个CRT Lib,在某些情况下可能会触发一些错误(特别是因为VStudio 9.0是一个assembly)。这适用于libmx.dlllibmex.dll,但不适用于libeng.dll

我同意我的问题在很多地方都被错误地表述了。我对 dll 和让 matlab 调用 C 的方法非常陌生,在调用 Matlab 时,它总是以各种方式搞乱我的思路。感谢你对一个糟糕问题的详细回答。我现在已经没有任何问题了,因为将发送给用户的版本是使用我们的 C 代码的 mex 文件编译的 matlab 运行时 matlab 编译器。当编译一个 mex 文件时,两个我遇到困难的库会自动包含进来。只有 matlab 引擎不可用,但在运行时加载。 - Alexander Vandenberghe
这不是一个坏问题!无论如何,我觉得有点奇怪的是 libeng.dll 没有包含在内,而其他两个都有,因为所有三个都是由 MCR 安装的。但是嘿,我也是 MATLAB 的新手。 - CristiFati
嗯,运行Matlab引擎应用程序需要安装Matlab的完整版本,而不仅仅是Matlab运行时环境。因此,即使它们被包含在内,也无法启动Matlab引擎。 - Alexander Vandenberghe
谢谢你的提示!现在我看到 MCRlibeng.dll 只有11kb,只公开了17个函数,所以它一定是某种代理。 - CristiFati

0

首先,将您的代码移动到具有访问权限而不是只读的目录中。此外,请查看这里的答案:延迟加载DLL

要将dll文件添加到Visual Studio中,可以按照此在Visual Studio中链接dll

另一个建议是将dll放在c:\windows\system32中。当程序运行时,它会在c:\windows\system32目录中搜索此文件。在此之前,它将搜索从中运行程序的目录。Visual Studio从其项目的目录运行程序(如果未将所述.dll文件放置在windows\system32目录中,则应将其放置在该目录中)。同样,如果手动从其目录运行程序的可执行文件,则所述.dll文件应位于可执行文件所在的相同文件夹中。您需要管理员权限才能这样做。


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