0
votes

This is probably just caused by me lacking c++/cli knowledge, but I can't seem to find this question asked anywhere.

I have a project called ManagedProject that is compiled with /CLR and has a (c++/cli) ref class called RefClass.cpp, and a native C++ class called CppClass.cpp.

I am able to call the constructor of CppClass from RefClass.

However, I am unable to call CppClass from a separate project "OtherProject" that is also compiled with /clr. OtherProject only has Other.cpp. OtherProject has a Reference to ManagedProject so I am able to call RefClass, but even though I am able to #include "CppClass.h", I get LNK2019 AND LNK2028 errors when I try to call CppClass's constructor.

I get the same errors when calling it from a non-/clr native class.


Here is a code sample:

ManagedProject

RefClass.cpp:

// has a .h file with the constructor declaration & instance variable int test;
#include "CppClass.cpp"
RefClass:RefClass(int test){
    this->test = test;
    CppClass inst(42); //This works
}

CppClass.cpp:

// has a .h file with the constructor declaration & instance variable int test2;
CppClass:CppClass(int test2){
    this->test2 = test2;
}

OtherProject

Other.cpp:

#include "CppClass.cpp"
int wmain(/*args*/){
    RefClass^ refinst = gcnew RefClass(64); //This works    
    CppClass inst(42); //This fails, I get LNK2019 & LNK2028 at Other.obj
}

Actual error output: Note: here SQLPrecheckReport is CppClass, and appzsqlmigrate.cpp is Other.cpp, appzsqlmigrate is the name of OtherProject.

32>appzsqlmigrate.obj : error LNK2028: unresolved token (0A000B59) "public: __cdecl SQLPrecheckReport::SQLPrecheckReport(int)" (??0SQLPrecheckReport@@$$FQEAA@H@Z) referenced in function "int __cdecl wmain(int,wchar_t * * const)" (?wmain@@$$HYAHHQEAPEA_W@Z)

32>appzsqlmigrate.obj : error LNK2028: unresolved token (0A000B5A) "public: __cdecl SQLPrecheckReport::~SQLPrecheckReport(void)" (??1SQLPrecheckReport@@$$FQEAA@XZ) referenced in function "int __cdecl wmain(int,wchar_t * * const)" (?wmain@@$$HYAHHQEAPEA_W@Z)

32>appzsqlmigrate.obj : error LNK2019: unresolved external symbol "public: __cdecl SQLPrecheckReport::SQLPrecheckReport(int)" (??0SQLPrecheckReport@@$$FQEAA@H@Z) referenced in function "int __cdecl wmain(int,wchar_t * * const)" (?wmain@@$$HYAHHQEAPEA_W@Z)

32>appzsqlmigrate.obj : error LNK2019: unresolved external symbol "public: __cdecl SQLPrecheckReport::~SQLPrecheckReport(void)" (??1SQLPrecheckReport@@$$FQEAA@XZ) referenced in function "int __cdecl wmain(int,wchar_t * * const)" (?wmain@@$$HYAHHQEAPEA_W@Z)

1
The CLR provides excellent support for modules but it doesn't retroactively add that support to unmanaged code as well. You have to do the normal C++ dance here and put the C++ class in a plain static library or DLL project and link their .libs so the native code can be shared.Hans Passant
@HansPassant , I see now that the issue doesn't have to do with c++/cli. Sorry if this seems obvious, I am completely new to using dlls and libraries. Can you give me instructions to do this: "put the C++ class in a plain static library or DLL project and link their .libs so the native code can be shared"? What I have tried so far didn't resolve the errors...Dominic Roy-Stang

1 Answers

2
votes

C++/CLI allows managed and unmanaged code to live together in the same DLL, but they each still use the linking mechanisms that they use normally.

Managed code is linked by having the managed class marked as public, and loading the assembly as a reference. Unmanaged code is linked by exporting functions by specifying __declspec(dllexport) in the exporting DLL and __declspec(dllimport) in the importing DLL/EXE.

If you want to export this as a C++ class, you'll need to #include "CppClass.h", which it looks like you're already doing, based on your error messages. You'll need to specify those declspec attributes; the usual way to do that is to have a preprocessor symbol defined in the project settings for ManagedProject but not for OtherProject, and key off of that to #define something as either __declspec(dllexport) or __declspec(dllimport).

An alternative would be to wrap the unmanaged class in a managed class, and use the .Net mechanisms to deal with linking. This would make the linking simpler, and it's very much what C++/CLI was designed for (though normally the wrapper would be consumed by C#, rather than more C++/CLI).