Shell 扩展:C/C++ 运行时 DLL 的静态链接与动态链接

Posted

技术标签:

【中文标题】Shell 扩展:C/C++ 运行时 DLL 的静态链接与动态链接【英文标题】:Shell Extensions: Static-Linking vs. Dynamic-Linking of C/C++ Run-Time DLLs 【发布时间】:2013-08-31 09:40:13 【问题描述】:

在构建 Windows Explorer shell 扩展(目前使用 VS2010 SP1)时,您是否建议 static-linking(用于 CRT、C++ 运行时和其他支持库,如 ATL ) 还是动态链接

static-linking 选项的好处之一是使部署更容易(实际上,通过这种方式,可以只在-proc COM 服务器 DLL,不依赖于其他 C/C++ 运行时 DLL)。

动态链接的情况下,如果msvcr100.dllmsvcp100.dllWindows\System32中的DLL被shell扩展使用,好在如果微软修复了一些东西(例如安全修复)在这些 DLL 中,这些修复自动由自定义 shell 扩展使用。 然而,不好的是那些“全局”修复也可能在依赖代码中引入错误和破坏东西。

至于 VCRedist DLL 的 app-local 部署,我不确定它在 shell 扩展的情况下如何工作。应该在 shell 扩展 COM DLL 中嵌入什么样的清单来引用 shell 扩展文件夹下的 VCRedist DLL?

【问题讨论】:

【参考方案1】:

当您有多个模块相互交互时,必须使用 CRT 的 DLL 版本通常是一个非常困难的要求。一个特别重要的方面是在所有模块中使用相同的分配器。这样在一个模块中分配的对象可以被另一个模块中的代码安全地销毁。这在 C++ 中经常出现,做一些像从函数返回 std::string 这样简单的事情是非常麻烦的。它在被调用者中创建,需要在调用者中销毁。如果跨模块边界进行函数调用并且两个模块使用不同的堆,则会发生灾难。

标准 C++ 对象的布局也与 CRT 实现相关联。不同版本的编译器使用不同的 std::string 实现。如果一个模块使用与另一个模块不同的实现,那么灾难就会发生,调用者根本无法正确使用被调用者创建的对象。即使像调试设置这样简单的事情也可能导致不匹配,Microsoft CRT 中的迭代器调试支持因导致不匹配而臭名昭著,它使 STL 对象变大。

然而,这些是 COM 中避免的问题。有一个强大的内存管理协议,基于 IUnknown 接口的 AddRef 和 Release 方法。这使对象的创建者始终是对象的所有者并负责销毁它。其他分配,如 BSTR 和 SAFEARRAY,必须始终从保证在进程内共享的堆中进行。 CoTaskMemAlloc、CoTaskMemFree 和 IMalloc 实现了该管道。

并且它强烈避免了对象布局问题,COM 严格只使用接口。严格禁止跨互操作边界传递 C++ 对象或异常之类的操作。唯一的实现细节是调用约定,严格的 __stdcall,和接口 v-table 布局,严格绑定到接口的 IID。更改界面需要选择新的 IID。

长话短说,因此您根本不需要使用 CRT 的共享版本。事实上,许多 COM 服务器都是用 /MT 编译的。使用 /MD 也很好,如果您的 COM 服务器本身是使用多个模块实现的,您只会考虑它。

【讨论】:

感谢您的分析。有人让我注意到 CRT 使用了很多资源,例如FLS 索引是最快用完的索引,当您加载多个静态链接的 DLL 时,LoadLibrary() 可能会失败。您如何看待静态链接 CRT 的“资源耗尽” 问题? (如前所述,为了使事情 - 例如部署 - 更简单,对于小型扩展,我只使用了/MT static-linking。)【参考方案2】:

这个问题的简短回答是“视情况而定”。但这可能是你已经走了多远。

然而,它确实变成了“两害相权取其轻”的选择,并且在很大程度上取决于你的扩展在做什么。如果你构建它是静态的,与非静态相比,它有多大?如果没有太大区别,那么您显然没有使用大量的 DLL 代码,这很好。

如果您发现您的扩展程序从几十 KB 增长到几兆字节,那么您必须考虑是要求客户端下载 redist-package 还是将其作为安装的一部分更好。

没有一个答案是永远正确的。这是一种妥协,您必须判断哪个对您(和您的客户)最有意义 - 易于安装,或依赖外部 DLL。

如果您将 redist DLL 与可执行文件安装在同一文件夹中,Windows 会自行找到该 DLL,因此不需要任何额外工作。(不适用于 shell 扩展)

【讨论】:

如果您将 redist DLL 与可执行文件安装在同一文件夹中,Windows 会自行找到该 DLL,因此不需要任何额外工作。 可能不是这样对于进程内 shell 扩展,因为主机可执行文件不会与 shell 扩展位于同一目录中。 @DavidHeffernan:好的,所以我会删除那句话,因为我不知道如何使它起作用。你有什么建议吗? 加载程序处理导入表,并在您的任何代码运行之前执行此操作。所以你不能轻易影响搜索顺序。也许可以使用清单,我不知道。就个人而言,我为我的 shell 扩展静态链接运行时。 所以,基本上是“将 DLL 放在正常路径(system32 或类似路径),或静态链接” @Mr.C64:我从来没有写过shell扩展,但是,是的,这似乎是结论——System32,静态链接还是失败!在这些选择中,我认为静态链接似乎是最不坏的选择。

以上是关于Shell 扩展:C/C++ 运行时 DLL 的静态链接与动态链接的主要内容,如果未能解决你的问题,请参考以下文章

开发函数计算的正确姿势———为 PHP 运行时添加自定义扩展

shell32.dll里没有扩展名的图标

php 编译5.3.8源码包dll扩展,如何选择生成dll是nts还是ts的?

C/C++ VS 代码扩展引发构建错误:““C/C++”任务的任务提供程序意外提供了“shell”类型的任务。”

将数据从C#传入和传出DLL

请问自己的软件如何运行Lua脚本