处理 C++/CLI 包装器中的非托管字符串 - BLOCK_TYPE_IS_VALID、_CrtIsValidHeapPointer

发布于 2024-10-31 16:41:11 字数 2649 浏览 1 评论 0 原文

我是 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)

这是 Unmanagement.h;

class UNMANGED_API UnmanagedClass
{
public:
    UnmanagedClass(const float* flts, uint fltlen, int offset);
    string getSomeString() { return _someString; }

private:
    string _someString;
};

这一切都可以编译。然后,奇怪/缺乏经验就开始出现。

在 DEBUG 配置中调试此问题时,UnmanagedClass::getSomeString() 似乎返回一个合理/预期的字符串值。我可以通过在 //2 上设置断点并查看 x 的值来看到这一点。如果我进入 //3,我可以看到 ret 的值为 x。但是,如果我尝试跳出/越过 //3,我会收到几个失败的断言(BLOCK_TYPE_IS_VALID_CrtIsValidHeapPointer)和调试器停滞不前,永远不会返回到托管实现。

在 RELEASE 配置中调试此问题时,我没有得到失败的断言,并且返回到我的托管实现,但是 getSomeString() 返回的字符串值在我查看的任何地方都是垃圾; //2//3 以及托管实现中。

我用几种不同的方式修改了代码,但没有效果。

我认为需要围绕 //2 完成一些编组工作,但就如何编组 basic_string< 而言,我还没有找到任何真正切中要害的东西/code> 到 System::String^,或者如果需要的话。如果是这样,那么我们将非常感谢有关显式语法的一些帮助。

我还通过返回 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();
}

编辑:添加 Unmanged.dll 的 Dumpbin.exe 签名!UnmangedClass::getSomeString()

(public: class std::basic_string,class std::allocator< ;char> > __thiscall Codegen::getSomeString(void))

I'm new to C++/CLI but have been coding managed code for many years... apparently too many years. :)

Attempting to write a wrapper for an unmanaged class provided by a 3rd party, and I'm seeing some strange stuff. I'm hoping you all can help me out weed what what is my noobishness and what is actually strange.

CLI Wrapper:

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

I should also note that I have these directives in the header;

#pragma managed(push, off)
#include "Unmanaged.h"
#pragma comment(lib, "lib\\Unmanaged_dll.lib")
#pragma managed(pop)

Here's Unmanaged.h;

class UNMANGED_API UnmanagedClass
{
public:
    UnmanagedClass(const float* flts, uint fltlen, int offset);
    string getSomeString() { return _someString; }

private:
    string _someString;
};

This all compiles. Then the strangness/lack of experience kicks in.

When debugging this in DEBUG configuration, UnmanagedClass::getSomeString() appears to be returning a resonable/expected string value. I can see this by setting a breakpoint on //2 and peeking the value of x. If I step to //3, I can see that ret has the value of x. However, if I attempt to step out/over //3, I get a couple of failed assertions (BLOCK_TYPE_IS_VALID and _CrtIsValidHeapPointer) and the debugger stalls, never returning to the managed implementation.

When debugging this in RELEASE configuration, I don't get the failed assertions and I return to my managed implementation, but the string value returned by getSomeString() is garbage where ever I peek it; //2, //3 as well as in the managed implementation.

I've massaged the code in a few different ways to no avail.

I think there is some Mashalling that needs to be done around //2, but I haven't been able to find any thing that really hits home as far as how to marshall that basic_string to a System::String^, or if it's even required. If so, then some help with explicit syntax would be greatly appreciated.

I've also narrowed the call that produces the failed assertions down to //1 by returning return ""; //3. These assertions point to trying to modify/delete memory that dosn't exist on the heap the current runtime has access to. Does that have to do with needing to marshall the return value of UnmangedClass::getSomeString()?

Hoping I'm just missing some simple concept here and there isn't a problem with the 3rd party code. Please let me know if I can provide any more detail, and apologies for my almost complete ignorance of the grand-daddy of all languages.

Thanks in advance for any information or "pointers";

EDIT: Adding C# Managed client implementation;

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

EDIT: Adding Dumpbin.exe signature of Unmanged.dll!UnmangedClass::getSomeString()

(public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __thiscall Codegen::getSomeString(void))

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

月下伊人醉 2024-11-07 16:41:12

这个问题与.NET或C++/CLI无关,问题纯粹是在本机代码中。

您违反了 std::string 的单一定义规则,如果您的定义与 Unmanagement_dll.dll 使用的内容不完全匹配,那么一切都会崩溃。听起来好像该 DLL 用于调试定义/类布局。

This problem has nothing to do with .NET or C++/CLI, the problem is purely in the native code.

You've violated the One Definition Rule for std::string, if your definition doesn't exactly match what Unmanaged_dll.dll uses, all hell breaks loose. And it sounds as if that DLL is used the debug definition/class layout.

森末i 2024-11-07 16:41:12

您可以将本机字符串转换为托管字符串。这篇 MSDN 上的文章提供了有关如何操作的示例在 Microsoft 平台上提供的所有不同字符串类型之间进行转换:

话虽如此,我获取了您的代码并对其进行了编译,并且我不会遇到任何失败。当然,我必须想出自己的初始化 UnmanagedClass::_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;
};

以及它的实现:

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

能有所帮助。

You converted your native string to a Managed string just fine. This article on MSDN, has samples on how to convert between all the different string types that ships on Microsoft platforms:

Having said that, I took your code, and compiled it, and I couldn't get anything to fail. Of course I had to come up with my own way of initializing UnmanagedClass::_someString, which I did by just doing this:

UnmanagedClass::UnmanagedClass(const float* /*flts*/, unsigned int /*fltlen*/, int /*offset*/)
{
    _someString = "A few of my favorite things";
}

When I did that, and stepped through this code:

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

It worked just fine.
Here is the rest of what I did:

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

And it's implementation:

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

And the managed class

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

I hope that helps a little.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文