开发者

Handling an Unmanaged String in a C++/CLI Wrapper - BLOCK_TYPE_IS_VALID, _CrtIsValidHeapPointer

开发者 https://www.devze.com 2023-02-23 07:06 出处:网络
I\'m new to C++/CLI but have been coding managed code for many years... apparently too many years. :)

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 t开发者_StackOverflow中文版o //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))


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号