Error when using dllimport in a DLL client

Howdi folks, I'm currently creating a DLL and the client which goes with it using the stock procedure mentioned at a lot of places on the internet. Basically, create a DLL project which actually defines a PROJECT_EXPORTS in the Project.h file. Something like this:

// Assume the name of the project is SanProj and the header file is SanProj.h
#ifdef SANPROJ_EXPORTS
    #define SANPROJ_API __declspec(dllexport)
#else
    #define SANPROJ_API __declspec(dllimport)
#endif

Now the normal way of using this header is to include this in all the headers of your API classes and using SANPROJ_EXPORTS for "exporting" declarations when in the DLL and "importing" declarations when used as a client. For e.g. let's say we have a header file with a currency class:

// currency.hpp
#include "SanProj.h"
#include <ostream>
#include <string>

namespace SanProj {

    class SANPROJ_API Currency {

    public:
        Currency();
        const std::string& name();
        const std::string& code();
        bool empty() const;

    protected:
        std::string name_;
        std::string code_;
    };

    SANPROJ_API bool operator==(const Currency&,
                    const Currency&);

    SANPROJ_API bool operator!=(const Currency&,
                    const Currency&);

    SANPROJ_API std::ostream& operator<<(std::ostream& out, Currency& c);
}

And another header file with specific currencies:

// allccy.hpp
namespace SanProj {

    class SANPROJ_API USDCurrency : public Currency {
    public:
        USDCurrency() {
            name_ = "American Dollar";
            code_ = "USD";
        }
    };


    class SANPROJ_API CADCurrency : public Currency {
    public:
        CADCurrency() {
            name_ = "Canadian Dollar";
            code_ = "CAD";
        }
    };

}

The above classes form the contract of the DLL project. Now let's look at the client project files, which is a single class with main function:

#include "currency.hpp"
#include "allccy.hpp"

#include <iostream>

using namespace SanProj;

int main(int argc, char* argv[])
{
    USDCurrency uccy;
    std::cout << uccy;
}

Assuming all referencing/settings are already done in the Visual Studio project, I get the following error when trying to compile the client:

1>testdll.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall SanProj::USDCurrency::~USDCurrency(void)" (__imp_??1USDCurrency@SanProj@@QAE@XZ)
1>testdll.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: __thiscall SanProj::USDCurrency::USDCurrency(void)" (__imp_??0USDCurrency@SanProj@@QAE@XZ)

Not surprisingly, this error goes away when I remove the dllimport part from the SanProj.h file and the executable is created.

My question is, what's the point of the IDE generated dllimport if we can't compile clients against the header? Is there a way I can continue to use the header with both dllimport and dllexports and remove the linker errors? Also, why is it trying to resolve the symbol which has dllimport from the LIB file?

TIA, /sasuke

EDIT: Linker Command used by VisualStudio; as you can see, it has the LIB file.

/OUT:"E:\vsprojects\SomeSln\Release\testdll.exe" /INCREMENTAL:NO /NOLOGO "E:\vsprojects\SomeSln\Release\SanProj.lib" "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /MANIFEST /ManifestFile:"Release\testdll.exe.intermediate.manifest" /ALLOWISOLATION /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"E:\vsprojects\SomeSln\Release\testdll.pdb" /SUBSYSTEM:CONSOLE /OPT:REF /OPT:ICF /PGD:"E:\vsprojects\SomeSln\Release\testdll.pgd" /LTCG /TLBID:1 /DYNAMICBASE /NXCOMPAT /MACHINE:X86 /ERRORREPORT:QUEUE

Answers


EDIT: Sure I'm wrong as the answer of jcopenha is the answer. The linker complains about the missing constructor and destructor that you don't export in the DLL. However the rest is still valid.

[...]

You should have two build targets (or projects, depending on the environment you use).

First target will build the DLL. The files this target will need to be built are, based on what you report:

currency.hpp
allccy.hpp

and probably the implementation of the base class "currency". You must have defined SANPROJ_EXPORTS in the preprocessor defines in order to use the currency.hpp file as a definition of the functions exported by your DLL. This target will produce a .DLL file and probably (depends on configuration) a .lib file. It may also generate other files like a text representation of the exports of the library (a .DEF file).

Then, you need to build your application (the second target/project): the header file you need is just simply the same of the library for the #include part. Just be sure to NOT define SANPROJ_EXPORTS or the compiler will try to export again the simbols instead of importing them. Then you need to add the following settings to the compiler and linker:

  • Add to the include path the directory containing the .hpp header.

  • Add to the libraries path of the linker (lib) the directory containing the .lib file.

  • Tell the linker to also link against the .lib you just created (add the full name of your lib file, assuming the DLL is named "currency" probably it will be "currency.lib".

Where and how to add this settings depends on the toolchain/environment and compiler you are using.

In the end, be sure that the compiled executable will be able to find the DLL in the project folder or in a system directory (in the PATH) or it will not start. Just copy the DLL with a post-build step if you have the executable in a different folder than the one used to build the DLL.

The fact that removing the _dllimport part will build the project is probably due to the compiler finding the header AND the implementation of the functions you meant to export, and building all of them statically into the executable.

Assuming you are not in the .NET "managed world", and that we are talking about Windows, there are some more points to look at if you want to distribute your library, but this is another answer.


Need Your Help

Adding a number to a phrase

ruby

I can print a raw number with this code:

Detect line/code of JAXB cyclic error

java jaxb jersey

I'm doing a project which uses JAX-RS and JPA, the entity classes were generated using Netbeans and because the classes are so big and generated cyclic errors happened.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.