Handling an Unmanaged String in a C++/CLI Wrapper - BLOCK_TYPE_IS_VALID, _CrtIsValidHeapPointer
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.
精彩评论