C++ cli 在C++/CLI包装器-块\u类型\u有效,\u CrtIsValidHeapPointer
我是C++/CLI新手,但多年来一直在编写托管代码。。。显然太多年了 试图为第三方提供的非托管类编写包装,我看到了一些奇怪的东西。我希望你们能帮我弄清楚什么是我的无趣,什么是真正的奇怪 CLI包装器:C++ cli 在C++/CLI包装器-块\u类型\u有效,\u CrtIsValidHeapPointer,c++-cli,marshalling,unmanaged,wrapper,managed,C++ Cli,Marshalling,Unmanaged,Wrapper,Managed,我是C++/CLI新手,但多年来一直在编写托管代码。。。显然太多年了 试图为第三方提供的非托管类编写包装,我看到了一些奇怪的东西。我希望你们能帮我弄清楚什么是我的无趣,什么是真正的奇怪 CLI包装器: public ref class Wrapper { public: Wrapper(const float* flts, unsigned int fltlen, int offset) { _unmanagedClass = new UnmanagedClass
public ref class Wrapper
{
public:
Wrapper(const float* flts, unsigned int fltlen, int offset)
{
_unmanagedClass = new UnmanagedClass(flts, fltlen, offset);
}
~Wrapper()
{
delete _unmanagedClass;
}
String^ getSomeString()
{
string x = _unmanagedClass->getSomeString(); //1
String^ ret = gcnew String(x.c_str()); //2
return ret; //3
}
private:
UnmanagedClass* _unmanagedClass;
};
我还应该注意,我在标题中有这些指令
#pragma managed(push, off)
#include "Unmanaged.h"
#pragma comment(lib, "lib\\Unmanaged_dll.lib")
#pragma managed(pop)
这里是非托管的
class UNMANGED_API UnmanagedClass
{
public:
UnmanagedClass(const float* flts, uint fltlen, int offset);
string getSomeString() { return _someString; }
private:
string _someString;
};
这些都是编译的。然后,缺乏经验的感觉就开始了
在调试配置中调试时,UnmanagedClass::getSomeString()
似乎正在返回合理/预期的字符串值。我可以通过在//2
上设置断点并查看x
的值来看到这一点。如果我转到//3
,我可以看到ret
的值为x
。但是,如果我试图跳出/越过//3
,我会得到两个失败的断言(块类型\u是有效的和\u CrtIsValidHeapPointer
),调试器会暂停,永远不会返回托管实现
在版本配置中调试时,我不会得到失败的断言,我会返回到托管实现,但是getSomeString()
返回的字符串值在我看到它的地方都是垃圾<代码>//2
,//3
以及托管实现中
我用几种不同的方法修改了代码,但都没有用
我认为有一些Mashalling需要围绕//2
来做,但我还没有找到任何真正的东西,比如如何将基本字符串
打包成系统::字符串^
,或者如果需要的话。如果是这样的话,我们将非常感谢您对显式语法的帮助
我还通过返回return“”,将生成失败断言的调用范围缩小到//1
//3
。这些断言指向试图修改/删除当前运行时访问的堆上不存在的内存。这是否与需要整理UnmangedClass::getSomeString()的返回值有关
希望我只是缺少一些简单的概念,第三方代码没有问题。请让我知道,如果我能提供任何更多的细节,并为我几乎完全不知道所有语言的祖父道歉
提前感谢您提供的任何信息或“提示”
编辑:添加C#托管客户端实现
public unsafe string GetString(List<float> flts )
{
float[] fltArr = flts.ToArray();
Wrapper wrap;
fixed (float* ptrFlts = fltArr)
{
wrap = new Wrapper(ptrFlts , fltArr.Length, 0);
}
var x = wrap.getSomeString();
return x.ToString();
}
公共不安全字符串GetString(列表flts)
{
float[]fltArr=flts.ToArray();
包装纸;
固定(浮动*ptrFlts=fltArr)
{
包装=新包装(ptrFlts,fltArr.长度,0);
}
var x=wrap.getSomeString();
返回x.ToString();
}
编辑:添加Unmanged.dll的Dumpbin.exe签名!UnmangedClass::getSomeString()
(public:class std::basic_string
\u thiscall Codegen::getSomeString(void))
您将本机字符串转换为托管字符串还不错。此示例介绍了如何在Microsoft平台上提供的所有不同字符串类型之间进行转换:
话虽如此,我接受了你的代码,并编译了它,我不能让任何东西失败。当然,我必须想出自己初始化非托管类的方法::\u someString,我就是这样做的:
UnmanagedClass::UnmanagedClass(const float* /*flts*/, unsigned int /*fltlen*/, int /*offset*/)
{
_someString = "A few of my favorite things";
}
当我这么做的时候,我通过了这个代码:
#include "stdafx.h"
#include "Wrapper.h"
int _tmain(int argc, _TCHAR* argv[])
{
Wrapper^ w = gcnew Wrapper(NULL, 0, 0);
System::String^ s = w->getSomeString();
return 0;
}
效果很好。
以下是我所做的其他工作:
// UnmanagedClass.h
#pragma once
#pragma unmanaged
#include <vector>
class UnmanagedClass
{
public:
UnmanagedClass(const float* flts, unsigned int fltlen, int offset);
std::string getSomeString() { return _someString; }
private:
std::string _someString;
};
我希望这能有所帮助。这个问题与.NET或C++/CLI无关,问题完全在本机代码中
您违反了std::string
的一个定义规则,如果您的定义与Unmanaged_dll.dll
使用的内容不完全匹配,那么一切都会失控。听起来好像调试定义/类布局使用了该DLL。谢谢您的回复。我是否正确地理解了std:string的定义在调试和发布模式之间的变化?我编辑了这篇主要文章,以提供Unmanaged::getSomeString的dumpbin签名。有什么方法可以更接近他们的字符串类型吗?如果我要按原样使用本机代码,是否需要始终在调试配置中运行包装器?@Josh:您是否有非托管dll.dll
的代码?如果是这样,“正确”的解决方案是使用不同的名称进行调试和发布配置,就像Microsoft CRT一样(例如MSVCR90.dll与MSVCR90D.dll),然后使用#If NDEBUG
和#pragma comment(lib)
选择正确的名称。如果您不能构建DLL的发布版本,那么您也不能对代码使用发布CRT。我建议不要跨DLL边界共享类的原因之一。@Josh:非托管类的布局本身也可能在更改,因此DLL存储的位置\u someString
不是编译器查找它的地方。@Ben,再次感谢您的快速回复!我无法访问源代码,但我可以访问作者。就这样,我的鸭子排成一行,这是否像让作者同时提供调试和发布位那样简单?我还将提到,我正在使用lib链接,而不是DLLImport。。。不幸的是,我不知道这是否重要…@Josh:如果两个模块对标准库使用相同的精确布局,它就可以正常工作。。。这意味着编译器版本(包括服务包)、调试与发布、SCL迭代器调试等。堆必须共享,因此您还需要使用CRT的DLL版本。当您想要转移到下一个伟大的新编译器版本时,这肯定会带来麻烦。不那么脆弱的修复方法是为您提供一个缓冲区作为char*
,并让他将字符串复制到其中(可能需要第二个函数来获取
// UnmanagedClass.cpp
#include "UnmanagedClass.h"
#include <tchar.h>
UnmanagedClass::UnmanagedClass(const float* /*flts*/, unsigned int /*fltlen*/, int /*offset*/)
{
_someString = "A few of my favorite things";
}
// wrapper.h
#pragma once
#pragma unmanaged
#include "UnmanagedClass.h"
#pragma managed
public ref class Wrapper
{
public:
Wrapper(const float* flts, unsigned int fltlen, int offset)
{
_unmanagedClass = new UnmanagedClass(flts, fltlen, offset);
}
~Wrapper()
{
delete _unmanagedClass;
}
System::String^ getSomeString()
{
std::string x = _unmanagedClass->getSomeString(); //1
System::String^ ret = gcnew System::String(x.c_str()); //2
return ret; //3
}
private:
UnmanagedClass* _unmanagedClass;
};