加载 .net 包装器无法加载本机依赖项

Loading .net wrapper fails to load native dependency

情况:多个版本的 .net 包装器 class,所有的名称都相同,签名相同。我们想在 wcf 服务方法中动态加载 1 个包装器。我计划用每个包装器 + 依赖的 dll 设置文件夹。我们将根据用户选择的版本使用 switch 或 if 语句 运行.

我有 Assembly.LoadFrom 和路径,可以创建包装器实例 class。但是在包装器中调用方法失败 "cannot load or find CTV.dll".

如果我将机器或用户环境路径更改为指向 CTV.dll,它将执行该方法。但这是全局的,只会指向一个版本文件夹中的特定 CTV.dll。我试过很多次了

Environment.SetEnvironmentVariable("Path", [文件夹中 ctv.dll 的路径], EnvironmentVariableTarget.Process);

但是没有效果,出现同样的错误

有没有我遗漏的东西。好像会有答案的。

还有其他方法来处理这个用例吗?

解决方案 1:

乍一看,在加载托管包装器之前自己加载目标本机 dll 似乎是一个很好的解决方案(通过使用带有绝对路径的 LoadLibrary 调用)

[DllImport("kernel32", SetLastError=true, CharSet = CharSet.Ansi)]
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)]string lpFileName);

来自docs.microsoft.com

If the string specifies a full path, the function searches only that path for the module.

我也想谈谈另一种选择,我尝试过并取得了成功。

解决方案 2:

我们可以使用本机 SetDllDirectory API.

将新路径注入 LoadLibrary 的搜索顺序

本机库是使用 LoadLibrary 函数加载的,并且有特定的 目录搜索顺序 用于要加载的库。

请参阅 LoadLibrary 函数文档中的这一部分:

The search path can be altered using the SetDllDirectory function. This solution is recommended instead of using SetCurrentDirectory or hard-coding the full path to the DLL.

我向一个不存在的模块添加了符号互操作调用:

[DllImport("oguzozgul.dll")]
static extern uint InvokeOguzOzgul(IntPtr oguz, uint ozgul);

然后调用此函数并使用 ProcMon

监视搜索行为

您可以清楚地看到 LoadLibrary 以什么顺序查找本机图像。 (在我的系统上)

然后,在调用这个不存在的模块之前,我调用了 [SetDllDirectory]:

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetDllDirectory(string lpPathName);

// Calling SetDllDirectory:
SetDllDirectory("c:\temp\");

并再次调用该方法。结果如下

检查进程的文件夹后,LoadLibrary 立即检查注入的路径。

因此,如果您调用 SetDllDirectory 并将正确的目录路径添加到 LoadLibrary 的搜索顺序,则当托管包装器需要时,应该会自动从那里加载所需版本的本机 dll。

希望这对您有所帮助。