【问题标题】:Can object pointers be passed between CLR and native C++?可以在 CLR 和本机 C++ 之间传递对象指针吗?
【发布时间】:2016-11-23 10:11:19
【问题描述】:

我一直在尝试将类指针从本机 DLL 传递给 CLR。我没有那么成功。我明白了,它使用 void * 返回指针,然后使用 IntrPtr 和 ToPointer() 将其转换为 ClassType *。但是在我尝试访问其成员方法的那一刻,它无法获取旧的属性值。我得到的是 0 和一些随机值。

我什至尝试使用 VC100 作为 CLR 和本机 dll 的工具集。我仍然得到值“读取字符串字符时出错”。

我尝试 google 并找不到太多关于将对象指针从本机传递到 CLR 的信息。我甚至提到了代码项目中可用的step-by-step

编辑添加:


我有一个本地 dll,其中有一个返回对象指针的函数

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..
}
...
}

稍后在托管代码 clr 中,我使用返回指针调用 memberFunct1。

[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(); 

我使用这个 obj1 指针来调用成员方法,但在成员方法中,成员值显示为错误的 ptr。


有人能指点我正确的方向吗?

提前致谢。


我在我的 Native dll 中添加了一个额外的方法。

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

并从我的 CLR 项目中调用它

[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));

但我收到链接错误?我已经链接了??!!??

错误 LNK2028: 未解析的令牌 (0A000356) "public: __thiscall Obj1::Obj1(int,char const *,char const *)" (??0Obj1@@$$FQAE@HPBD0@Z) 在函数 "int __cdecl main(void)" (?main@@$$HYAHXZ)

错误 LNK2028:在函数“int __cdecl main(void)" (?main@@$$HYAHXZ)

错误 LNK2019:在函数“int __cdecl main (void)" (?main@@$$HYAHXZ)

错误 LNK2019:函数“int __cdecl main( void)" (?main@@$$HYAHXZ)

【问题讨论】:

  • @Martin 抱歉,我已经编辑了我的帖子并添加了一些代码。
  • 这是一个沼泽标准dangling pointer bug。 C 或 C++ 程序中非常常见的错误,通常很难诊断。但是,当您使用 pinvoke 时,pinvoke 编组器所需的堆栈空间可确保obj 被可靠地覆盖。您必须将其存储在其他地方,最好的位置几乎总是调用者的堆栈帧。将返回类型更改为 void 并添加 Obj1* retval 参数。
  • @HansPassant 我也试过你的。但我仍然收到链接错误。我已经编辑了我的帖子以包含您提到的内容,并添加了显示链接的图片。
  • @ras - 从屏幕截图中 - 删除 ClrDLLWrapper 项目中的Native.dll
  • @MartinBa - 我已将其删除。还是有链接错误。

标签: visual-c++ dll c++-cli marshalling void-pointers


【解决方案1】:

您不要以这种方式使用 C++/CLI!

C++/CLI 的整个是充当 .NET 世界和本地世界之间的粘合剂。

你要做的是:

  • 在 C++/CLI 源文件中包含 SomeMethodclass Obj1 的 C++ 标头,并且
  • 让 C++/CLI 程序集引用 Native.dll 项目

然后,您可以像在 C++ 中一样简单地使用它 - convert any CLR types to native representation first(如果包含 MFC,则 System::String 可以通过 MFC CString 进行转换):

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

至于为什么会在调试器中看到垃圾:可能是因为你返回的是本地对象的地址

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
}

在这种情况下,一旦您从SomeMethod 返回,obj 将被清理(调用其 D'tor 并回收其堆栈内存)。

  • 要么按值返回对象
  • new 向上对象并返回指针。 (虽然这是有泄漏风险的代码)

【讨论】:

  • 我已经做到了。我已经包含了头文件并复制了 Native.dll 并将其添加为对我的 CLR 项目的引用。我实例化了它,我得到一个链接错误????!??? [error LNK2028: unresolved token (0A00041D) "public: __thiscall SomeDLLClass1::SomeDLLClass1(int,char const *,char const *)" (??0SomeDLLClass1@@$$FQAE@HPBD0@Z) 在函数 "int __clrcall main (cli::array^)" ]
  • 实例化行[Obj1 testPtr2 = Obj1(1, (char*)(void*)Marshal::StringToHGlobalAnsi(str1), (char*)(void*)Marshal::StringToHGlobalAnsi(str2) );]
  • @ras - 您不要复制 Native.dll 并将其添加为参考。您必须添加 Native.vcxproj 作为参考并激活Link Lib Dependencies == true。 - 或者,如果本机项目存在于其他解决方案中,您可以从本机项目链接到 .lib 文件。
  • 我已经通过 Project > 属性添加了引用,并将 Link Lib Dependencies 设置为 true。它上面显示一个感叹号,但仍然有相同的链接错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-03
  • 1970-01-01
  • 1970-01-01
  • 2010-10-26
  • 2019-11-13
  • 1970-01-01
相关资源
最近更新 更多