ATL - DLL exporting C++ classes derived from ATL base classes

Source: Microsoft Support

RAPID PUBLISHING

RAPID PUBLISHING ARTICLES PROVIDE INFORMATION DIRECTLY FROM WITHIN THE MICROSOFT SUPPORT ORGANIZATION. THE INFORMATION CONTAINED HEREIN IS CREATED IN RESPONSE TO EMERGING OR UNIQUE TOPICS, OR IS INTENDED SUPPLEMENT OTHER KNOWLEDGE BASE INFORMATION.

Symptoms


A C++ class A is derived from ATL base class B (e.g. CComObjectRootEx<class ATL::CComSingleThreadModel>) and is exported from a DLL (call it ExportingATL.DLL). In a module that is a consumer of this DLL (call it ImportingFromDLL.DLL), a class C is also derived from ATL base class B, causing linker errors when one builds the ImportingFromDLL.DLL and links with ExportingATL.Lib.
Following are the linker errors that will come:

ExportingATL.lib(ExportingATL.dll) : error LNK2005: "public: unsigned long __thiscall ATL::CComObjectRootEx<class ATL::CComSingleThreadModel>::InternalAddRef(void)" (?InternalAddRef@?$CComObjectRootEx@VCComSingleThreadModel@ATL@@@ATL@@QAEKXZ) already defined in ImportingFromDLL.obj
ExportingATL.lib(ExportingATL.dll) : error LNK2005: "public: unsigned long __thiscall ATL::CComObjectRootEx<class ATL::CComSingleThreadModel>::InternalRelease(void)" (?InternalRelease@?$CComObjectRootEx@VCComSingleThreadModel@ATL@@@ATL@@QAEKXZ) already defined in ImportingFromDLL.obj
Creating library .\Debug/ImportingFromDLL.lib and object .\Debug/ImportingFromDLL.exp
UsingClass.obj : warning LNK4217: locally defined symbol ?_AtlInitialConstruct@?$CComObjectRootEx@VCComSingleThreadModel@ATL@@@ATL@@QAEJXZ (public: long __thiscall ATL::CComObjectRootEx<class ATL::CComSingleThreadModel>::_AtlInitialConstruct(void)) imported in function __catch$?CreateInstance@?$CComObject@VCAtlExtendedType@@@ATL@@SGJPAPAV12@@Z$0
.\Debug/ImportingFromDLL.dll : fatal error LNK1169: one or more multiply defined symbols found

If one does not use ATL in the client DLL, he gets:

error LNK2019: unresolved external symbol "__declspec(dllimport) public: long __thiscall ATL::CComObjectRootEx<class ATL::CComSingleThreadModel>::_AtlInitialConstruct(void)" (__imp_?_AtlInitialConstruct@?$CComObjectRootEx@VCComSingleThreadModel@ATL@@@ATL@@QAEJXZ) referenced in function __catch$?CreateInstance@?$CComCreator@V?$CComObject@VCSecondATLObject@@@ATL@@@ATL@@SGJPAXABU_GUID@@PAPAX@Z$0

Cause



When we export a class derived from a templated base class (such as those in ATL), starting from VC++ 8.0, its base classes , which are specializations of class templates are also exported. This is a design change made in C++ Compiler.


Earlier when one exported a class A from a DLL, only that class (A) got exported. But consider a scenario in which class A was derived from a template-based base class B, and in the client of the DLL, we have a class that also derives from class B. Since we have have exported only class A from the DLL, we would need to define class B again on the client side. So there was need to keep the two definitions of same base class consistent and synchronous, causing a lot of maintenance problems; e.g., every time class B needs to be changed, both the DLL and its client would need to rebuild.


To overcome these problems, a design change was made in C++ compiler to export template-based base classes along with the exported derived class.  As the base class is a template-based class, it is specialized by the compiler in order to export it (we can't export unspecialized template classes).  If the consumer/client of the DLL is also using ATL and has caused the same specializations of the base class(es), this will cause linker errors about them being already defined.

Resolution



ATL library was designed with the intention of developing COM-based types which are not exported directly as classes, but which are exposed through COM interfaces. It is not a recommended practice (and is considered a bad design) to export COM types directly, rather than through their interfaces, as it breaks the implementation-hiding principle of COM.


Here are two recommended ways in which COM classes built using ATL should be exposed:
  1. Export ATL types as registered COM object interface implementations.
  2. Export a function that returns an interface pointer to the type.

DISCLAIMER

MICROSOFT AND/OR ITS SUPPLIERS MAKE NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY, RELIABILITY OR ACCURACY OF THE INFORMATION CONTAINED IN THE DOCUMENTS AND RELATED GRAPHICS PUBLISHED ON THIS WEBSITE (THE “MATERIALS”) FOR ANY PURPOSE. THE MATERIALS MAY INCLUDE TECHNICAL INACCURACIES OR TYPOGRAPHICAL ERRORS AND MAY BE REVISED AT ANY TIME WITHOUT NOTICE.


TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, MICROSOFT AND/OR ITS SUPPLIERS DISCLAIM AND EXCLUDE ALL REPRESENTATIONS, WARRANTIES, AND CONDITIONS WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO REPRESENTATIONS, WARRANTIES, OR CONDITIONS OF TITLE, NON INFRINGEMENT, SATISFACTORY CONDITION OR QUALITY, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, WITH RESPECT TO THE MATERIALS.
Eigenschappen

Artikel-id: 960961 - Laatst bijgewerkt: 8 dec. 2008 - Revisie: 1

Feedback