C++ cli 在C++/CLI包装器-块\u类型\u有效,\u CrtIsValidHeapPointer

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

我是C++/CLI新手,但多年来一直在编写托管代码。。。显然太多年了

试图为第三方提供的非托管类编写包装,我看到了一些奇怪的东西。我希望你们能帮我弄清楚什么是我的无趣,什么是真正的奇怪

CLI包装器:

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