I'm working in Unity C#, trying to integrate with a dll written in ada. I need to pass a class containing strings and doubles to the dll for it to modify and return, but I get the following error:
"Type System.Text.StringBuilder which is passed to unmanaged code must have a StructLayout attribute."
Now, these classes already have a StructLayoutAttribute. And I can't put a StructLayout attribute on a Stringbuilder, Unity tells me it only works on classes and structs. Which makes me really confused about the error message, because it seems like it's asking me for something that's impossible?
public class controller : MonoBehavior{
myClass student = new myClass();
[DllImport ("my_dll", EntryPoint="myFunction#12")]
public static extern void myFunction(myClass c);
void Start(){
myFunction (student);
}
}
[StructLayoutAttribute(LayoutKind.Sequential, size=2)]
public class myClass {
public double height = 0.0;
public System.Text.StringBuilder name = new System.Text.StringBuilder(16);
}
IIRC C# marshals StringBuilder as char* by default when using PlatformInvoke features. This is handy for interfacing with C call convention DLLs. Some Ada compilers (especially the GCC one) might allow Ada libraries to be optimised for C call conventions. The "almost magical" nature of this may in fact be true. :)
My suggestion: surround your C# call to the Ada functions with an unsafe{} block and fixed() your char array, then pass it to your Ada function, then drop out of the fixed block again after it returns.
I don't have a working copy of Visual Studio so I can't verify this.
(Edited to fix incorrect terminology)
Something about this definitely feels off. If your Ada DLL knows how to handle a System.Text.StringBuilder then that seems almost magical. You generally need to pass primitive types around when dealing with native libraries. Also, this Ada DLL is possibly not a native dll, which is what Mono is expecting to work with. See Interop With Native Libraries for more info on marshaling data.
Now lets assume that your Ada DLL is actually a native DLL. And it somehow knows what to do with a StringBuilder. You can possibly pass a void pointer to the memory address via IntPtr. But I highly doubt this would work. Most likely what you want to do is pass the actual string through:
public class controller : MonoBehavior{
myClass student = new myClass();
[DllImport ("my_dll", EntryPoint="myFunction#12")]
public static extern void myFunction(myClass c);
void Start(){
student.name = "John Doe";
myFunction (student);
}
}
[StructLayoutAttribute(LayoutKind.Sequential, size=2)]
public class myClass {
//The following MarshalAs may be unnecessary.
[MarshalAs (UnmanagedType.LPStr)]
public string name;
public double height = 0.0;
}
LPStr is a 'long pointer to a string'. The assumption is your Ada DLL will know what to do with that. If that doesn't work, then you can remove the MarshalAs and see if it likes it better that way.
This C function will take an input str that must be less than ~250 characters and returns a modified string. You'll see that it mallocs a string the size of sprintf's modified string and then strcopy's it. There is no corresponding free() because it will be freed by mono as mentioned here.
char* myFunction(char* input, double height)
{
char buffer[256];
int n = sprintf(buffer, "%s -- %f", input, height);
char* ret = (char*)malloc(n+1); // +1 for the newline
strcopy(ret, buffer);
return ret;
}
Related
I have a C++ function that produces a list of rectangles that are interesting. I want to be able to get that list out of the C++ library and back into the C# application that is calling it.
So far, I'm encoding the rectangles like so:
struct ImagePatch{
int xmin, xmax, ymin, ymax;
}
and then encoding some vectors:
void MyFunc(..., std::vector<int>& rectanglePoints){
std::vector<ImagePatch> patches; //this is filled with rectangles
for(i = 0; i < patches.size(); i++){
rectanglePoints.push_back(patches[i].xmin);
rectanglePoints.push_back(patches[i].xmax);
rectanglePoints.push_back(patches[i].ymin);
rectanglePoints.push_back(patches[i].ymax);
}
}
The header for interacting with C# looks like (and works for a bunch of other functions):
extern "C" {
__declspec(dllexport) void __cdecl MyFunc(..., std::vector<int>& rectanglePoints);
}
Are there some keywords or other things I can do to get that set of rectangles out? I found this article for marshalling objects in C#, but it seems way too complicated and way too underexplained. Is a vector of integers the right way to do this, or is there some other trick or approach?
The STL is a C++ specific library, so you cant directly get it across as one object to C#.
The one thing that is guaranteed about std::vector is that &v[0] points to the first element and all the elements lie linearly in memory (in other words, its just like a C array in terms of memory layout)
So marshal as array of int... which shouldn't be hard - There are lot of examples on the web.
Added
Assuming you only pass the data from C++ to C# :
C# cannot handle a C++ vector object, so do not try passing it by reference : Instead your C++ code must return a pointer to an array of ints...
If you are not going to be using this function from multiple threads, you can use static storage :
int *getRects(bool bClear)
{
static vector<int> v; // This variable persists across invocations
if(bClear)
{
v.swap(vector<int>());
}
else
{
v.clear();
// Fill v with data as you wish
}
return v.size() ? &v[0] : NULL;
}
call getRects(true) if the returned data is significant in size, so you release the memory in v.
For simplicity, instead of passing out the size of the vector data too, just put a sentinel value at the end (like say -1) so the C# code can detect where the data ends.
Yes. You can. Actually, not just std::vector, std::string, std::wstring, any standard C++ class or your own classes can be marshaled or instantiated and called from C#/.NET.
Wrapping a std::vector<any_type> in C# is indeed possible with just regular P/Invoke Interop, it is complicated though. even a std::map of any type can be done in C#/.NET.
public class SampleClass : IDisposable
{
[DllImport("YourDll.dll", EntryPoint="ConstructorOfYourClass", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void SampleClassConstructor(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DestructorOfYourClass", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void SampleClassDestructor(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DoSomething", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void DoSomething(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DoSomethingElse", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void DoSomething(IntPtr thisObject, int x);
IntPtr ptr;
public SampleClass(int sizeOfYourCppClass)
{
this.ptr = Marshal.AllocHGlobal(sizeOfYourCppClass);
SampleClassConstructor(this.ptr);
}
public void DoSomething()
{
DoSomething(this.ptr);
}
public void DoSomethingElse(int x)
{
DoSomethingElse(this.ptr, x);
}
public void Dispose()
{
if (this.ptr != IntPtr.Zero)
{
// The following 2 calls equals to "delete object" in C++
// Calling the destructor of the C++ class will free the memory allocated by the native c++ class.
SampleClassDestructor(this.ptr);
// Free the memory allocated from .NET.
Marshal.FreeHGlobal(this.ptr);
this.ptr = IntPtr.Zero;
}
}
}
Please see the below link,
C#/.NET PInvoke Interop SDK
(I am the author of the SDK tool)
Once you have the C# wrapper class for your C++ class ready, it is easy to implement ICustomMarshaler so that you can marshal the C++ object from .NET.
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.icustommarshaler.aspx
I'm pretty sure you can't do this. You have to be able to translate the C++ code directly to a C# class, so you would at least have to replicate the internals of the vector class to marshall it correctly. I'm also pretty sure you won't be able to move references across the boundary, you'll have to use IntPtr (raw pointers). The approach that i know works is to marshall a raw array of the structs.
Looking for help from experience people who have dealt with C++ and C#
I am now writing a C# project which requires calling an existing C++ function in the internal library, and encountering some difficulties.
C++ Code Function Header (Cannot be changed)
typedef int (*PTR_func)
(const char* param1, // input
const char* param2, // input
VirtualInternalString& po_output); // Third param is actually the output
C++ Code Class for the internal string object (Cannot be changed)
namespace InternalLib
{
class VirtualInternalString
{
public :
virtual ~VirtualInternalString(){};
virtual void _cdecl SetString(const char *) =0;
virtual const char * _cdecl c_str() const =0;
};
class SafeInternalString :public VirtualInternalString
{
public :
SafeInternalString():TheString(""){};
virtual ~SafeInternalString(){};
virtual void _cdecl SetString(const char * chararray) { TheString = chararray;};
virtual const char * _cdecl c_str() const { return TheString.c_str() ;} ; // This is the std::string
protected:
std::string TheString;
};
class SafePtrString :public VirtualInternalString
{
public :
SafePtrString():PtrString(0){};
SafePtrString(std::string &str):PtrString(&str){};
virtual ~SafePtrString(){};
virtual void _cdecl SetString(const char * chararray) { (* PtrString) = chararray;};
virtual const char * _cdecl c_str() const { return PtrString->c_str() ;} ;
protected :
std::string * PtrString;
};
}
Now I have to utilize this "func" in my C# code. I can call and use the C++ function properly, but problem occurs during output return (the third param). It should be problem related return type.
C# (Can be changed)
[DllImport("xxxxlib", CallingConvention = CallingConvention.Cdecl)]
private extern static int func(string param1,
string param2,
[MarshalAs(UnmanagedType.LPStr)] StringBuilder res); // problem here
I know I have to create an appropriate wrapper to decopose the c++ object into C# string but I don't have previous experience about it. I have tried several solutions from the internet but not working.
Does any one know how I can implement this? Would you mind showing me some simple C# skeleton code?
vasek's possible duplicate won't work if you only change the C# side. But what you can do is create wrappers in C++ that create raw char * with associated length parameter.
You create an additional C++ header and source which contains:
int func
(const char* param1, // input
const char* param2, // input
char* po_output, // Third param is actually the output
int length); // Length of the buffer allocated by caller
And then you make your func call PTR_func.
This is a case in which I think using C++/CLI does make sense. You may want to build a tiny C++/CLI bridge between the native C++ DLL and your C# code.
The C++/CLI layer is responsible to take your custom C++ string class as output parameter from the DLL function, and convert from this custom string to a C#/.NET string object.
Your custom C++ string class seems to wrap std::string; on the other hand, C#/.NET strings use Unicode UTF-16 as their text encoding, so you may need to convert between Unicode UTF-8 (stored in std::string and in your custom string class) and UTF-16 (for C#/.NET strings).
Similarly, you also need to convert C# UTF-16 strings to UTF-8 in this C++/CLI bridging layer, and pass these UTF-8-encoded strings as your const char* input parameters to the native DLL function.
Use C++ COM and consume from C# code. Only if u are on windows platform.
In past experience, I did use SWIG tool for generation wrapper implementation
It has support for C++ to wrapper generation to several languages, where Java the most popular one
There is a great tutorial - http://www.swig.org/tutorial.html
C# is also supported - http://www.swig.org/Doc3.0/CSharp.html
/* InternalLib.i */
%module InternalLib
%{
/* include header with a VirtualInternalString class*/
#include "InternalLib.h"
%}
then call swig to generate wrapper files:
swig -csharp InternalLib.i
In my C++ dll named "scandll.dll" I have the following function
extern "C" __declspec(dllexport) void scanfile(char * returnstring)
{
strcpy(returnstring, "return string");
}
in my C# code I'm doing this
[DllImport("scandll.dll", CharSet = CharSet.Ansi, SetLastError = true )]
public static extern int scanfile(ref IntPtr strReturn);
and this is the method that I'm using to get the value from the dll
public void Scan()
{
string filename = "";
IntPtr ptr = new IntPtr();
scanfile(ref ptr);//Here i get the error
filename = Marshal.PtrToStringAnsi(ptr);
}
I get an exception thrown as "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I'm following this link
Calling C++ code from C# error using references in c++ ref in c#
Any help would be appreciated.
Thanks
Your C++ code is wrong - all it's doing is changing the value of the pointer which has been passed to it to point to a constant string. The caller knows nothing about that change.
It's the same as if you'd done this:
void MyCfunction(int number)
{
number = 3;
}
and then hoped that a caller of that function would somehow see the number '3' anywhere.
You could do something like this in C++:
void MyCFunction(char* pReturnString)
{
strcpy(pReturnString, "Hello, world");
}
but you also need to make sure on the C# side that you had provided a buffer with pReturnString pointing to it. One way to do this by using a StringBuilder, and then having your C# declaration of the C function take a parameter like this:
[MarshalAs(UnmanagedType.LPStr)] StringBuilder returnString
You need to reserve space in the StringBuilder before calling into the C++ function.
In real life, C/C++ functions like this pretty much always take an additional length parameter, which allows the caller to tell the callee the maximum length of string it's allowed to copy into the buffer. There is no consistency of convention about whether the length includes the terminating \0 or not, so people are often careful about running right up to the end of the buffer.
memcpy ( destination, * source, size_t num );
Try this to assign the value to returnstring = "rturn name";
UDK uses .NET. So maybe it is possible somehow to use .NET from UnrealScript?
It is really great to use C# with UnrealScript.
One certainly could build C++ layer to interact between .NET and UnrealScript which will use dllimport, but it is not the subject for this question.
So there seems to be no way to access .NET libraries directly from UnrealScript, but one can combine [DllExport] extension for C# and UnrealScript interop system to interact with .NET without intermediate C++ wrapper.
Lets look at simple example of exchanging with int, string, structure and filling UnrealScript String in C#.
1 Create C# class
using System;
using System.Runtime.InteropServices;
using RGiesecke.DllExport;
namespace UDKManagedTestDLL
{
struct TestStruct
{
public int Value;
}
public static class UnmanagedExports
{
// Get string from C#
// returned strings are copied by UnrealScript interop system so one
// shouldn't worry about allocation\deallocation problem
[DllExport("GetString", CallingConvention = CallingConvention.StdCall]
[return: MarshalAs(UnmanagedType.LPWStr)]
static string GetString()
{
return "Hello UnrealScript from C#!";
}
//This function takes int, squares it and return a structure
[DllExport("GetStructure", CallingConvention = CallingConvention.StdCall]
static TestStructure GetStructure(int x)
{
return new TestStructure{Value=x*x};
}
//This function fills UnrealScript string
//(!) warning (!) the string should be initialized (memory allocated) in UnrealScript
// see example of usage below
[DllExport("FillString", CallingConvention = CallingConvention.StdCall]
static void FillString([MarshalAs(UnmanagedType.LPWStr)] StringBuilder str)
{
str.Clear(); //set position to the beginning of the string
str.Append("ha ha ha");
}
}
}
2 Compile the C# code, say, as UDKManagedTest.dll and place to [\Binaries\Win32\UserCode] (or Win64)
3 On UnrealScript side one should place declarations of the functions:
class TestManagedDLL extends Object
DLLBind(UDKManagedTest);
struct TestStruct
{
int Value;
}
dllimport final function string GetString();
dllimport final function TestStruct GetStructure();
dllimport final function FillString(out string str);
DefaultProperties
{
}
Then one can use the functions.
The only trick is filling UDK string like it is shown in FillString method. Since we pass string as fixed length buffer, this string MUST be initialized. The length of initialized string MUST be more or equal to length C# will work with.
Further reading could be found here.
This question already has answers here:
Marshal "char *" in C#
(2 answers)
Closed 4 years ago.
I am using PInvoke for interoperability between Native Code (C++) and Managed Code (C#).
I just write a simple function which gets a string from C++ code. My code looks like
C# Code:
[DllImport("MyDll.dll")]
private static extern string GetSomeText();
public static string GetAllValidProjects() {
string s = GetSomeText();
return s;
}
C++ Code
char* GetSomeText() {
std::string stri= "Some Text Here";
char * pchr = (char *)stri.c_str();
return pchr;
}
All works fine at C++ end, i.e the variable pchr contains "Some Text Here" but at C# the string s contains noting in it. I don't know what I am doing wrong. Any help would be appreciated
First of all, as others have pointed out, your C++ is broken even before trying interop. You are returning a pointer to stri's buffer. But because stri is destroyed as soon as the function returns, the return value is not valid.
What's more, even if you fixed this, you need to do more. It won't work allocating memory in your C++ code which you would need the C# code to deallocate.
There are a few options to do it right.
Your C# code can ask the C++ code how long the string is. Then a C# StringBuilder is created and allocated to the appropriate size. Next the StringBuilder object is passed to the C++ code and its default marshalling is as a LPWSTR. In this approach the C# code allocates the string and your C++ code receives a C string to which it must copy the buffer.
Alternatively you can return a BSTR from the C++ which allows allocation in the native C++ code and deallocation in the C# code.
The BSTR approach is probably how I would do it. It looks like this:
C++
#include <comutil.h>
BSTR GetSomeText()
{
return ::SysAllocString(L"Greetings from the native world!");
}
C#
[DllImport(#"test.dll", CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.BStr)]
private static extern string GetSomeText();
Update
Hans Passant added a couple of useful observations in the comments. First of all, most P/Invoke interop is done against an existing interface which cannot be changed and you do not have the luxury of picking your preferred interop interfacing approach. It would appear that is not the case here, so which approach should be chosen?
Option 1 is to allocate the buffer in the managed code, after having first asked the native code how much space is needed. Perhaps it is enough to use a fixed size buffer that both parties agree on.
Where option 1 falls down is when assembling the string is expensive and you don't want to do it twice (e.g. once to return its length, and once again for the contents). This is where option 2, the BSTR comes into play.
Hans pointed out one drawback of the BSTR, namely that it carries a UTF-16 payload but your source data may well char*, which is a "bit of a hassle".
To overcome the hassle you can wrap up the conversion from char* to BSTR like this:
BSTR ANSItoBSTR(char* input)
{
BSTR result = NULL;
int lenA = lstrlenA(input);
int lenW = ::MultiByteToWideChar(CP_ACP, 0, input, lenA, NULL, 0);
if (lenW > 0)
{
result = ::SysAllocStringLen(0, lenW);
::MultiByteToWideChar(CP_ACP, 0, input, lenA, result, lenW);
}
return result;
}
That's the hardest one out of the way, and now it's easy to add other wrappers to convert to BSTR from LPWSTR, std::string, std::wrstring etc.
Another way to get a string from C++. In case the you cannot modify your c++ dll. You can declare the DllImport with IntPtr instead of string. When the function is invoked, you can marshal the Ptr back to String.
[DllImport("MyDll.dll")]
private static extern IntPtr GetSomeText();
public static string GetAllValidProjects()
{
string s = Marshal.PtrToStringAnsi(GetSomeText());
return s;
}
Note : as mention in the previous post. "Some Text Here" is allocated on the stack so as soon as the function returns, the stack will unwire. Therefore the data is potential be overridden. Hence you shall use Marshal.PtrToStringAnsi right after the call. Don't hold to the IntPtr.
char* GetSomeText()
{
std::string stri= "Some Text Here";
char * pchr = (char *)stri.c_str();
return pchr;
}
It is because
std::string stri= "Some Text Here";
is a stack object and it is getting destroyed after GetSomeText() call. After the call pchr pointer which you are returning is invalid.
You may need to allocate space for the text dynamically to be able to access it latter.
Change your C++ function definition to:
char* GetSomeText()
{
std::string stri = "Some Text Here";
return strcpy(new char[stri.size()], stri.c_str());
}
Something like above. You got the idea...
GetSomeText() is returning pointer of char is wont work if u declare string variable to store it.
try
[DllImport("MyDll.dll")]
private static extern string GetSomeText();
public static string GetAllValidProjects()
{
string s = new string(GetSomeText());
return s;
}
there's a constructor that takes char*