Discussion:
Conflicting C Runtimes can of worms
(too old to reply)
Murrgon
2009-10-13 17:05:30 UTC
Permalink
Ref URL: http://msdn.microsoft.com/en-us/library/2kzt1wy3%28VS.71%29.aspx

<quote>
Do not mix static and dynamic versions of the run-time libraries. Having more
than one copy of the run-time libraries in a process can cause problems,
because static data in one copy is not shared with the other copy. The linker
prevents you from linking with both static and dynamic versions within one
.exe file, but you can still end up with two (or more) copies of the run-time
libraries. For example, a dynamic-link library linked with the static
(non-DLL) versions of the run-time libraries can cause problems when used with
an .exe file that was linked with the dynamic (DLL) version of the run-time
libraries. (You should also avoid mixing the debug and non-debug versions of
the libraries in one process.)
</quote>

The above has long been established. However, I have discovered it to be a
*very* common occurrence in open source software to have different components
specify different code generation rules, which violates the above cautionary
statement. As a result I have a few questions that have been nagging me.

1) If I have a DLL that uses the shared/dll version of the CRT, but links with
a library that uses the static version of the crt, what do you end up with?
(Besides linker warnings). Does the code the library depend on actually get
linked in to the DLL?

2) Same situation as above, but you've told your DLL to ignore libcmt(d).lib.
Will this force the linker to ONLY use the shared/dll version of the CRT,
despite the other lib saying otherwise?

3) What are the dangers of having one DLL using the shared/dll version of the
CRT and another that has been statically linked with the CRT? This can happen
often when using 3rd party DLLs that don't provide the source to build it.


Thank you
Murrgon
Nathan Mates
2009-10-13 20:18:03 UTC
Permalink
Post by Murrgon
<quote>
Do not mix static and dynamic versions of the run-time libraries.
</quote>
The above has long been established. However, I have discovered it
to be a *very* common occurrence in open source software to have
different components specify different code generation rules, which
violates the above cautionary statement.
What is the problem? If they're open source you could -- you know
-- *modify* the open source library .vcproj files to be consistent in
runtime lib choice. Bonus points for making different build
configurations for all 4 possible choices of runtime libs --
Multithreaded, Multithreaded DLL, Multithreaded Debug & Multithreaded
Debug DLL. Then, you can pick and choose which of the 4 you want to
use. Submit your changes back to the project maintainers for extra
credit.

Nathan Mates
--
<*> Nathan Mates - personal webpage http://www.visi.com/~nathan/
# Programmer at Pandemic Studios -- http://www.pandemicstudios.com/
# NOT speaking for Pandemic Studios. "Care not what the neighbors
# think. What are the facts, and to how many decimal places?" -R.A. Heinlein
Murrgon
2009-10-13 20:30:41 UTC
Permalink
Post by Nathan Mates
What is the problem? If they're open source you could -- you know
-- *modify* the open source library .vcproj files to be consistent in
runtime lib choice. Bonus points for making different build
configurations for all 4 possible choices of runtime libs --
Multithreaded, Multithreaded DLL, Multithreaded Debug & Multithreaded
Debug DLL. Then, you can pick and choose which of the 4 you want to
use. Submit your changes back to the project maintainers for extra
credit.
The problem is, your response doesn't answer the questions. Yes, I can get
all of the code, where available, change the settings and recompile it, but
that doesn't tell me the affect conflicting settings actually has on software
that has already been released.


Murrgon
Murrgon
2009-10-13 20:46:48 UTC
Permalink
Post by Nathan Mates
What is the problem? If they're open source you could -- you know
-- *modify* the open source library .vcproj files to be consistent in
runtime lib choice. Bonus points for making different build
configurations for all 4 possible choices of runtime libs --
Multithreaded, Multithreaded DLL, Multithreaded Debug & Multithreaded
Debug DLL. Then, you can pick and choose which of the 4 you want to
use. Submit your changes back to the project maintainers for extra
credit.
The other thing to add to this is, if I am going to submit a change to an open
source project, it's nice if I can give an explanation as to why I made the
changes and what the ramifications were.

Murrgon
Ben Voigt [C++ MVP]
2009-10-13 23:58:19 UTC
Permalink
Post by Murrgon
3) What are the dangers of having one DLL using the shared/dll version of
the CRT and another that has been statically linked with the CRT? This
can happen often when using 3rd party DLLs that don't provide the source
to build it.
That is indeed common, and in fact might even mix CRTs from different
compiler vendors.

As long as there are no CRT objects being passed across the DLL boundary,
and every memory block is deallocated from the same library that allocated
it, everything will be ok. So with DLL exports, stick to types that are
globally defined by Windows, such as HANDLE, HRESULT, DWORD, BSTR, since
every library will agree on their definition.

Within a library you can use structures such as FILE and functions such as
malloc, since only that library will see them.
Post by Murrgon
Thank you
Murrgon
Carl Daniel [VC++ MVP]
2009-10-14 04:02:05 UTC
Permalink
Post by Murrgon
1) If I have a DLL that uses the shared/dll version of the CRT, but
links with a library that uses the static version of the crt, what do
you end up with? (Besides linker warnings). Does the code the
library depend on actually get linked in to the DLL?
You'll get linker errors/warnings due to conflicting symbols. Usually, the
only correct way to resolve this is to use the /NODEFAULTLIB linker option
to suppress all CRT selections baked into the code, then explicitly link in
the version of the CRT that you need. If you _need_ version X and the
library _needs_ version Y, then you'll need to split code into two or more
DLLs, each depending on exactly one version of the CRT.
2) Same situation as above, but you've told your DLL to ignore
libcmt(d).lib. Will this force the linker to ONLY use the shared/dll
version of the CRT, despite the other lib saying otherwise?
If you've used /NODEFAULTLIB to suppress default dependencies to particular
libraries, any remaining default references to other libraries will still be
honored by the linker (and may still result in conflicts - e.g. the code
could be trying to default link 3 or more different CRT flavors).
3) What are the dangers of having one DLL using the shared/dll
version of the CRT and another that has been statically linked with
the CRT? This can happen often when using 3rd party DLLs that don't
provide the source to build it.
No danger if objects allocated in one DLL always remain within that DLL. If
any object allocated in one DLL are shared with code in another DLL, you'll
likely have problems - unless the sharing is through pure interfaces, such
as with COM components.

-cd
Pavel A.
2009-10-14 08:16:31 UTC
Permalink
"Carl Daniel [VC++ MVP]" <***@mvps.org.nospam>
wrote in message news:***@TK2MSFTNGP02.phx.gbl...

.............
Post by Carl Daniel [VC++ MVP]
Post by Murrgon
3) What are the dangers of having one DLL using the shared/dll
version of the CRT and another that has been statically linked with
the CRT? This can happen often when using 3rd party DLLs that don't
provide the source to build it.
No danger if objects allocated in one DLL always remain within that DLL.
If any object allocated in one DLL are shared with code in another DLL,
you'll likely have problems - unless the sharing is through pure
interfaces, such as with COM components.
Hi Carl,
Could you comment a bit more how this actually works:
Do CRT copies in each DLL use the private pool of the DLL?
How file handles are shared between multiple copies of CRT in the main app
and DLLs?

What is a good, supported way to build a DLL that works with VC++ 2005 or
2008 apps
built with *any* RTL variant - static or DLL, debug or release?
(of course, given that malloc's and free's are done in same module)

Regards,
--pa
Carl Daniel [VC++ MVP]
2009-10-14 14:31:42 UTC
Permalink
Post by Pavel A.
"Carl Daniel [VC++ MVP]"
Post by Carl Daniel [VC++ MVP]
Post by Murrgon
3) What are the dangers of having one DLL using the shared/dll
version of the CRT and another that has been statically linked with
the CRT? This can happen often when using 3rd party DLLs that don't
provide the source to build it.
No danger if objects allocated in one DLL always remain within that
DLL. If any object allocated in one DLL are shared with code in
another DLL, you'll likely have problems - unless the sharing is
through pure interfaces, such as with COM components.
Hi Carl,
Do CRT copies in each DLL use the private pool of the DLL?
How file handles are shared between multiple copies of CRT in the
main app and DLLs?
Each CRT has it's own heap. i.e. it's not the executable objects (EXEs and
DLLs) that define pools, it's CRT instances. If you have the CRT linked
statically twice and dynamically once in a single EXE+DLLs, then you have
three separate heaps (and three separate copies of other CRT structures such
as file descriptors).

File handles, e.g. HANDLE, are kernel objects and are valid throughout the
process. File descriptors, e.g. FILE* and file numbers are unique to each
CRT instance and cannot be shared between code using different copies of the
CRT.
Post by Pavel A.
What is a good, supported way to build a DLL that works with VC++
2005 or 2008 apps
built with *any* RTL variant - static or DLL, debug or release?
(of course, given that malloc's and free's are done in same module)
Follow the model of the Win32 API:

- Only pure "C" interaces - no C++ class at all.
- Use opaque "handles" to pass (temporary) ownership of module-allocated
objects to callers.
- For every kind of "handle" that's returned by your API, providing a
matching free/release/delete funciton.

By far the most widely applicable solution is to use COM - it was designed
for this very purpose. You can definitely "roll your own" though, just just
need to be mindful in your API design that 100% of the code that allocates,
frees or interprets the contents of memory blocks created by your DLL
resides within your DLL. You can loosen the "interprets the contents"
requirement for pure C-style structs, just make sure that your API header
files explicitly set packing around the struct definition so that you're not
dependent on the context. Likewise, make sure to explicitly decorate all of
your API entry points with a calling convention (e.g. __stdcall) - never
rely on the default.

-cd
Murrgon
2009-10-15 13:39:15 UTC
Permalink
Post by Carl Daniel [VC++ MVP]
Each CRT has it's own heap. i.e. it's not the executable objects (EXEs and
DLLs) that define pools, it's CRT instances. If you have the CRT linked
statically twice and dynamically once in a single EXE+DLLs, then you have
three separate heaps (and three separate copies of other CRT structures such
as file descriptors).
File handles, e.g. HANDLE, are kernel objects and are valid throughout the
process. File descriptors, e.g. FILE* and file numbers are unique to each
CRT instance and cannot be shared between code using different copies of the
CRT.
Post by Pavel A.
What is a good, supported way to build a DLL that works with VC++
2005 or 2008 apps
built with *any* RTL variant - static or DLL, debug or release?
(of course, given that malloc's and free's are done in same module)
- Only pure "C" interaces - no C++ class at all.
- Use opaque "handles" to pass (temporary) ownership of module-allocated
objects to callers.
- For every kind of "handle" that's returned by your API, providing a
matching free/release/delete funciton.
By far the most widely applicable solution is to use COM - it was designed
for this very purpose. You can definitely "roll your own" though, just just
need to be mindful in your API design that 100% of the code that allocates,
frees or interprets the contents of memory blocks created by your DLL
resides within your DLL. You can loosen the "interprets the contents"
requirement for pure C-style structs, just make sure that your API header
files explicitly set packing around the struct definition so that you're not
dependent on the context. Likewise, make sure to explicitly decorate all of
your API entry points with a calling convention (e.g. __stdcall) - never
rely on the default.
This helps tremendously. Thank you very much.

Murrgon

Continue reading on narkive:
Loading...