C++/CLI wrapper memleak when calling managed lib from unmanaged - c#

I have this error when calling managed code library from unmanaged code:
Run-Time Check Failure #2 - Stack around the variable 'BridgeObj' was corrupted.
The code I created is:
Managed lib:
using System;
namespace My.Name.Space
{
public class Sample
{
public int Request(string xml_input, out string xml_output)
{
xml_output = "Retun string.";
return 0;
}
}
}
Wrapper C++/CLI:
#include "stdafx.h"
#include <msclr\auto_gcroot.h>
#using "..\link\ManagedLib.dll"
using namespace System::Runtime::InteropServices; // Marshal
struct ManagedModul
{
public:
msclr::auto_gcroot<My::Name::Space::Sample^> SampleModul;
};
class __declspec(dllexport) Bridge
{
private:
ManagedModul _private;
public:
Bridge()
{
_private.SampleModul = gcnew My::Name::Space::Sample();
};
~Bridge()
{
}
int Request ( const char * xmlin, char ** xmlout )
{
System::String ^ps;
_private.SampleModul->Request(gcnew System::String(xmlin), ps);
* xmlout = (char*) (Marshal::StringToHGlobalAnsi(ps)).ToPointer();
return 0;
}
};
Sample usage:
#include "stdafx.h"
#include <Windows.h>
#pragma comment ( lib, "..\\link\\Wrapper.lib" )
class Bridge
{
public:
Bridge();
~Bridge();
int Request ( const char * xmlin, char ** xmlout );
};
int _tmain(int argc, _TCHAR* argv[])
{
Bridge BridgeObj;
char * buffer = NULL;
BridgeObj.Request("aaaaa", & buffer );
LocalFree ( buffer );
return 0;
}

class Bridge
{
public:
Bridge();
~Bridge();
int Request ( const char * xmlin, char ** xmlout );
};
This is a very, very bad practice. Instead of using a .h file that is used in both projects, you redeclared the Bridge class. And got it wrong, you missed the _private member. This always goes wrong. If not immediately then in a year from now when you modify the real Bridge class.
What happens next is pretty inevitable. The real class object is larger than what the compiler thinks when it compiles your redeclared class. So it doesn't reserve enough space on the stack to store the object. Inevitable, that will cause another variable on the stack to get whacked, overwritten whenever the real class assigns the _private member.
Buy Microsoft a cigar for building in this diagnostic, stack corruption like this is normally extraordinarily hard to diagnose. And use a .h file to declare Bridge.

Related

Unit testing hybrid C++ / CLI / C# Code in Visual Studio fails

We have been given a C# DLL which contains code required for loading and manipulating a proprietary data format. Our codebase is primarily native C++. So we can access the supplied library, I wrote a DLL that consists of
a native C++ wrapper class that implements the necessary API
A C++/CLI bridge class that marshals data in and out and and delegates calls to the C# DLL.
I wrote a basic (native) C++ console application to test that the API does what I wanted. It did. I then wrote a (native, Microsoft Visual Studio) unit test project that calls the API. Surprisingly, it crashes with some unstated exception when constructing the interface object.
Some minimal code:
InteropAPI.h:
#pragma once
#pragma unmanaged
#if defined(INTEROP_API_EXPORTS)
#define INTEROP_API __declspec(dllexport)
#else
#define INTEROP_API __declspec(dllimport)
#endif
#include <string>
class InteropBridgeArchive;
struct InteropRecord
{
std::string Name;
int Id;
std::string Data;
};
class INTEROP_API InteropAPI
{
public:
InteropAPI();
~InteropAPI();
InteropRecord GetRecord(int id) const;
private:
InteropBridgeArchive * impl_;
};
InteropAPI.cpp:
#pragma managed
#include "InteropBridge.h"
#pragma unmanaged
#include "InteropAPI.h"
InteropAPI::InteropAPI()
: impl_(new InteropBridge())
{
}
InteropAPI::~InteropAPI()
{
delete impl_;
}
InteropRecord InteropAPI::GetRecord(const int id) const
{
return impl_->GetRecord(id);
}
InteropBridge.h
#pragma once
#pragma unmanaged
#include "InteropAPI.h"
#pragma managed
#include <vcclr.h>
class InteropBridge
{
public:
InteropArchive();
InteropRecord GetRecord(System::Int32 id) const;
private:
gcroot<InteropCSharpDll::Archive^> archive_;
};
InteropBridge.cpp:
#pragma unmanaged
#include <string>
#pragma managed
#include "InteropBridge.h"
#include <msclr/marshal_cppstd.h>
InteropBridge::InteropBridge()
{
archive_ = gcnew InteropCSharpDll::Archive;
}
InteropRecord InteropBridge::GetRecord(const System::Int32 id) const
{
auto return_value = archive_->GetRecord(id);
InteropRecord record;
record.Name = msclr::interop::marshal_as<std::string>(return_value.Name);
record.Id = return_value.Id;
record.Data = msclr::interop::marshal_as<std::string>(return_value.Data);
return record;
}
The test C# DLL has this:
using System.Linq;
namespace InteropCSharpDll
{
public struct Record
{
public string Name;
public int Id;
public string Data;
}
public class Archive
{
public Record GetRecord(int id)
{
return _records.First(r => r.Id == id);
}
private readonly Record[] _records =
{
new Record {Name = "Foo", Id = 0, Data = "Foo.Data"},
new Record {Name = "Bar", Id = 2, Data = "Bar.Data"},
new Record {Name = "Egg", Id = 7, Data = "Egg.Data"},
};
}
}
Clearly, if this hybrid structure can't be unit tested as native code, we can't test anything that touches it, so it's quite important to get to the bottom of why it crashes. This code runs nicely as a native C++ project:
#include <cstdio>
#include <iostream>
#include "../InteropCppCliBridgeDll/InteropAPI.h"
#pragma comment(lib, "../x64/Debug/InteropCppCliBridgeDll.lib")
int main(int , char ** )
{
const InteropAPI archive;
const auto record = archive.GetRecord(0);
std::cout << "Name: " << record.Name << "\n";
std::cout << "Id: " << record.Id<< "\n";
std::cout << "Data: " << record.Data<< "\n\n";
}
whereas in a native unit test project, the crash occurs in or around the line
archive_ = gcnew InteropCSharpDll::Archive;

How to pass List<int> from C# to C++ CLi?

I am beginner in C language family...
So, problem is - in my solution I have a repo (wrote in C#) that store List<int>, also I have an engine that (wrote in C++). So, I need pass List from C# implementation to C++ CLI for executing...
As far as I understood problem is C++ know how to work with std::vector and C# know how to work with List and I need somehow convert List to vector...
How to do it?
Any assumption appropriate.
EDIT
Sorry for misunderstanding, but my CLI works as a mapper for pure C++ implementation. So, as far as I understood from C# I need to pass my List to C++ CLI , C++ CLI will convert a List to vector and invoke another C++ file with pure C++ implementation.
This is my solution
h file
using namespace System;
using namespace System::Collections::Generic;
//forward declaration
class MathCore;
namespace MathCore_CLI_namespace
{
public ref class MathCore_CLI
{
public:
MathCore_CLI();
~MathCore_CLI();
int computeMulPlusVals(List<int>^ list_first, List<int>^ list_second);
//int computeMulPlusVals(std::vector<int> vect_first, std::vector<int> vect_second);
private:
MathCore * m_pMathCore;
};
}
cpp file
#include "stdafx.h"
#include "MathCore_CLI.h"
#include "..\Engine\MathCore.h"
#include <iostream>
#include <array>
using namespace System;
using namespace System::Collections::Generic;
namespace MathCore_CLI_namespace
{
const int size = 5;
int count = 0;
int arrayVal[size];
MathCore_CLI::MathCore_CLI()
{
m_pMathCore = new MathCore();
}
MathCore_CLI::~MathCore_CLI()
{
delete m_pMathCore;
}
int computeMulPlusVals(List<int>^ list_first, List<int>^ list_second)
{
return 0;
}
}
Error
What am I doind wrong?
C++ CLI directly supports List and you don't need a conversion. Here is a typical signature.
private: System::Void FooMethod( System::Collections::Generic::List<Int32>^ list )
Besides, In your updated question, you have a linker error.
Open C++ project properties, Find Linker and then Input. Add the location to your (EngineLib_Cli.dll) Library there
Also I found more optimized solution
int MathCore_CLI::computeMulPlusVals(array<int>^ arr_first, array<int>^ arr_second)
{
auto vec_first = std::vector<int>(arr_first->Length);
cli::pin_ptr<int> pPinnedFirst = &arr_first[0];
memcpy(vec_first.data(), pPinnedFirst, arr_first->Length * sizeof(int));
auto vec_second = std::vector<int>(arr_second->Length);
cli::pin_ptr<int> pPinnedSecond = &arr_second[0];
memcpy(vec_second.data(), pPinnedSecond, arr_second->Length * sizeof(int));
return m_pMathCore->computeMulPlusVals(vec_first, vec_second);
}
By steps
1) you need to create your vector
2) hold this memory in heap
3) just copy the data
It is going to be much more faster

how to use byte array as parameter when calling a WindowsPhoneRuntimeComponent?

First of all hey guys! I would like to access 2 C++ functions from my windows phone app.
so i followed every step of this tutorial and i managed to call the function as the poster of the tutorial. Now i wanted to access my own functions so i created the class in the header and in the .cpp file and the project is building ok as long as my functions are not public. Means i cant access them.
public ref class Base64Encoding sealed
{
public:
char *EncodeData(char *data, int length, int *resultLength); //this doesnt compile
char *DecodeString(char *data, int *resultLength); //this compiles buts inaccessible
};
i get a return exception saying error C3992: signature of public member contains invalid type char.
I did some googling and as far as i understand i cannot send parameters of type char since its unmanaged code or something like that.
So what is the issue here? why i cannot pass parameters of type char?
Update
i followed robwirving suggestion and now the header looks like this.
public ref class Base64Encoding sealed
{
public : Platform::String^ EncodeData(String^ StringData);
public : Platform::String^ DecodeString(String^ StringData);
};
in order now to get char* data from the String^ StringData parameter i did in my .cpp
#include <string>
#include <iostream>
#include <msclr\marshal_cppstd.h>
using namespace Platform;
using namespace std;
String^ EncodeData(String^ StringData)
{
// base64 lookup table. this is the encoding table for all 64 possible values
static char *figures = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
msclr::interop::marshal_context context;
std::string s = context.marshal_as<std::string>(StringData);
char *data = new char[s.size() + 1];
int length = s.length;
int *resultLength = s.length;
data[s.size()] = 0;
/* bla bla some functions irrelevant*/
.
.
.
return StringFromAscIIChars(result);
}
static String^ StringFromAscIIChars(char* chars)
{
size_t newsize = strlen(chars) + 1;
wchar_t * wcstring = new wchar_t[newsize];
size_t convertedChars = 0;
mbstowcs_s(&convertedChars, wcstring, newsize, chars, _TRUNCATE);
String^ str = ref new Platform::String(wcstring);
delete[] wcstring;
return str;
}
but now i get 2 errors on building.
1: error C1114: WinRT does not support #using of a managed assembly
2: IntelliSense: an ordinary pointer to a C++/CX mapping ref class or interface class is not allowed
You said the answer right in your question. You can't use a char in a public function of a WinRT component. You can however use strings, I'd suggest changing your functions to the following:
public ref class Base64Encoding sealed
{
public:
Platform::String^ EncodeData(Platform::String^ data);
Platform::String^ DecodeString(Platform::String^ data);
};
In the definition of your encode/decode functions you can then convert your inputs from a Platform::String^ to a char array, call your original C++ function, then convert the return value back to a Platform::String^
I know this may seem like a lot of extra work, but it makes the interop a lot easier for the C# that will consume your WinRT component.
Update:
I think your additional errors probably come from the including msclr\marshal_cppstd.h and the way you are converting your Platform::String^ to a std::string.
Reference this post for how to convert from Platform::String^ to a char*: How to convert Platform::String to char*?

How to call C++ function takes Vector call from C#

I am working in C++ and creating library which analyses the data. I have created few classes which have functions taking C++ vector. Now I want to create UI in C# and call these classes. I am thinking to create API to call from C#.
Since data are arrays/vector then how can I call it from C#?
I would have just made this a comment, but my rep isn't high enough. There are some complications when using STL classes (such as vector or string) in a class library with C++. You can check here for some more info and possible solutions: I can pass std::string for a Dll and what i can do with DLL´s?
You need to create your own C++/CLI interop to achieve this.
Strongly recommend a nice book, "Expert C++/CLI" by Marcus Heege, quite a good read.
Here's my brief example:
// Program.cs
static void Main(string[] args)
{
List<string> someStringList = new List<string>();
someStringList.Add("Betty");
someStringList.Add("Davis");
someStringList.Add("Eyes");
NativeClassInterop nativeClass = new NativeClassInterop();
string testString = nativeClass.StringCat(someStringList);
}
// NativeClass.h, skipping this, it's obvious anyways
// NativeClass.cpp, normal C++ class, this was in some DLL project, don't need exports
#include "stdafx.h"
#include "NativeClass.h"
std::string NativeClass::StringCat(std::vector<std::string> stringList)
{
std::string result = "";
for(unsigned int i = 0; i < stringList.size(); i++)
{
if(i != 0)
{
result += " ";
}
result += stringList[i];
}
return result;
}
// NativeClassInterop.cpp, in same DLL project, but compile this file with /clr switch
#include <gcroot.h>
#using <System.dll>
#include <vector>
#include <string>
#include "NativeClass.h"
// Helper method
static std::string nativeStringFromManaged(System::String^ str)
{
System::IntPtr hGlobal =
System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(str);
std::string nativeString((hGlobal.ToPointer() == 0)
? "" : (char*)hGlobal.ToPointer());
System::Runtime::InteropServices::Marshal::FreeHGlobal(hGlobal);
return nativeString;
}
// C++/CLI wrapper class
public ref class NativeClassInterop
{
public:
System::String^ StringCat(System::Collections::Generic::List<System::String^>^ someStringList)
{
// You get to do the marshalling for the inputs
std::vector<std::string> stringList;
for(int i = 0; i < someStringList->Count; i++)
{
stringList.push_back(nativeStringFromManaged(someStringList[i]));
}
NativeClass nativeClass;
std::string nativeString = nativeClass.StringCat(stringList);
// And for the outputs ;-)
System::String^ managedString = gcnew System::String(nativeString.c_str());
return managedString;
}
};

How do I load a C dll into a C# code?

I am in the need of using some functions in a C made program. To test I defined the following :
This is my .h file :
namespace amt
{
class AMT_EXPORT FaceRecognition
{
public:
std::string amt_test_string(std::string in);
};
};
This is my .cpp file :
#include <memory.h>
#include <string>
#include <iostream>
#include <fstream>
#include "api_shared.h"
#include <sys/stat.h>
using namespace std;
std::string amt::FaceRecognition::amt_test_string (std::string in)
{
std::string s="in: "+in;
std::cout<<s<<std::endl;
return s;
}
I am trying to invoke the method like this :
const string str = "C:\\minimal.dll";
[DllImport(str)]
public static extern string amt_test_string(string input);
static void Main(string[] args)
{
string myinput = "12";
string myoutput = "";
myoutput = amt_test_string(myinput);
Console.WriteLine(myoutput);
Console.Read();
}
But im getting an error saying that it cannot find the entry point named amt_test_string..why so? I am a newbie in C btw
That's not a C DLL, that's a C++ DLL. C and C++ are not the same language. In particular, C++ has name mangling, so the function name which gets exported to the DLL is decorated.
I'd strongly recommend that you avoid having C++ exports in your DLL for that reason. If you use only C exports, the symbol name will be predictable (i.e. will not depend on the specific details of how your C++ compiler decorates names), and you won't have to worry about runtime differences, like how your C++ standard library implements std::string.
I'd recommend your DLL export look like this:
extern "C" // This says that any functions within the block have C linkage
{
// Input is 'in', output gets stored in the 'out' buffer, which must be 'outSize'
// bytes long
void DLLEXPORT amt_FaceRecogniztion_amt_test_string(const char *in, char *out, size_t outSize)
{
...
}
}
This interface does not rely on any particular library's std::string implementation, and C# knows how to martial char* parameters as C strings. However, memory management is more complicated, as you need to figure out an upper bound on how big the output is going to be and pass in an appropriately sized bufer.

Categories

Resources