Pointer of a C# object for unmanaged interop - c#

I am currently writing a wrapper for the PhysFS library, and I stumbled across a bit of troubles regarding the marshalling of managed objects. Take for example the PHYSFS_enumerateFilesCallback method, which takes a function pointer and a user-defined pointer as its arguments. How can I pass managed objects to this method? This is what I am currently doing:
// This is the delegate signature
public delegate void EnumFilesCallback(IntPtr data, string origdir, string fname);
// This is the method signature
[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
public static extern void PHYSFS_enumerateFilesCallback(string dir, EnumFilesCallback c, IntPtr d);
Finally, this is what I'm doing to pass an arbitrary object to the method:
// I use the unsafe keyword because the whole Interop class is declared so.
// This code was taken from https://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle(VS.71).aspx
public static void EnumerateFilesCallback(string dir, EnumFilesCallback c, object data)
{
unsafe
{
GCHandle objHandle = GCHandle.Alloc(data);
Interop.PHYSFS_enumerateFilesCallback(dir, c, (IntPtr)objHandle);
objHandle.Free();
}
}
When I run this code:
static void Enum(IntPtr d, string origdir, string fname )
{
System.Runtime.InteropServices.GCHandle handle = (System.Runtime.InteropServices.GCHandle)d;
TestClass c = (TestClass)handle.Target;
Console.WriteLine("{0} {1}", origdir, fname);
}
static void Main(string[] args)
{
PhysFS.Init("");
PhysFS.Mount("D:\\", "/hello", true);
TestClass x = new TestClass() { a = 3, b = 4 }; // This can be any gibberish object
PhysFS.EnumerateFilesCallback("/hello/", Enum, x);
}
The delegate gets called 4 times with legit data, the fifth time it contains garbage data and then it throws an AccessViolationException
I suspect this is because the object gets GCed in between the calls to the delegate. Can anyone shed light on this?
UPDATE: Changing the mounted directory eliminates the rubbish data, yet the exception is still thrown, and still before all the data can be consumed

Have you tried to create the callback and store it as a class static field?
private static EnumFilesCallback callback = new EnumFilesCallback(Enum);
And in your main method:
PhysFS.EnumerateFilesCallback("/hello/", callback, x);
This should probably avoid the GC to collect the local variable holding the delegate object.

Thanks to everyone who invested their time trying to provide an answer! I've finally found the source of the problem and solved it!
The problem was... I am a bit ashamed of it... calling convention.
All the PInvoked methods were declared as cdecl while I forgot to declare the delegates as such, so it created unbalanced stacks and mayhem and whatnot...

Related

Return function value from dll c++ to c# [duplicate]

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.

Passing a class containing a StringBuilder from C# to ada

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;
}

Using a managed buffer in native code after the call returns

I have a buffer I am allocating in C# and passing to native code. I then save a pointer to that buffer:
Header Definitions
// called when a meter is attached
typedef void(__cdecl * lpfnMeterAttachCallback)();
extern lpfnMeterAttachCallback frMeterAttachCallback;
// called when a meter is detached
typedef void(__cdecl * lpfnMeterDetachCallback)();
extern lpfnMeterAttachCallback frMeterDetachCallback;
// called when a meter is detached
typedef void(__cdecl * lpfnMeterReadCompleteCallback)();
extern lpfnMeterReadCompleteCallback frMeterReadCompleteCallback;
extern char* frbuffer;
Main
lpfnMeterAttachCallback frMeterAttachCallback;
lpfnMeterDetachCallback frMeterDetachCallback;
lpfnMeterReadCompleteCallback frMeterReadCompleteCallback;
char* frbuffer;
extern "C" __declspec(dllexport) void __cdecl InitDriver(
lpfnMeterAttachCallback meterAttachCallback,
lpfnMeterDetachCallback meterDetachCallback,
lpfnMeterReadCompleteCallback meterReadCompleteCallback, char* buffer)
{
frMeterAttachCallback = meterAttachCallback;
frMeterDetachCallback = meterDetachCallback;
frMeterReadCompleteCallback = meterReadCompleteCallback;
frbuffer = buffer;
...
}
and later in a background call, fill it as so:
JSONValue testSS = ss->GetJSON();
std::string help = *testSS;
strcpy(frbuffer, help.c_str());
if (frMeterReadCompleteCallback) frMeterReadCompleteCallback();
However my string comes back empty, or as corrupted data. Ideally, I wanted to have a string passed back to my delegate and then use that, but ran into InterOp issues trying to do so.
C#:
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void InitDriver(MeterAttachCallback meterAttachCallback,
MeterDetachCallback meterDetachCallback,
MeterReadCompleteCallback meterReadCompleteCallback, StringBuilder sb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MeterAttachCallback();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MeterDetachCallback();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MeterReadCompleteCallback();
static public void OnMeterAttach()
{
Console.WriteLine("OnMeterAttach");
}
static public void OnMeterDetach()
{
Console.WriteLine("OnMeterDetach");
}
static StringBuilder sb = new StringBuilder(10 * 1024 * 1024); //10MB
static public void OnMeterReadComplete()
{
Console.WriteLine("OnMeterReadComplete; JSON:", sb.ToString());
}
static void Main(string[] args)
{
InitApolloCppDriver(OnMeterAttach, OnMeterDetach, OnMeterReadComplete, sb);
Console.ReadLine();
}
Am I writing/reading the buffer incorrectly, or should I be implementing this in a different way?
Yes, this cannot work as intended. The pinvoke marshaller has built-in knowledge about StringBuilder. When you use it normally, it first pins the underlying buffer so it cannot move while the native code is running. Then it makes the call to the native code and after that call completes it directly manipulates the members of the StringBuilder object to make it jive with the native string.
None of this is happening in your case, you party on the buffer after the pinvoke marshaller returned. In other words, you are writing to a buffer whose address is not guaranteed to be stable. That's very, very bad, debugging a corrupted GC heap is very unfun. You do happen to get away with it in this specific case, the buffer is too large, next time you won't be so lucky.
And of course the internal members of StringBuilder don't get updated, getting an empty string is the expected outcome.
And the primary reason you should not be doing this, there is no conversion done from the 8-bit C-string characters you write (ANSI encoding assumed) to the utf-16 Unicode codepoints that a StringBuilder stores. In other words, this is not an optimization at all, the string always needs to be copied. The data corruption you see is a side effect of other code re-using the memory that was previously occupied by a temporary unmanaged buffer. You didn't get to a point yet where you in turn corrupted the memory of, say, a bitmap. Completely undiagnosable of course.
You need to throw this away. Since copying is inevitable, you might as well have the native code allocate the buffer.

How to create Multithreaded Dll

I have Dll that reads file from disk and returns it's content:
mydll.h:
extern "C" __declspec(dllexport) void InitFile3dPoints(wchar_t* i_file);
extern "C" __declspec(dllexport) int GetNumPointsForSurface(int i_surf_index);
extern "C" __declspec(dllexport) void GetPointsForSurface
(double* o_result, int i_resultLength, int i_surf_index);
mydll.cpp:
File3dPoints file_3dPoints;
void InitFile3dPoints(wchar_t* i_file)
{ file_3dPoints = readFile3dObjectFromDisk(i_file) }
int GetNumPointsForSurface(int i_surf_index)
{ return file_3dPoints[i_surf_index].getNumPoints(); }
void GetPointsForSurface(double* o_result, int i_resultLength, int i_surf_index);
{
const int num_points = file_3dPoints[i_surf_index].getNumPoints();
if (num_points < i_resultLength)
return;
for (int i = 0; i < num_points; ++i)
o_result[i] = file_3dPoints[i_surf_index].getPoint(i);
}
client.cs:
IntPtr inst = LoadLibrary("mydll.dll");
InitFile3dPoints(filename);
for (int i = 0; i < n; ++i)
{
int num_points_for_surface = GetNumPointsForSurface(i);
double[] points = new double[num_points_for_surface];
GetPointsForSurface(points, points.Length, i);
// some code
}
FreeLibrary(inst);
My dll is not thread-safe. One thread can call InitFile3dPoints. And before calling GetPointsForSurface another thread can call InitFile3dPoints.
Could you plase advise me how do make it thread-safe? Creating mutex for accessing file_3dPoints will not solve the problem, I need every thread that comes in mydll.cpp to have its copy of file_3dPoints.
Thanks
There are many options to do that.
First and most important do not use global variables, they're a nightmare (for this and other reasons). Let's start to change InitFile3dPoints signature to allocate the required memory and to return it to the caller (so the address can be used as "handle"):
File3dPoints* InitFile3dPoints(const wchar_t* i_file)
{
return readFile3dObjectFromDisk(i_file);
}
Please note that readFile3dObjectFromDisk must return a heap allocated object of type File3dPoints.
Then change each function to accept that pointer:
int GetNumPointsForSurface(const File3dPoints* data, int i_surf_index)
{
return *data[i_surf_index].getNumPoints();
}
File3dPoints* can be marshaled in C# with IntPtr:
IntPtr inst = LoadLibrary("mydll.dll");
IntPtr data = InitFile3dPoints(filename);
Finally do not forget to add a DisposeFile3dPoints function where you deallocate data:
void DisposeFile3dPoints(File3dPoints* data)
{
if (data != NULL)
delete data;
}
In general to make a DLL "thread-safe" (according to the context of your question) you should make each function self-contained (all the data it needs comes from its parameters, it doesn't have any local static variable nor global ones). Please note that this doesn't make it really thread-safe in any wider sense (if, for example, you'll expose a write function to that data then you'll still need to protect access but it can be done more easily in the C# side).
You can do even better wrapping all these functions inside a C# class, users won't even see that:
public class File3D : IDisposable
{
public File3D(string path)
{
// Initialize. Call InitFile3dPoints
}
~File3D()
{
// Call Dispose(false)
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private IntPtr _data;
private void Dispose(bool disposing)
{
// Unmanaged resource, ignore disposing parameter
// and call DisposeFile3dPoints
}
}
Protect access to global variables using a mutex. Better yet, don't use global variables.
Reorganize your code so that InitFile3dPoint() returns a File3dPoints that you then pass to the other functions as context. The typical approach is to return a pointer to a dynamically allocated instance. You then have to take care of proper ownership and deallocation management, but you make the DLL itself thread-safe even without any mutex.

How to specify whether to take ownership of the marshalled string or not?

suppose I have x.dll in C++ which looks like this
MYDLLEXPORT
const char* f1()
{
return "Hello";
}
MYDLLEXPORT
const char* f2()
{
char* p = new char[20];
strcpy(p, "Hello");
return p;
}
Now, suppose I want to use this in C#
[DllImport("x.dll")]
public static extern string f1();
[DllImport("x.dll")]
public static extern string f2();
Is there any way to tell CLR to take strong ownership of the string returned from f2, but not f1? The thing is that the fact that the string returned from f1 will eventually be freed, deleted, or whatever by GC is equally bad with the fact that the string returned from f2 won't. Hope the question was clear. Thanks in advance
If you have any influence at all over the dll implementation, then I strongly suggest you simply don't do it like you showed in your example. Otherwise, please refine the question to mention that constraint.
If you have to return a heap allocated string from the dll, then you should also provide a cleanup function (always good practice when exporting dynamically allocated memory from a dll). You P/Invoke the allocating function with a return of IntPtr and marshal that with one of the Marshal.PtrToString... at http://msdn.microsoft.com/en-us/library/atxe881w.aspx and finish off by calling the cleanup function for the native side of things.
Another way is to use BSTR (example from Marshaling BSTRs in COM/Interop or P/Invoke):
Native:
__declspec(dllexport)
void bstrtest(BSTR *x)
{
*x = SysAllocString(L"Something");
}
Managed:
[DllImport("mydll.dll")]
extern static void bstrtest(ref IntPtr dummy);
static void Main(string[] args)
{
var bstr = IntPtr.Zero;
bstrtest(ref bstr);
var text = Marshal.PtrToStringBSTR(bstr);
Console.WriteLine(text);
Marshal.FreeBSTR(bstr);
}
I just found a similar question on SO: PInvoke for C function that returns char *

Categories