如何将Win32 App插件加载到自己的目录中

时间:2010-04-14 12:58:46

标签: dll winapi plugins search-path

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

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

相关细节:

  • 主机应用程序插件位于主机应用程序强制要求的目录中。该目录不在DLL搜索路径中,我无法控制它。
  • 插件本身打包为插件目录的子目录,包含插件代码本身,以及与插件相关的任何资源(例如图像,配置文件......)。我控制该子目录中的内容,称为“捆绑”,但不是它所在的位置。
  • 该应用程序的常见插件安装习惯用于最终用户将插件包复制到插件目录。

此插件是该插件的Macintosh版本的端口。在Mac上没有问题,因为每个二进制文件都包含自己的动态库搜索路径,我设置为我需要的插件二进制文件。要在Mac上设置它只需要在Xcode IDE中进行项目设置。这就是为什么我希望在Visual Studio中有类似的东西,但我找不到任何相关的东西。此外,Visual Studio的帮助不过是谷歌。

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

作为一名Mac开发人员,我意识到我可能会要求一些非常基础的东西。如果是这样的话,我道歉,但我已经没有头发了。

4 个答案:

答案 0 :(得分:7)

你不是要求非常基本的东西。 Windows根本不支持你想要的东西。

您可以选择解决此问题:

  • 创建两个DLL。你的插件实现dll,静态链接你需要的任何其他dll。还有一个由托管应用程序加载的简单“外观”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 = nodes创建一个.manifest文件。您需要为程序集命名。让我们称之为“MyAssembly”。

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

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity name="MyAssembly" processorArchitecture="*" type="win32" version="1.0.0.1"/>
    <file name="firstrequireddll.dll"/>
    <file name="2ndrequireddll.dll"/>
</assembly>

现在,这就是你的程序集清单。我们已经完成了一半。

下一半是实际让你的dll使用程序集,为此你需要在你的Dll文件中添加一个清单资源。该清单最终需要包含以下内容: -

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <dependency>
        <dependentAssembly>
            <assemblyIdentity type="win32" name="MyAssembly" version="1.0.0.1" processorArchitecture="*"/>
        </dependentAssembly>
    </dependency>
</assembly>

显然,应用程序清单(嵌入在dll中时是一个令人困惑的名称)也可以使用<file>节点,因此可以跳过创建程序集,并且只需使用

<?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>

作为dll的清单。我还没有玩过那个迭代,所以我不确定这是如何改变正常的dll搜索路径(如果有的话)。


在不了解您的开发环境的情况下,很难知道如何建议您如何向dll添加清单。如果您正在编辑.rc文件并手动输入清单,请知道在Dll中使用的资源ID是2,而不是1,它通常用于exe示例中。

如果您使用的是DevStudio 2005或更高版本,那么有一个方便的#pragma指令可以使所有内容神奇地拥有正确的ID并位于正确的位置。


如果项目设置为默认值,VS2005及更高版本将自动生成,并根据需要嵌入清单。这个#pragma将添加额外的程序集依赖项 到生成的清单: -

#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

答案 1 :(得分:1)

在这种情况下,

Delay loaded DLLs是你的朋友。我不久前遇到了完全相同的问题而且实际上相当简单。您指定链接器(/DELAYLOAD标志)哪些模块是延迟加载的,基本上它们的模块未在PE标头中列为显式导入,因此加载器在找不到所述模块时将不会抱怨所有模块对这些模块中的函数的调用被包装在存根中,该存根确保模块被加载和放置。找到了这个功能。

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

假设本机代码并且您可以使用explicit run-time dynamic link(而不是任何形式的隐式链接),请使用GetModuleHandleGetModuleFileName找出您的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";

在生成的字符串上调用LoadLibrary