Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Exporting CString from a DLL

472 views
Skip to first unread message

EricW

unread,
Feb 28, 2008, 11:38:07 AM2/28/08
to
Hi All,
I'm trying to use a CString variable in a class that is exported from a
DLL and I'm getting the linker errors below. I changed the CString
declaration to CStringEx and exported that class like below which resulted
in the errors. If I take the regrettable step of commenting out the
declaration of CString, CStringA and CStringW in altstr.h (I'm not using MFC
at all) and replacing it with the corresponding CStringExA/W it seems
better, but that is solution I'd rather not do. Is there any other way to
use CString in a DLL.

Thanks,
EricW

class __declspec( dllexport ) Test
{
public:
CStringWEx m_strName;
};

CStringWEx is a class I derived from the base CString classes.


ServerEngine.lib(ServerEngine.dll) : error LNK2005: "public: __thiscall
ATL::CStringT<wchar_t,class ATL::StrTraitATL<wchar_t,class
ATL::ChTraitsCRT<wchar_t> > >::~CStringT<wchar_t,class
ATL::StrTraitATL<wchar_t,class ATL::ChTraitsCRT<wchar_t> > >(void)"
(??1?$CStringT@_WV?$StrTraitATL@_WV?$ChTraitsCRT@_W@ATL@@@ATL@@@ATL@@QAE@XZ)
already defined in AdminMsgs.obj

ServerEngine.lib(ServerEngine.dll) : error LNK2005: "public: __thiscall
ATL::CSimpleStringT<wchar_t,0>::operator wchar_t const *(void)const "
(??B?$CSimpleStringT@_W$0A@@ATL@@QBEPB_WXZ) already defined in AdminMsgs.obj

ServerEngine.lib(ServerEngine.dll) : error LNK2005: "public: class
ATL::CStringT<wchar_t,class ATL::StrTraitATL<wchar_t,class
ATL::ChTraitsCRT<wchar_t> > > & __thiscall ATL::CStringT<wchar_t,class
ATL::StrTraitATL<wchar_t,class ATL::ChTraitsCRT<wchar_t> >
>::operator=(class ATL::CStringT<wchar_t,class
ATL::StrTraitATL<wchar_t,class ATL::ChTraitsCRT<wchar_t> > > const &)"
(??4?$CStringT@_WV?$StrTraitATL@_WV?$ChTraitsCRT@_W@ATL@@@ATL@@@ATL@@QAEAAV01@ABV01@@Z)
already defined in ErrorLogOptionsDlg.obj

ServerEngine.lib(ServerEngine.dll) : error LNK2005: "public: __thiscall
ATL::CStringT<wchar_t,class ATL::StrTraitATL<wchar_t,class
ATL::ChTraitsCRT<wchar_t> > >::CStringT<wchar_t,class
ATL::StrTraitATL<wchar_t,class ATL::ChTraitsCRT<wchar_t> > >(void)"
(??0?$CStringT@_WV?$StrTraitATL@_WV?$ChTraitsCRT@_W@ATL@@@ATL@@@ATL@@QAE@XZ)
already defined in AdminMsgs.obj

http://msdn2.microsoft.com/en-us/library/ms174286(VS.80).aspx


Giovanni Dicanio

unread,
Feb 28, 2008, 2:09:34 PM2/28/08
to

"EricW" <em...@empty.com> ha scritto nel messaggio
news:e6iPWiie...@TK2MSFTNGP02.phx.gbl...

> Is there any other way to use CString in a DLL.

Would it be possible for you to *not* using CString at the DLL interface?
i.e. you can use CString inside the DLL, but you may consider using wchar_t*
at the DLL interface.

Giovanni


EricW

unread,
Feb 28, 2008, 2:28:21 PM2/28/08
to
I can certainly do that but I know there will come a time when I'll greatly
prefer having the ability to pass around CStrings so I was looking for a
generic solution.

Eric

"Giovanni Dicanio" <giovanni...@invalid.com> wrote in message
news:u07652je...@TK2MSFTNGP04.phx.gbl...

Doug Harrison [MVP]

unread,
Feb 28, 2008, 2:51:36 PM2/28/08
to

To begin, use the following exporting technique. Replace your direct use of
the __declspec with X_EXPORT, replacing "X" with the name of your DLL,
making sure it has a reasonable chance of being unique:

#ifdef COMPILING_X_DLL
#define X_EXPORT __declspec(dllexport)
#else
#define X_EXPORT __declspec(dllimport)
#endif

Then #define COMPILING_X_DLL only in that DLL's project. This method can be
used by multiple DLLs, and since the macro names are unique, they don't
conflict.

Also, CString has no virtual functions and thus is not a good candidate for
public derivation. Consider using plain old CString and extending its
interface through non-member functions.

Finally, if you want to stick with CStringWEx, try to reproduce the problem
in a minimal DLL consisting of two files, a.cpp and b.cpp, which you can
compile at the command line with:

cl -LD -MD -D_AFXDLL a.cpp b.cpp

If you don't discover the solution along the way, post the files here,
including the definition of CStringWEx.

--
Doug Harrison
Visual C++ MVP

EricW

unread,
Feb 28, 2008, 3:24:37 PM2/28/08
to
Hi Doug,
I went with the solution of deriving a new class from CString because the
MSDN article below suggested that is the best approach. It also seemed to
suggest the changing of atlstr.h/afxstr.h was required to make this work
completely which I'd greatly prefer not to do but without that you'll get
duplicate definitions. Also I did do something like you suggested in making
a macro for __declspec( ? ) originally I just left it out for time. Anyway,
I think I can live with the solution of modifying atlstr.h for now until
something better comes along.

Thanks,
EricW


http://msdn2.microsoft.com/en-us/library/ms174286(VS.80).aspx


"Doug Harrison [MVP]" <d...@mvps.org> wrote in message
news:u23es3leac5lr2l8p...@4ax.com...

Doug Harrison [MVP]

unread,
Feb 28, 2008, 5:15:17 PM2/28/08
to
On Thu, 28 Feb 2008 15:24:37 -0500, "EricW" <em...@empty.com> wrote:

>Hi Doug,
> I went with the solution of deriving a new class from CString because the
>MSDN article below suggested that is the best approach.

It isn't for the reason I gave.

>It also seemed to
>suggest the changing of atlstr.h/afxstr.h was required to make this work
>completely which I'd greatly prefer not to do but without that you'll get
>duplicate definitions.

You should have listened to your instinct. :)

>Also I did do something like you suggested in making
>a macro for __declspec( ? ) originally I just left it out for time. Anyway,
>I think I can live with the solution of modifying atlstr.h for now until
>something better comes along.

The only reason I would ever touch a standard header would be to fix a bug,
and even then, I'd look long and hard for a workaround that does not
require touching a standard header. It appears you're doing this not to fix
a bug, but to derive a new class, like it's a standard design technique;
it's not. Besides having to figure out these linker errors you're getting
now, you're creating a maintenance nightmare, because you're going to have
to alter this header every time you install VC, inspect it after applying
service packs, and so forth, and anyone you give your code to will have to
do the same thing. Then are the compatibility problems you may
inadvertently introduce.

>Thanks,
>EricW
>
>
>http://msdn2.microsoft.com/en-us/library/ms174286(VS.80).aspx

The only advice on that page that may be worth taking is, "For more
information on this issue, see the Knowledge Base article, "Linking Errors
When you Import CString-derived Classes" (Q309801)"

http://support.microsoft.com/kb/309801

At least it tells you to modify your stdafx.h file instead of a standard
header. Both articles use the wrong macro for selecting the __declspec
flavor, the MSDN article using AFX_EXT_CLASS, and the KB article _USRDLL;
see my previous message for the one correct method. Again, your best bet is
to forget about deriving from CString and extend its interface through
non-member functions.

--

Alexander Grigoriev

unread,
Feb 28, 2008, 10:53:12 PM2/28/08
to
In general, it's not a good idea to pass C++ objects across DLL boundaries.
You can only do it if you pass it as a pointer to an interface, and all
implementation, including its destructor, is in the DLL.

"EricW" <em...@empty.com> wrote in message
news:%23RaJfBk...@TK2MSFTNGP05.phx.gbl...

Giovanni Dicanio

unread,
Feb 29, 2008, 5:27:03 AM2/29/08
to

"Alexander Grigoriev" <al...@earthlink.net> ha scritto nel messaggio
news:ujJRUZoe...@TK2MSFTNGP06.phx.gbl...

> In general, it's not a good idea to pass C++ objects across DLL
> boundaries. You can only do it if you pass it as a pointer to an
> interface, and all implementation, including its destructor, is in the
> DLL.

I do agree.

Giovanni


EricW

unread,
Feb 29, 2008, 10:54:57 AM2/29/08
to
Hi All,
Thanks for the feedback. While I did get it to work (yes, by modifying
atlstr.h, ick) I'm going to back off of it for many of the reasons you
pointed out and try the stdafx.h approach. I've never changed a standard
header file to fix a problem and I don't want to start now. If you happen
to know anyone on the compiler team and can give them a nudge to smooth out
STL and DLL interaction that would be great,... yeah, I know I'm dreaming
there :).

Thanks again,
Eric

"Doug Harrison [MVP]" <d...@mvps.org> wrote in message

news:77aes3p18ggv0jqhp...@4ax.com...

Doug Harrison [MVP]

unread,
Feb 29, 2008, 12:51:36 PM2/29/08
to
On Fri, 29 Feb 2008 10:54:57 -0500, "EricW" <em...@empty.com> wrote:

>Hi All,
> Thanks for the feedback. While I did get it to work (yes, by modifying
>atlstr.h, ick) I'm going to back off of it for many of the reasons you
>pointed out and try the stdafx.h approach. I've never changed a standard
>header file to fix a problem and I don't want to start now. If you happen
>to know anyone on the compiler team and can give them a nudge to smooth out
>STL and DLL interaction that would be great,... yeah, I know I'm dreaming
>there :).
>
>Thanks again,
>Eric

There haven't been any problems with STL and DLLs since VS.NET 2002, or
even VC6 after applying the Dinkumware patches. I'm really perplexed by
this documentation you found. I would have thought that MFC would dllexport
the most frequently used specializations of CString on char and wchar_t
anyway. What happens if you do nothing at all to "fix" things?

It seems to me that the "auto-exporting" of template base classes
introduced in VC7 to avoid the usually pointless C4275 warning has caused
nothing but problems while "solving" problems that simply do not arise if
you follow these rules for sharing C++ classes and objects across module
boundaries:

1. Link all participating modules to the same CRT DLL. This way, they share
the same heap, file descriptors, and most other CRT state.

2. Treat it as equivalent to static linking WRT things like compiler
version, compiler options, compilation dependencies, and so forth.

3. Don't use templates that have static data without explicitly
instantiating and dllexporting the specializations you use.

Following these rules will allow you to compose programs from DLLs and use
the classes they define as real C++ classes instead of COM-like interfaces.

I don't see how the auto-exporting of template bases does anything useful,
because you can't control precisely which module exports them. I mean, you
can still easily end up with multiple copies of a template specialization
when more than one DLL uses it, which is not a problem if you follow the
rules above; in particular, auto-exporting doesn't solve (3). IIRC, the
real goal was to suppress C4275, but it's always been possible to suppress
C4275 with #pragma warning(disable), which is fine to use, along with
suppressing C4251, if you follow these rules.

0 new messages