繁体   English   中英

DLL搜索DLL

[英]DLLs searching for DLLs

我已经完成了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 仍然为 true。 因此,要么SetDllDirectory实际上没有通过ctypes正确获取字符串,要么就没有对其进行任何完整性检查。

我现在的感觉是,每个js-ctypes调用都以某种方式在其自己的上下文中发生,这使.net的程序集搜索机制不满意,而使此功能起作用的唯一方法是使用单独的DLL并使用单个函数从javascript调用。 然后,在同一上下文中调用SetDllDirectory和LoadLibrary来调用链中的下一个包装器,然后包装器调用我的真实C#代码。 乱七八糟,似乎更容易出问题,所以我希望有人能证明我做错了?

由于没有其他人似乎有答案,因此我将记录最终的工作。

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

由于我既不想这样做,因此需要制作一个仅直接依赖于框架程序集的.n​​et DLL,而无需引用我自己的任何内容。 然后,这将启动CLR并运行我的代码。 然后,我可以通过Assembly::LoadFrom()加载要使用的程序集,并调用此处要使用的方法。

当然,以这种方式加载程序集仍然会导致在可执行文件dir或GAC中搜索任何其他依赖程序集(如果尚未加载的话),并且在最琐碎的情况下,除了非常琐碎的情况之外,它都太复杂而无法显式地打扰从最基本的向上顺序加载每个程序集。 因此,首先注册AssemblyResolve事件。 当CLR在其两个搜索位置找不到程序集时,它将引发此事件,并让我确定该程序集的完整路径并再次使用Assembly::LoadFrom()加载它。

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

您将需要修改DLL搜索路径

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM