Click here to Skip to main content
Click here to Skip to main content

Linker Errors, CString, ATL, MFC, and YOU!

, 3 May 2003
Rate this:
Please Sign up or sign in to vote.
Linker errors due to CStringT template classes and ATL vs. MFC issues

Sample Image - maximum width is 600 pixels

Introduction

The Scenario: Make an ATL based class library that can be used from both an ATL based console application and an MFC/ATL based GUI application. The class library cannot contain any MFC links. The GUI application must use ATL. The console application may not use any MFC, or link to any library that does.

Ok, that sounds like a pretty easy order, but if your not used to playing with some linker settings, you'll get tripped up fast. You could always go the easy route by adding /FORCE:MULTIPLE in the linker command line options, but then every time you compile you will get treated to tons of linker warnings. This is not the way to the shining path! Our goal is to make a well behaved library that will link up without any special options specified in the consuming applications, other than importing the library, and including the header file.

Terms

I'm going to call any application that links to a library a consumer. Consumers will #include the library header, and then link to the library file in thier linker stage. In the sample, ConsoleApp, and Application are both consumers.

Know Thy Enemy (Your Linker Error Nemises)


Fig 0: Setting the configuration for building.

If you compile the sample project with the configuration setting set to "Errors Galore" (Fig 0), you will be treated to loads of nasty looking error messages. LNK2005, LNK2019, LNK4098, LNK1169, LNK1120

LNK2005 <symbol> already defined in <library> (<object generating the error>)
LNK2019 unresolved external symbol "<symbol signature>" referenced in function <function signature>
LNK4098 defaultlib '<library name>' conflicts with use of other libs; use /NODEFAULTLIB:library
LNK1169 one or more multiply defined symbols found
LNK1120 <X> unresolved externals
Table of possible linker messages from "Errors Galore" configuration.

Taking out the trash

Let's start with everyone's least favorite, the often seen LNK4098. This error hangs out with LNK2005. You can fix both by setting your linker options for the library to match the most restricted application that will use your library. In this case, the MFC GUI application demands that it be linked with the Multi-Threaded DLL. If you are compiling in a debug setting, you must link with Multi-Threaded Debug DLL (See Fig1). Once you set this option for your library and recompile it, you will see LNK4098 and LNK2005 beat a hasty retreat. Because you are also going to use this library with an ATL console application, you must set Runtime Library to Multi-Threaded DLL for console application as well. NOTE: If you have other libraries that you are using that expose function signatures similar to the ones in your library, then you may still get LNK2005 errors. Find the offending library, and remove it from your project. If it's a default library, and you are sure you won't need it, you can add it to the Ignore Default Library list (See Fig2).


Fig 1: Setting the Runtime Library to match the Runtime Library used by the consuming application. If you were setting this option in Release configuration, then you would select Multi-threaded DLL (/MD).

Fig 2: Sometimes when there's no other option, you can just ignore the library that the compiler is complaining about. This is not the way to walk the shining path.

Now, you can try to recompile the sample in the " Less Errors " configuration, and see that we have successfully banished LNK4098 and LNK2005. You will immediately notice that we still haven't gotten rid of an LNK2019 and LNK1120 error. What is really curious about this is that the ConsoleApp linked just fine to the library, and the call from ConsoleApp to the library is the same as the call from Application to the library. What's happening here is under the hood in atlstr.h, the header that contains the ATL definition for CString. The definition for CString in VC++ 7 is radically different from the VC++ 6 one in that it is a template class, where the VC++ 6 implementation is just a class. The problem is that MFC's CString default template is different than the one that ATL is using. What we need to do is to get both the library and the consumer application to use the same signature for the linker. Then, the linker can find the signature in the library at link time, and you won't get those nasty LNK2019 and LNK1120 errors.

MFC defines CString as:

typedef ATL::CStringT< TCHAR, StrTraitMFC< TCHAR > > CString;

While ATL defines CString as (With _ATL_CSTRING_NO_CRT defined.):

typedef CStringT< TCHAR, StrTraitATL< TCHAR > > CString;

This difference in the StrTrait is where all the problems with LNK2019 and LNK1120 come from.

The solution I'm going to use came from this MSDN article, which shows a similar tactic, but unfortunately the method on MSDN makes the library link to MFC, and we don't want that for our ATL library. What I am going to do instead is to change the function in the library to use CAtlString instead of CString in the method calls. If you look at the definition for CAtlString, you will notice that it is just a #define for CStringT, with a specific template. Because CAtlString explicitly states the signature for the parameter, this enables the linker to find the function in the library because it is looking for a function with parameters of type CStringT< TCHAR, StrTraitATL< TCHAR > >.

Also, I am including atlstr.h in the library's header file. This way, when the header file is included in a consumer application, CAtlString will be defined for the header file, and you won't get undefined symbols linker errors for CAtlString. This also ensures that the consumer application doesn't need any funky configuration changes; just include and go! To see the application link up correctly, change the configuration to "Debug" or "Release" and compile it.

On with the code

Because there are 3 individual projects in the attached sample and they are mostly in default state, I am not going to cover them in any great detail. Let's hit the highlights, and see the source code for other miscellenous comments.

#pragma once

// Include this here, so that any consumer code will automatically import 
// atlstr so that StrTraitATL will be defined for this header.
#include <ATLSTR.H>

class CHelloLib
{
public:
    CHelloLib(void);
    ~CHelloLib(void);

#ifndef _ERRORS_GALORE
    // By specifying CAtlString as the parameter token, the linker will 
        // know to use the right ATL version of CStringT, and thus find this 
        // function's signature. 
    void HelloWorld(CAtlString message);
#else
    // Cause LNK2019 by letting the linker try to guess which CStringT 
        // template to use to resolve this function signature. The linker 
        // will try to select the MFC version by default, and not find this 
        // function in the library. This causes the LNK2019.
    void HelloWorld(CString message);
#endif
};

Above, we see the source for the library include file. When included in a consumer application, this file will include atlstr.h, so that CAtlString is defined.

// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once#define WIN32_LEAN_AND_MEAN //Exclude rarely-used stuff from 
                                        // Windows headers
// TODO: reference additional headers your program requires here

#include <atlbase.h>
#include <atlstr.h>
// Define CAtlString as CString so that the IDE will show the intellesense 
// for CString instead of showing nothing for CAtlString. CAtlString will 
// still be used in library generation so that the right linking can be done.
#define CAtlString CString

Here is the stdafx.h for the Library. You should note at the bottom that I am defining CAtlString to a CString. This may seem counter productive, as in the HelloLib.h header, I just changed all the CString's to CAtlString's! However, without this #define, the intellisense doesn't seem to want to display the quick pick function list that I love so much. Thus, by casting this in stdafx.h for the library (and only the library), intellesense is enabled. When the library compiles, the compiles automatically selects the proper CStringT definition for ATL, as this is an ATL based project. I placed this #define in stdafx.h so that it would not be included in the library header file that will be used by a consumer. This way, the CAtlString declarations are in full effect when the consumer includes the library.

// ConsoleApp.cpp : Defines the entry point for the console application.
//

#ifdef _DEBUG
    #pragma comment(lib, "..\\Library\\Debug\\Library.lib")
#else
    #pragma comment(lib, "..\\Library\\Release\\Library.lib")
#endif 

#include "stdafx.h"
#include "..\Library\HelloLib.h"

int _tmain(int argc, _TCHAR* argv[])
{
    printf("Calling into library.\n");

    CHelloLib hw;
    hw.HelloWorld("I was called from a console application.");

    return 0;
}

Above, the code for ConsoleApp.cpp, shows you how to select between a debug edition of an included library, and a release version of an included library. I found this to be a quite handy trick, as it instructs the linker to bring in a library without having to adjust the project settings at all! The Application demo also uses the same trick, so I won't bother to put it in here.

Points of Interest

I think that it's kind of an issue with ATL/MFC as they are technologies that can be mixed together, they should also be able to work around each other. The above solution is rather hacky, even tho it gets the job done simply. I would rather have ATL or MFC check to see if one or the other is included, and use the proper signature, but maybe that could be a goal for VC++ 8...

History

4/11/2003 - Initial creation / Shining path walk.

4/15/2003 - Not as smart as I thought I was / Path suddenly went dim... Had to change several things to keep from redefining CString in MFC applications when importing the library headers. Path is now back in view.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Nick Pirocanac
Web Developer
United States United States
I have been programming for about 15 years, starting off with simple BASIC on the venerable C=64 and progressing all the way to C#.
 
I am currently for hire and contracting projects, so if you have opportunities in the Mid-West area, feel free to contact me! My strengths include C#/ASP.NET, MS-SQL Server, Client/Server/Distributed Architectures, C++/ATL7/MFC, and JAVA (Servlets/J2ME).
 
I am currently developing a live ranking system for Halo at www.haloranking.com, stop by and check it out!

Comments and Discussions

 
GeneralMy vote of 5 Pinmembermanoj kumar choubey12-Feb-12 21:16 
GeneralATL MFC PinmemberAdeel Mirza16-Feb-10 0:52 
GeneralFor VS 2003 only needed to change Code Generation setting Pinmemberbscaer29-Jun-07 12:16 
QuestionWhat about LNK4217? PinmemberEricFowler27-Jun-06 17:53 
I am getting this error building a project with MFC/ATL 80 from the command line. Here is the error:
 
Linking Executable - objchk\i386\injector.exe
errors in directory c:\depots\magellan\branches\a-ericfo2\main\magellan\shell\injector
c:\depots\magellan\branches\a-ericfo2\main\magellan\shell\injector\mfcs80d.lib(stdafx.obj) : error LNK4217: locally defined symbol _wcscpy_
imported in function "void __cdecl ATL::Checked::wcscpy_s(wchar_t *,unsigned int,wchar_t const *)" (?wcscpy_s@Checked@ATL@@YAXPA_WIPB_W@Z)
 
I am not defining this symbol AFAIK. If I go into ATL::Checked::wcscpy_s and comment out totally the call to wcscpy_s() it gives me the same error on another CRT function, IIRC it was calloc(), but it doesn't much matter.
 
Anyone got any clues? I could use one or three. Confused | :confused:
 
Eric
GeneralFILETIME error PinmemberArunAntony20-Mar-06 20:58 
GeneralRe: FILETIME error PinmemberNick Pirocanac22-Mar-06 18:20 
GeneralLinker problem for porting ATL server from VC++6 to VC++7 Pinmembersubash110727-May-05 4:20 
GeneralRe: Linker problem for porting ATL server from VC++6 to VC++7 PinsussAnonymous16-Jun-05 23:03 
GeneralThanks for taking the time PinmemberCheeran Santhosh10-Dec-04 9:35 
Generalatlstr.h Pinsussb33rc0der17-Jun-04 13:22 
GeneralLNK2019 keeps alive with UNICODE PinmemberFPF31-Jul-03 21:17 
GeneralRe: LNK2019 keeps alive with UNICODE PinmemberPeter Weyzen8-Sep-03 10:34 
GeneralRe: LNK2019 keeps alive with UNICODE PinmemberFPF8-Sep-03 21:15 
GeneralRe: LNK2019 keeps alive with UNICODE PinmemberSami.Deen15-Nov-07 14:39 
GeneralLNK2005 error PinsussAnonymous10-Jul-03 4:12 
GeneralATL with MFC support - LNK2005 problem persists PinsussAnonymous17-Jun-03 0:02 
GeneralRe: ATL with MFC support - LNK2005 problem persists PinmemberNick Pirocanac17-Jun-03 6:39 
GeneralHello Mr. Nick Pirocanac Pinmemberaleilee@126.com17-Mar-04 0:12 
GeneralUseful and broad PinmemberBartosz Bien4-May-03 6:23 
GeneralRe: Useful and broad PinmemberJohn R. Shaw4-May-03 8:22 
Generalnot .NET related.... Pinmemberleppie3-May-03 23:58 
GeneralRe: not .NET related.... PinmemberRodrigo Strauss4-May-03 6:45 
GeneralRe: not .NET related.... PinmemberJames T. Johnson4-May-03 19:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.141022.2 | Last Updated 4 May 2003
Article Copyright 2003 by Nick Pirocanac
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid