0
votes

I have been trying to pass a class pointer from a native DLL to CLR. I am not so successful with it. I get to the point it returns the pointer using void * and later converting it to ClassType * using IntrPtr and ToPointer(). But the minute I try to access its member methods, its not able to get the old property values. I get instead 0 and some random values.

I have even tried to use VC100 as the toolset for both the CLR and native dll. Still I get the value "Error reading characters of string".

I tried to google and can't find much info on passing object pointers from native to CLR. I have referred to even the step-by-step available in code project.

Edited to add this:


I have a native dll where a function that returns object pointer

Native.dll:

#if defined EXPORTDLL // inside DLL
#define DLL_EXPORT __declspec(dllexport)
#else // outside DLL
#define DLL_EXPORT __declspec(dllimport)
#endif

.....

DLL_EXPORT void*  SomeMethod(uint16_t num1, const char * str1, const char * str2)
{
  Obj1 obj(...);
  ...
  return (void*)&obj; // <- at this point it is still correct
}

....
class DLL_EXPORT Obj1{
....
const std::string var1;
const std::string var2;
const std::string var3;
....
DLL_EXPORT strct1 memberFunction1()
{
   // do something with the variables
   // here when its called by managed code, the var1, var2, var3 shows random values and bad ptr..
}
...
}

And later in managed code clr I call the memberFunct1 using the return pointer.

[DllImport("Native.dll", EntryPoint = "?SomeMethod@@YAPAVstruct1@someSpace@@GV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z", CallingConvention = CallingConvention::Cdecl)]
IntPtr * SomeMethod(uint16_t num1, [In, MarshalAs(UnmanagedType::LPStr)]String^  str1, [In, MarshalAs(UnmanagedType::LPStr)] String^ str2);
.....

String^ str1 = gcnew String("1223568");
String^ str2 = gcnew String("1.2.3.5");
Obj1 *objptr = (Obj1*)(SomeMethod(1,str1, str2)).ToPointer();
// <- at this point the objptr properties are showing garbage
objptr->memberFunction1(); 

I use this obj1 pointer to call a member method but in the member method the member values are showing as bad ptr.


Can someone point me in the right direction please?

Thanks in advance.


I have added an additional method in my Native dll.

void DLL_EXPORT SomeMethod2(int i1, const char* var1, const char* var2, Obj1* retval); 

And called it from my CLR project

[DllImport("Native.dll", EntryPoint = "?SomeMethod2@@YAXHPBD0PAVObj1@@@Z", CallingConvention = CallingConvention::Cdecl)]
void SomeMethod2(int i1, [MarshalAs(UnmanagedType::LPStr)]String^ var1, [MarshalAs(UnmanagedType::LPStr)]String^  var2, Obj1* retval);
....
Obj1 * testPtr3;
SomeMethod2(1, (char*)(void*)Marshal::StringToHGlobalAnsi(str1), (char*)(void*)Marshal::StringToHGlobalAnsi(str2), testPtr3);
SomeMethod2(1, str1, str2, testPtr3);

Obj1  testPtr2 = Obj1(1, (char*)(void*)Marshal::StringToHGlobalAnsi(str1), (char*)(void*)Marshal::StringToHGlobalAnsi(str2));

But I get link errors? I have already linked it??!!??

error LNK2028: unresolved token (0A000356) "public: __thiscall Obj1::Obj1(int,char const *,char const *)" (??0Obj1@@$$FQAE@HPBD0@Z) referenced in function "int __cdecl main(void)" (?main@@$$HYAHXZ)

error LNK2028: unresolved token (0A00035B) "void __cdecl SomeMethod2(int,char const *,char const *,class Obj1 *)" (?SomeMethod2@@$$FYAXHPBD0PAVObj1@@@Z) referenced in function "int __cdecl main(void)" (?main@@$$HYAHXZ)

error LNK2019: unresolved external symbol "public: __thiscall Obj1::Obj1(int,char const *,char const *)" (??0Obj1@@$$FQAE@HPBD0@Z) referenced in function "int __cdecl main(void)" (?main@@$$HYAHXZ)

error LNK2019: unresolved external symbol "void __cdecl SomeMethod2(int,char const *,char const *,class Obj1 *)" (?SomeMethod2@@$$FYAXHPBD0PAVObj1@@@Z) referenced in function "int __cdecl main(void)" (?main@@$$HYAHXZ)

enter image description here

1
@Martin Sorry, I have edited my post and added some codes.razzy
This is a bog standard dangling pointer bug. A very common bug in a C or C++ program, usually very hard to diagnose. But not when you use pinvoke, the stack space required by the pinvoke marshaller ensures that obj gets overwritten reliably. You have to store it elsewhere, the best place is almost always the stack frame of the caller. Change the return type to void and add an Obj1* retval parameter.Hans Passant
@HansPassant I tried yours too. But I still get Link error. I have edited my post to include what you have mentioned and added an image showing the link.razzy
@ras - from the screenshot - remove the Native.dll from the ClrDLLWrapper projectMartin Ba
@MartinBa - I have removed it. Still have the link error.razzy

1 Answers

1
votes

You do not use C++/CLI in this way!

The whole point of C++/CLI is to act as a glue between the .NET world and the native world.

What you do instead is to:

  • Include the C++ headers for SomeMethod and class Obj1 in the C++/CLI source file and
  • have the C++/CLI assembly reference the Native.dll project

Then, you can simply use it as you would normally in C++ - convert any CLR types to native representation first (System::String can be converted by MFC CString if you include MFC):

String^ str1 = gcnew String("1223568");
String^ str2 = gcnew String("1.2.3.5");
Obj1 *objptr = SomeMethod(1, CString(str1), CString(str2));

As for why you see garbage in the debugger: It is probably because you return the address of a local object:

DLL_EXPORT void*  SomeMethod(uint16_t num1, const char * str1, const char * str2)
{
  Obj1 obj(...);
  ...
  return (void*)&obj; // <- at this point it is still correct
}

In this case, objwill be cleaned up (its D'tor invoked and its stack memory recycled) as soon as you return from SomeMethod.

  • Either return the object by value
  • or new up the object and return the pointer. (though that is leak-risking code)