可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
My product is a C++ library, which, on Windows, is distributed as a dll. It makes very little use of the c-runtime (basic iostream and that's it), so I'm sure that all recent versions of the CRT will be fine.
Since my client is supposed to build his application using my dll, I don't want to impose upon him any specific runtime version. I'd like my dll to bind to whatever runtime library version my client's app is using (and I can assume that he'll use dynamic linking for his CRT). After all, isn't that what dynamic linking is all about? Is that possible?
EDIT: linking the dll against the static runtime libs won't work either, because then the static runtime (from the dll) and the dynamic runtime (from the client's application) will be mixed, which is bad.
EDIT: What I'm mainly asking is how do I tell the runtime loader to link my dll against whatever CRT the application is linked with? Something with the manifest, perhaps?
More generally, my question is how to build a nicely-behaving dll, that's to be used by clients building they're own applications?
EDIT: Thanks to the advice in the answers, I've transferred all references to std classes into inlined functions in my headers, and linked my dll with the static runtime libraries. It now seems to work even in applications linked with different CRT versions.
回答1:
There's no real way to ensure your DLL works with multiple runtimes -- any of the types that change between them can lead to incompatibilities. For instance, the size of an object can change, or the location of members in them. There is very little room in C++ for this kind of thing.
The best thing you can do is statically link to the runtime and ensure the exported API is limited to types strictly under your control -- no passing std::string
to a function, no stdlib types as members, and don't new
in one DLL and delete
in another. Don't mix inline and exported functions (including constructors/destructors) for the same object, because member order and padding might change between compilers. The pimpl idiom might help here.
回答2:
If you expose any C++ objects across DLL boundaries then this is simply not possible. What you can do (and we use a 3rd-party DLL that does this) is build your library in multiple configurations (32-bit/64-bit, debug/release, static/dynamic runtime, static/dynamic library) to satisfy as many people as possible. This may be a bit tedious to setup at first, but once you have all the configurations setup it's just a matter of building them all. Of course you also need to consider which runtime you are building against (vc8, vc9, vc10, etc), so if you want to cover all the bases you could have quite a lot of configurations.
回答3:
Linking your DLL against the static runtime libs should work, except that you must be very careful about memory management (e.g. whoever calls your DLL can't free() or delete[] anything allocated by your DLL) and you cannot exchange standard C data structures (e.g. FILE*). (Am I missing anything?)
回答4:
You can achieve this by using WinAPI calls for I/O and anything else that possibly relies on the runtime.
The most painful part is that you may have to override the global new
and delete
to use WinAPI functions exclusively because they are likely to use malloc/free internally. There are many other painful aspects to this, and my opinions is that it isn't worth the trouble. Here is an article that covers this topic.
回答5:
If you want to expose your objects in a runtime neutral way then I can't see any solution other than COM.
回答6:
Well, there is a huge difference between the C-runtime and the C++-runtime. If you where to use msvcrt.dll, which in recent years became "knighted" as a true system DLL, you could rely on its existence on XP onwards (though for Windows 2000 you would need some redistributable for the version 6 of msvcrt.dll). You can make use of msvcrt.dll by compiling your code with the compiler from the latest WDKs (Windows Driver Kits). Even though this is user-mode code, this is a viable and good method to compiler it.
IOStreams, however, require the C++-runtime. And that complicates things a lot.
EDIT: linking the dll against the
static runtime libs won't work either,
because then the static runtime (from
the dll) and the dynamic runtime (from
the client's application) will be
mixed, which is bad.
Well, if you mix code in such a way, you have something wrong in your design. You would have similar problems when running a debug build of your DLL with a release build of the other code or vice versa.
I can only recommend that you either make direct use of COM, or - if that's too big - try to emulate some ideas of COM. The most important one would be that you have a factory function and that there is an (class) interface declared (and gets never changed) between those two pieces of code (i.e. the DLL and its caller). The factory function would return an instance of the class and the class would manage its lifetime itself (which implies that all code for allocation and deallocation would reside in the same entity, i.e. your DLL). The lifetime management would then be exposed via addref
and release
member functions. IUnknown
could be the basis for this interface of yours without relying on other parts of the actual COM.
EDIT: What I'm mainly asking is how do
I tell the runtime loader to link my
dll against whatever CRT the
application is linked with? Something
with the manifest, perhaps? More
generally, my question is how to build
a nicely-behaving dll, that's to be
used by clients building they're own
applications?
Not easy at all. Even if you had all the versions of VS installed, you'd have to script your way out of this dilemma to pick the right version.
回答7:
Your dll is linked against the c-runtime it was compiled with. Your application will always use this runtime. Anyone who links to your dll uses their c-runtime. So there won't be any problem with this.
回答8:
If you use C++, it seems to be impossible to cross runtime boundaries unless you limit yourself on what can be exposed. As mentioned earlier, std:: objects do not work (std::string for instance).
Here is a small example that will cause a crash:
class Base
{
public:
virtual ~Base()
{
}
};
class ClassInDll : public Base
{
public:
__declspec( dllexport ) ClassInDll( int arg );
__declspec( dllexport ) ~ClassInDll();
private:
int _arg;
};
If this class is compiled into a VS2008 release mode DLL and one builds a .exe in VS2008 debug mode doing the following:
ClassInDll* c = new ClassInDll( 1 );
delete c;
the "delete c" statement causes a crash.
It has to do with the fact that ClassInDll has a virtual destructor.