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

Shell Extensions: Static-Linking vs. Dynamic-Linking of C/C++ Run-Time DLLs

本文关键字:链接 静态 动态 DLL 运行时 扩展 C++ 外壳      更新时间:2023-10-16

在构建Windows资源管理器外壳扩展(当前使用VS2010 SP1)时,您建议静态链接(到CRT、C++运行时和其他支持库(如ATL))还是动态链接

静态链接选项的好处之一是使部署更容易(事实上,通过这种方式,可以只在proc COM服务器DLL中部署shell扩展,而不需要外部依赖其他C/C++运行时DLL)。

动态链接的情况下,如果WindowsSystem32中的msvcr100.dllmsvcp100.dll等DLL由shell扩展使用,那么好处是如果Microsoft修复了这些DLL中的某些内容(例如安全修复程序),则修复程序将自动由自定义shell扩展使用


然而,糟糕的是,这些"全局"修复还可能在依赖代码中引入错误和破坏

至于VCRedist DLL的应用程序本地部署,我不确定它在shell扩展的情况下如何工作。应该在shell扩展COM DLL中嵌入哪种清单,以引用shell扩展文件夹下的VCRedist DLL?

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

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

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

它有力地避免了对象布局问题,COM严格只与接口配合使用。严格禁止在互操作边界上传递C++对象或异常。唯一的实现细节是严格的__stdcall调用约定和严格绑定到接口IID的接口v-table布局。更改接口需要选择一个新的IID。

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

这个问题的简短答案是"取决于"。但这可能是你已经走了多远。

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

如果你发现你的扩展从几十千字节增长到几兆字节,那么你必须考虑是让客户端下载redist包更好,还是将其作为安装的一部分。

没有一个答案总是正确的。这是一个折衷方案,你必须判断哪一个对你(和你的客户)最有意义——简单安装,还是依赖外部DLL。

如果你将redist DLL安装在与可执行文件相同的文件夹中,Windows会发现DLL本身就在那里,所以不需要任何额外的工作(不适用于shell扩展)