Exporting String Classes Using CStringT
In the past, MFC developers have derived from CString
to specialize their own string classes. In Microsoft Visual C++.NET (MFC 8.0), the CString class was superseded by a template class called CStringT. This provided several benefits:
It allowed the MFC
CString
class to be used in ATL projects without linking in the larger MFC static library or DLL.With the new
CStringT
template class, you can customizeCString
behavior using template parameters that specify character traits, similar to the templates in the C++ Standard Library.When you export your own string class from a DLL using
CStringT
, the compiler also automatically exports theCString
base class. SinceCString
is itself a template class, it may be instantiated by the compiler when used, unless the compiler is aware thatCString
is imported from a DLL. If you have migrated projects from Visual C++ 6.0 to Visual C++.NET, you might have seen linker symbol errors for a multiply-definedCString
because of the collision of theCString
imported from a DLL and the locally instantiated version. The proper way to do this is described below.
The following scenario will cause the linker to produce symbol errors for multiply defined classes. Assume that you are exporting a CString
-derived class (CMyString
) from an MFC extension DLL:
// MyString.h
class AFX_EXT_CLASS CMyString : public CString
{
// Your implementation code
};
The consumer code uses a mixture of CString
and CMyString
. "MyString.h" is not included in the precompiled header, and some usage of CString
does not have CMyString
visible.
Assume that you use the CString
and CMyString
classes in separate source files, Source1.cpp and Source2.cpp. In Source1.cpp, you use CMyString
and #include MyString.h. In Source2.cpp, you use CString
, but do not #include MyString.h. In this case, the linker will complain about CStringT
being multiply defined. This is caused by CString
being both imported from the DLL that exports CMyString
, and instantiated locally by the compiler through the CStringT
template.
To resolve this problem, do the following:
Export CStringA
and CStringW
(and the necessary base classes) from MFC90.DLL. Projects that include MFC will always use the MFC DLL exported CStringA
and CStringW
, as in previous MFC implementations.
Then create an exportable derived class using the CStringT
template, as CStringT_Exported
is below, for example:
#ifdef _AFXDLL
#define AFX_EXT_CSTRING AFX_EXT_CLASS
#else
#define AFX_EXT_CSTRING
#endif
template< typename BaseType, class StringTraits >
class AFX_EXT_CSTRING CStringT_Exported
: public CStringT< BaseType, StringTraits >
{
// Reimplement all CStringT<> constructors and
// forward to the base class implementation
};
In AfxStr.h, replace the previous CString
, CStringA
, and CStringW
typedefs as follows:
typedef CStringT_Exported< wchar_t,
StrTraitMFC< wchar_t > > CStringW;
typedef CStringT_Exported< char,
StrTraitMFC< char > > CStringA;
typedef CStringT_Exported< TCHAR,
StrTraitMFC< TCHAR > > CString;
There are several caveats:
You should not export
CStringT
itself because this will cause ATL-only projects to export a specializedCStringT
class.Using an exportable derived class from
CStringT
minimizes having to re-implementCStringT
functionality. Additional code is limited to forwarding constructors to theCStringT
base class.CString
,CStringA
, andCStringW
should only be marked__declspec(dllexport/dllimport)
when you are building with an MFC shared DLL. If linking with an MFC static library, you should not mark these classes as exported; otherwise, internal use ofCString
,CStringA
, andCStringW
inside user DLLs will markCString
as exported as well.