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

590 views Asked by At

When building Windows Explorer shell extensions (currently using VS2010 SP1), do you suggest static-linking (to CRT, C++ run-time and other support libraries like ATL) or dynamic-linking?

One of the benefits of the static-linking option is to make the deployment easier (in fact, in this way, it's possible to just deploy the shell extension in-proc COM server DLL, without external dependencies on other C/C++ run-time DLLs).

In case of dynamic-linking, if msvcr100.dll, msvcp100.dll, etc. DLLs in Windows\System32 are used by the shell extension, the good thing is that if Microsoft fixes something (e.g. security fixes) in those DLLs, the fixes are automatically used by the custom shell extension.
However, the bad thing is that those "global" fixes may also introduce bugs and break things in the dependant code.

As for app-local deployment of VCRedist DLLs, I'm not sure how it might work in case of a shell extension. What kind of manifest should be embedded in the shell extension COM DLL to refer to the VCRedist DLLs under the shell extension's folder?

2

There are 2 answers

1
Hans Passant On BEST ANSWER

Having to use the DLL version of the CRT is normally a pretty hard requirement when you have multiple modules interact with each other. One particularly important aspect is using the same allocator in all modules. So that an object that was allocated in one module can be safely destroyed by code in another module. This comes up in C++ frequently, doing something as simple as returning an std::string from a function is very troublesome. It gets created in the callee and needs to be destroyed in the caller. Disaster strikes if the function call was made across module boundaries and the two modules use different heaps.

The layout of standard C++ objects is tied to the CRT implementation as well. Different versions of the compilers use different std::string implementations. Disaster strikes if one module uses a different implementation from the other, the caller simply cannot correctly use the object created by the callee. Even something as simple as debug settings can cause a mismatch, the iterator debugging support in the Microsoft CRT is particularly notorious for causing a mismatch, it makes the STL object bigger.

These are however problems that are avoided in COM. There's a strong memory management protocol, based on the IUnknown interface's AddRef and Release methods. Which keeps the creator of the object always the owner of the object and responsible for destroying it. Other allocations, like BSTR and SAFEARRAY, must always be made from a heap that's guaranteed to be shared within a process. CoTaskMemAlloc, CoTaskMemFree and IMalloc implement that plumbing.

And it strongly avoids the object layout problem, COM strictly works with interfaces only. Doing something like passing C++ objects or exceptions across the interop boundary is strictly forbidden. The only implementation details are the calling convention, strictly __stdcall, and the interface v-table layout, strictly tied to the IID of the interface. Changing the interface requires picking a new IID.

Long story short, you therefore have no need at all to use the shared version of the CRT. And in fact many COM servers are compiled with /MT. Using /MD is fine as well, you'd only consider it if your COM server itself is implemented using multiple modules.

8
Mats Petersson On

The short answer to this question is "it depends". But that's probably how far you got already.

However, it really becomes a choice of "the lesser of two evils", and to a large degree it depends on what your extension is doing. If you build it static, how large is it, compared to non-static? If there isn't much of a difference, then you are obviously not using a huge amount of DLL code anyways, which would be fine.

If you find your extension grows from a few dozen kilobytes to several megabytes, then you have to ponder whether it's better to ask the clients to download the redist-package, or include it as part of your installation.

There is no one answer that is always right. It's a compromise, and you have to judge which makes most sense for you (and for your clients) - easy installation, or dependance on external DLL's.

If you install the redist DLL's in the same folder as the executable, Windows will find the DLL's there by itself, so shouldn't need any extra work. (Not applicable for shell extensions)