Unmanaged String Processing in C ++ / CLI Wrapper - BLOCK_TYPE_IS_VALID, _CrtIsValidHeapPointer

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

An attempt to write a wrapper for an unmanaged class provided by a third party, and I see some strange things. I hope you all can help me pick weeds, what is my noobility and what is really 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 is Unmanaged.h;

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

It all compiles. Then comes the oddity / lack of experience.

When debugging in the DEBUG configuration, UnmanagedClass::getSomeString() seems to return the value of the resonant / expected string value. I see this by setting a breakpoint at //2 and looking at the value of x . If I go to //3 , I see that ret has the value x . However, if I try to exit / above //3 , I get a couple of unsuccessful statements ( BLOCK_TYPE_IS_VALID and _CrtIsValidHeapPointer ) and _CrtIsValidHeapPointer , never returning to a managed implementation.

When debugging this in the RELEASE configuration, I do not get unsuccessful statements, and I return to my managed implementation, but the string value returned by getSomeString() is garbage wherever I look in it; //2 , //3 , as well as in a managed implementation.

I massaged the code in several ways, but to no avail.

I think there is something like Mashalling that needs to be done around //2 , but I could not find anything that really gets home, as far as this can be done, to basic_string in System::String^ or even if it is required. If so, then some help with explicit syntax would be greatly appreciated.

I also narrowed down the call, which causes unsuccessful statements to //1 , returning return ""; //3 return ""; //3 . These statements indicate an attempt to modify / delete memory that does not exist on the heap that the current runtime has access to. Is this due to the need to UnmangedClass::getSomeString() return value of UnmangedClass::getSomeString() ?

Hopefully I just skipped a simple concept here and there is no problem with third-party code. Please let me know if I can provide more detailed information and apologize for the almost complete ignorance of the great pope of all languages.

Thanks in advance for any information or "pointers";

EDIT: adding a managed C # 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 to Unmanged.dll! UnmangedClass :: getSomeString ()

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

+4
source share
2 answers

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

You have violated the rule of one definition for std::string , if your definition does not exactly match those used by Unmanaged_dll.dll , all hell breaks. And it sounds as if this DLL used the layout of the debug definition / class.

+2
source

You converted your native string to a managed string just fine. This MSDN article contains examples of how to convert all types of strings that ship on Microsoft platforms:

Having said that, I took your code and compiled it, and I could not get anything to fail. Of course, I had to come up with my own UnmanagedClass :: _ someString initialization method, which I did just by doing this:

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

When I did this and went 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; } 

Everything went perfectly. Here is the rest that 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 this is the 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 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 this helps a bit.

+1
source

Source: https://habr.com/ru/post/1347527/


All Articles