【问题标题】:DLLs searching for DLLsDLL 搜索 DLL
【发布时间】:2014-01-28 00:39:31
【问题描述】:

我为 Thunderbird 做了一个扩展。它调用(通过 js-ctypes)我编写的 C++ DLL,该 DLL 又引用其他 DLL,这些 DLL 是用 C#(现有代码)编写的程序集。如果所有文件都与 Thunderbird 可执行文件位于同一目录中,则一切正常。

我现在已将自己的文件移动到我创建的目录中,以使它们与 Thunderbird 文件不同。该目录位于路径中,因此我的 C++ DLL 在调用时会被加载。但是,当它开始查找引用的程序集时,它会失败。

Procmon 显示它只是在运行 Thunderbird 的目录中查找引用的程序集。不仅没有路径,甚至没有在系统目录中查找。

我能做些什么来让我的 DLL 加载它的依赖项而不将所有内容都转储到 Thunderbird 自己的文件夹中,当我将扩展程序移植到其他邮件程序时,这会变得有些混乱?

编辑:添加了 JS 代码的摘录。

从我的“init”函数中,有;

this._kernel32 = ctypes.open("kernel32.dll");

this._setDLLDir = this._kernel32.declare("SetDllDirectoryA",
                               ctypes.default_abi,
                               ctypes.bool,
                               ctypes.char.ptr);

var ret;
ret = this._setDLLDir("C:\\Program Files (x86)\\AuthentStreamAttacher");

this._lib = ctypes.open("AttacherC.dll");
this._getStr = this._lib.declare("GetPackage",
                         ctypes.default_abi,
                         ctypes.char.ptr);

this._freeStr = this._lib.declare("FreePackage", ctypes.default_abi, ctypes.void_t, ctypes.char.ptr);

ret = this._setDLLDir(null);

我实际调用 _getStr 并搜索 AttacherC.dll 的依赖项的位置是;

var ret;
ret = this._setDLLDir("C:\\Program Files (x86)\\AuthentStreamAttacher");
var str = this._getStr();

在每种情况下, ret 都是 true(根据调试器单步执行),这表明对 SetDllDirectory 的调用成功。无论我使用“A”还是“W”版本,行为都是相同的,JS 中没有任何内容可以简单地让我调用“SetDllDirectory”。就好像每个调用都发生在自己的隔离上下文中,但在我的 DLL 中,“GetPackage”使用 malloc 分配一些内存,然后需要在“FreePackage”中释放这些内存。 FreePackage 不会抛出异常,表明已分配的内存在两个调用之间持续存在。

更多奇怪的行为;如果我在 SetDllDirectory 中指定一个随机字符串作为路径(在本例中为“helloworld”),则 ret 仍然是 still true。因此,要么 SetDllDirectory 实际上没有通过 ctypes 正确获取字符串,要么没有对其进行任何完整性检查。

我现在的感觉是,每个 js-ctypes 调用都是在其自己的上下文中发生的,在某种程度上,它扰乱了 .net 的程序集搜索机制,而让它工作的唯一方法是拥有一个单独的本地 DLL从 javascript 调用的单个函数。然后在同一上下文中调用 SetDllDirectory 和 LoadLibrary 以调用链中的下一个包装器,然后调用我真正的 C# 代码。凌乱,似乎更容易出错,所以我希望有人过来证明我错了?

【问题讨论】:

  • 结核病扩展指南对此有什么要说的吗?通常你会打电话给SetDllDirectory。或者使用加载时链接。后者虽然不是很有趣。
  • 不是我见过的。我正在使用 js-ctypes 从 JavaScript 调用我的 DLL,因为我在 XPCOM 上失败了。 js-ctypes 文档涵盖了对初始 DLL 的搜索,但没有进一步搜索。
  • 好的,那我想你有路线了。您已经有了一个加载 DLL 的 JS 层。大概是打电话给LoadLibrary。因此,在 C++ DLL 上调用 LoadLibrary 之前,添加对 SetDllDirectory 的调用。一旦返回,添加对SetDllDirectory 的第二次调用以撤消更改。
  • 问题是我没有LoadLibrary。 C++ 是一个 .net 程序集,它在 js 中导出我想要的函数,但通过将它们作为引用添加到项目中来获取其他程序集 - 实际的搜索和加载发生在幕后某处。我模糊地意识到我可能可以使用反射来摆脱引用,但我以前从未去过那里,我希望有一种更简单、更简单的方法。就像以某种方式注册我的程序集,但此时 regasm 只是以“没有类型被注册”退出
  • 我认为你有一个混合模式的 C++/CLI 程序集。你的函数是用 __declspec(dllexport) 导出的,对吗?还是 .def 文件?我不知道 js-ctypes,但那里一定有一些东西可以调用你的 DLL 的加载。那是什么?是ctypes.open()吗?

标签: c# c++ .net dll jsctypes


【解决方案1】:

由于其他人似乎没有答案,我将记录我最终做了什么。

当本机代码调用 dotnet DLL 时,CLR 会在后台启动以运行它。尽管本机代码可以在各种位置搜索 DLL(包括由 SetDllDirectory 指定的位置),但 CLR 只会在启动初始可执行文件的目录和全局程序集缓存中查找。要通过在 Visual Studio 中添加对 DLL 的引用来访问已链接到的程序集,它们必须位于这两个位置之一。

因为我也不想这样做,所以需要制作一个仅直接依赖于框架程序集的 .net DLL,而不引用我自己的任何内容。然后让 CLR 启动并运行我的代码。然后我可以通过Assembly::LoadFrom() 加载我想要使用的程序集,并调用我想要使用的方法,如记录的here

当然,以这种方式加载程序集仍然会导致在可执行目录或 GAC 中搜索任何其他依赖程序集,如果它们尚未加载,并且除了最简单的情况外,它太复杂了从最基本的向上顺序显式加载每个程序集。所以首先注册 AssemblyResolve 事件。当 CLR 在其两个搜索位置找不到程序集时,它会引发此事件并让我确定程序集的完整路径并加载它,再次使用 Assembly::LoadFrom()

当然,LoadFrom 需要知道基本路径——似乎唯一可用的信息与可执行文件的目录有关,但有很多方法可以解决这个问题。

【讨论】:

  • 这很有帮助。我设法将所有这些东西打包在一个 c++/clr dll 中。
【解决方案2】:

您需要修改DLL search path

在调用ctypes.open() 之前调用SetDllDirectory 以加载您的C++ DLL。将包含您的 DLL 及其依赖模块的目录传递给 SetDllDirectory。当ctypes.open()调用返回时,再次调用SetDllDirectory,传递NULL,撤消搜索路径修改。

【讨论】:

  • 澄清一下,以防万一有人认为这是答案,只是在等待我回到它并接受,它对我不起作用,如主评论线程中所述:)
  • 是否有指南应该如何编写路径?即,我应该使用"C:\My Folder\My Sub Folder" 吗?我应该逃避` or use \` 吗?也许/?是否有正确定义SetDllDirectory() 路径的指南?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-20
  • 1970-01-01
  • 1970-01-01
  • 2011-04-19
  • 2015-07-01
  • 2011-04-09
相关资源
最近更新 更多