【发布时间】: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()吗?