In one of my C++/CLI projects, I need to use a class exported from a unmanaged DLL. When I build the C++/CLI project, I get two linking errors: LNK2028 and LNK2029. The linker cannot find the GetObject() method of a class.
First, I made sure that the export library of the unmanaged DLL was linked with the managed DLL.
Then I used the dumpbin tool to dump the exports of the unamanged DLL, I found that the GetObject() method was renamed as GetObjectW(), and the linker tries to look for GetObject() when linking the managed project, hence the errors.
We know that Microsoft implemented two sets of Win32 functions, one set for ANSI with the function names postfixed with A, one set for Unicode with function names postfixed with W. We call the functions without specifying which version we want to use, the compiler will figure it out depending on whether the UNICODE macro is defined. Here is an example (copied from wingdi.h)
WINGDIAPI int WINAPI GetObjectA(__in HANDLE h, __in int c,
__out_bcount_opt(c) LPVOID pv);
WINGDIAPI int WINAPI GetObjectW(__in HANDLE h, __in int c,
__out_bcount_opt(c) LPVOID pv);
#ifdef UNICODE
#define GetObject GetObjectW
#else
#define GetObject GetObjectA
#endif // !UNICODE
#if defined(_M_CEE)
#undef GetObject
__inline
int
GetObject(
HANDLE h,
int c,
LPVOID pv
)
{
#ifdef UNICODE
return GetObjectW(
#else
return GetObjectA(
#endif
h,
c,
pv
);
}
#endif /* _M_CEE */
If the UNICODE macro is defined, GetObject will be replaced with GetObjectW regardless whether it is used as a member method name or a function name. That is why the GetObject() method gets renamed as GetObjectW.
However, when compiling a C++/CLI project, the _M_CEE macro is defined, causing the GetObject macro to be undefined, and an inline function GetObject() defined. And that is why the linker looks for GetObject() instead of GetObjectW().
There isn’t a clean solution when you have a method whose name conflicts with a Win32 function. You can use the push_macro/pop_macro directives to temporarily undefine the macro defined for the Win32 function, to prevent the method name from being changed:
class __declspec (dllexport) UnmanagedClass
{
public:
#pragma push_macro("GetObject")
#undef GetObject
std::wstring GetObject();
#pragma pop_macro("GetObject")
void CallGetObject();
};
The problem is whenever you want to call this method in a unmanaged project, you have to undefine the macro, making the code quite messy.
To avoid these kind of linking errors in C++/CLI projects, 1) make sure the export library of unmanaged DLLs are added to the C++/CLI projects, 2) avoid naming a method with the same name of a Win32 system function.
Here is the code that I used to reproduce the issue.
1. The header file of the unamanged class.
#pragma once
#include "string"
class __declspec (dllexport) UnmanagedClass
{
public:
#pragma push_macro("GetObject")
#undef GetObject
std::wstring GetObject();
#pragma pop_macro("GetObject")
void CallGetObject();
};
2. The cpp file of the unmanaged class.
#include "stdafx.h"
#include "UnmanagedClass.h"
#pragma push_macro("GetObject")
#undef GetObject
std::wstring UnmanagedClass::GetObject()
{
return L"GetObject() is called";
}
void UnmanagedClass::CallGetObject()
{
// The GetObject macro must remain to be undefined here.
this->GetObject();
}
#pragma pop_macro("GetObject")
3. The cpp file in the managed project that calls the UnmangedClass::GetObject() method.
UnmanagedClass uc; std::wstring str = uc.GetObject(); String^ mstr = System::Runtime::InteropServices::Marshal::PtrToStringAuto( System::IntPtr::IntPtr((void*)str.c_str())); Console::WriteLine(mstr);
Leave a comment