Win32应用程序插件如何将其DLL加载到自己的目录中 < >我的代码是一个特定应用程序的插件,用Visual Studio 8用C++编写。它使用来自外部提供程序的两个DLL。不幸的是,我的插件无法启动,因为没有找到DLL(我将它们放在与插件本身相同的目录中)

Win32应用程序插件如何将其DLL加载到自己的目录中 < >我的代码是一个特定应用程序的插件,用Visual Studio 8用C++编写。它使用来自外部提供程序的两个DLL。不幸的是,我的插件无法启动,因为没有找到DLL(我将它们放在与插件本身相同的目录中),dll,winapi,plugins,search-path,Dll,Winapi,Plugins,Search Path,当我手动将DLL移动或复制到主机应用程序目录时,插件可以正常加载。这种移动对于最终用户来说是不可接受的麻烦,我正在寻找一种方法让我的插件透明地加载DLL。我能做什么 有关详情: 主机应用程序插件位于主机应用程序指定的目录中。该目录不在DLL搜索路径中,我无法控制它 插件本身打包为插件目录的子目录,包含插件代码本身,以及与插件相关的任何资源(例如图像、配置文件…)。我控制子目录(称为“bundle”)中的内容,但不控制它的位置 该应用程序的常见插件安装习惯用法是最终用户将插件包复制到插件目录

当我手动将DLL移动或复制到主机应用程序目录时,插件可以正常加载。这种移动对于最终用户来说是不可接受的麻烦,我正在寻找一种方法让我的插件透明地加载DLL。我能做什么

有关详情:

  • 主机应用程序插件位于主机应用程序指定的目录中。该目录不在DLL搜索路径中,我无法控制它
  • 插件本身打包为插件目录的子目录,包含插件代码本身,以及与插件相关的任何资源(例如图像、配置文件…)。我控制子目录(称为“bundle”)中的内容,但不控制它的位置
  • 该应用程序的常见插件安装习惯用法是最终用户将插件包复制到插件目录
此插件是Macintosh版本插件的一个端口。在Mac上没有问题,因为每个二进制文件都包含自己的动态库搜索路径,我根据需要为我的插件二进制文件设置了该路径。要在Mac上设置它,只需要在Xcode IDE中设置一个项目。这就是为什么我希望在VisualStudio中有类似的东西,但我找不到任何相关的东西。此外,VisualStudio的帮助根本不是,谷歌也不是

一个可能的解决方法是,我的代码明确地告诉Windows在哪里可以找到DLL,但我不知道如何找到,而且在任何情况下,因为我的代码甚至还没有启动,所以它没有机会这样做

作为一名Mac开发人员,我意识到我可能要求一些非常基本的东西。如果是这种情况,我很抱歉,但是我已经没有头发可以拔了。

使用()查找dll所在的路径。
然后使用()将该路径添加到dll搜索路径。

您没有要求非常基本的内容。Windows根本不支持您想要的东西

您有一些解决此问题的选项:

  • 创建两个DLL。您的插件实现dll,它静态链接到您需要的任何其他dll。和一个简单的“facade”dll,由托管应用程序加载。facade dll先调用SetDllDirectory,然后调用LoadLibrary,以加载具有所需搜索路径的实现dll,然后,对于每个插件导出的函数,它实现一个存根函数,该存根函数使用GetProcAddress直接将调用传递给实现dll
如果插件接口复杂,但您使用的dll接口不复杂,则:

  • 放弃并仅使用LoadLibrary(具有显式路径)和GetProcAddress访问附属dll中的功能。痛苦

  • 最后一个选项是文档记录最少、windows程序员理解最差的选项。基本上,我们使用windows版本的技术来支持.NET:并行程序集。不要害怕。“并排程序集”只是一个普通的旧dll,但附带一个.manifest文件,该文件提供了一些关于它的额外信息

我们之所以要这样做,是因为通过SxS技术链接的dll的搜索顺序不同于常规dll搜索顺序:-即-搜索c:\windows\WinSxS后,windows将搜索与引用dll的dll相同的文件夹,而不是exe的文件夹

首先清点插件dll需要链接的所有附属dll,并从中创建“程序集”。这意味着:创建一个包含大量file=节点的.manifest文件。您需要为程序集指定一个名称。让我们称之为“MyAssembly”

在dll文件夹中创建文件“MyAssembly.manifest”,其内容类似于以下内容:(列出需要包含的每个dll)


假设本机代码和您可以使用(而不是任何形式的隐式链接),请使用和查找dll的运行位置

HMODULE hModule = GetModuleHandleW(L"RunningDll.dll");
WCHAR path[MAX_PATH];
GetModuleFileNameW(hModule, path, MAX_PATH);
然后将dll的基本名称替换为要加载的plugin.dll的名称

CString plugin(path);
int pos = plugin.Find(L"RunningDll.dll");
plugin = plugin.Left(pos);
plugin += L"pluginName.dll";
调用生成的字符串。

在这种情况下是您的朋友。不久前,我遇到了完全相同的问题,其实很简单。指定链接器(
/DELAYLOAD
标志)哪些模块是延迟加载的,基本上它们没有在PE头中作为显式导入列出,因此加载程序在找不到所述模块时不会抱怨,并且来自这些模块的所有函数调用都封装在存根中,以确保加载模块并找到函数


因此,假设您希望延迟加载XmlLite库。首先,在链接器标志中指定
/DELAYLOAD:XmlLite.dll
。然后在模块的初始化函数(最好是
DllMain
)中,将XmlLite DLL解包到一个临时文件夹中,然后对其调用
LoadLibrary
。从那时起,对XmlLite.dll导出的任何函数的每次调用都将自动解决。

这没有多大帮助,因为他不能更改宿主应用程序的代码。据我所知,他可以更改插件dll。他无法更改插件加载的第三方dll。但是由于问题是从他的插件dll加载那些dll,他可以在代码中对此做些什么。你的建议不可行,因为我的代码从来没有启动过。不要从DllMain调用LoadLibrary。From“入口点函数应仅执行简单的初始化或终止任务。它不得调用LoadLibrary或LoadLibraryEx函数(或调用这些函数的函数),因为这可能会在DL中创建依赖项循环
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <file name="firstrequireddll.dll"/>
    <file name="2ndrequireddll.dll"/>
</assembly>
#if _MSC_VER >= 1400 // VS2005 added this directive
#pragma comment(linker, \
    "\"/manifestdependency:type='Win32' "\
    "name='Company.Product.Subsystem' "\
    "version='6.0.0.0' "\
    "processorArchitecture='*' "\
    "language='*'\"")
#endif
HMODULE hModule = GetModuleHandleW(L"RunningDll.dll");
WCHAR path[MAX_PATH];
GetModuleFileNameW(hModule, path, MAX_PATH);
CString plugin(path);
int pos = plugin.Find(L"RunningDll.dll");
plugin = plugin.Left(pos);
plugin += L"pluginName.dll";