Parameters Passed by Reference Come Back Garbage Using P/Invoke

receiving garbage values for pinvoke structure

Arrays in structs cannot be marshalled as UnmanagedType.LPArray, which is what you would need for these members to reach your unmanaged code.

As you probably know, the arrays are marshalled correctly when they appear as parameters to the function. If you must pass these arrays inside a struct, then you will need to declare the members as IntPtr in the C# struct declaration, and write the marshalling code manually. That's not very difficult for the integer array, it's just a case of pinning the array. For the string array then there is more work. You need an array of IntPtr which you then populate with calls to Marshal.StringToCoTaskMemAnsi.

It looks something like this:

var intHandle = GCHandle.Alloc(intArr, GCHandleType.Pinned);
struc.intArr = intHandle.AddrOfPinnedObject();

var strPtrArr = new IntPtr[strArr.Length];
for (int i = 0; i < strPtrArr.Length; i++)
strPtrArr[i] = Marshal.StringToCoTaskMemAnsi(strArr[i]);
var strHandle = GCHandle.Alloc(strPtrArr, GCHandleType.Pinned);
struc.strArr = strHandle.AddrOfPinnedObject();

testStructCall(intArr, strArr, ref struc);

intHandle.Free();
strHandle.Free();
for (int i = 0; i < strPtrArr.Length; i++)
Marshal.FreeCoTaskMem(strPtrArr[i]);

Issue with API with char* return being caught by IntPtr in P/invoke

Thanks to David, i was able to fix my problem. Since the return value of the function is a pointer to the basebuf parameter, it acquires garbage value when the function returns.

The basebuf should be an IntPtr not a string for the IntPtr return to get the correct value after the call. So it should be P/Invoked like this.

C# P/Invoke

[DllImport("myDll.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] 
internal static extern IntPtr MY_API(IntPtr basebuf,
[MarshalAs(UnmanagedType.LPArray), Out] byte[] strbuf);

C# Wrapper

public string MY_API_WRAPPER(string basebuf, out string strbuf)
{
byte[] strbufTemp = new byte[256];
IntPtr basebufPtr = Marshal.StringtoHGlobalAnsi(basebuf)
IntPtr ret = MY_API(basebufPtr , strbufTemp);
string retstr = Marshal.PtrToStringAnsi(ret);
strbuf = MyDataConverter.ByteToString(strbufTemp);
Marshal.FreeHGlobal(basebufPtr);
return retstr;
}

How to pass an unsigned long to a Linux shared library using P/Invoke

From here:

An unsigned long can hold all the values between 0 and ULONG_MAX inclusive. ULONG_MAX must be at least 4294967295. The long types must contain at least 32 bits to hold the required range of values.

For this reason a C unsigned long is usually translated to a .NET UInt32:

[DllImport("libaiousb")]
extern static uint AIOUSB_Init();

Delegate on instance method passed by P/Invoke

Does this mean that any such delegate is translated (marshalled?) individually to a static function...

Yes, you guessed at this correctly. Not exactly a "static function", there is a mountain of code inside the CLR that performs this magic. It auto-generates machine code for a thunk that adapts the call from native code to managed code. The native code gets a function pointer to that thunk. The argument values may have to be converted, a standard pinvoke marshaller duty. And always shuffled around to match the call to the managed method. Digging up the stored delegate's Target property to provide this is part of that. And it jiggers the stack frame, tying a link to the previous managed frame, so the GC can see that it again needs to look for object roots.

There is however one nasty little detail that gets just about everybody in trouble. These thunks are automatically cleaned-up again when the callback is not necessary anymore. The CLR gets no help from the native code to determine this, it happens when the delegate object gets garbage-collected. Maybe you smell the rat, what determines in your program when that happens?

 var myFunctor = new MySystemFunctor();

That is a local variable of a method. It is not going to survive for very long, the next collection will destroy it. Bad news if the native code keeps making callbacks through the thunk, it won't be around anymore and that's a hard crash. Not so easy to see when you are experimenting with code since it takes a while.

You have to ensure this can't happen. Storing the delegate objects in your class might work, but then you have to make sure your class object survives long enough. Whatever it takes, no guess from the snippet. It tends to solve itself when you also ensure that you unregister these callbacks again since that requires storing the object reference for use later. You can also store them in a static variable or use GCHandle.Alloc(), but that of course loses the benefit of having an instance callback in a hurry. Feel good about having this done correctly by testing it, call GC.Collect() in the caller.

Worth noting is that you did it right by new-ing the delegate explicitly. C# syntax sugar does not require that, makes it harder to get this right. If the callbacks only occur while you make the pinvoke call into the native code, not uncommon (like EnumWindows), then you don't have to worry about it since the pinvoke marshaller ensures the delegate object stays referenced.

Storing data for unmanaged code when using P/Invoke

If the C# code does not need to know the internals of the array and the structure, don't expose it to the C# code. Do all the work on this type in the unmanaged code and avoid marshalling overhead.

Essentially, you want to follow this basic pattern. I'm sure the details will differ, but this should give you the basic concept.

C++

MyStruct* newArray(const int len)
{
return new MyStruct[len];
}

void workOnArray(MyStruct* array, const int len)
{
// do stuff with the array
}

void deleteArray(const MyStruct* array)
{
delete[] array;
}

C#

[DllImport(dllname)]
static extern IntPtr newArray(int len);

[DllImport(dllname)]
static extern void workOnArray(IntPtr array int len);

[DllImport(dllname)]
static extern void deleteArray(IntPtr array);

Using ref insted of fixed pointers for P/Invoke

Your code (with some small modifications) will work for passing pointers to an array from managed code to native code. It won't work in the opposite direction, because native code doesn't know how to allocate a managed array (float[]) so you can't expect to treat it as one on the managed side.

But instead of passing a raw float * back, you can type it as returning an IntPtr, and use Marshal.Copy to extract the data.

Putting this together, the new P/Invoke signature and code will look like:

[DllImport(...)]
internal static extern int doSomething(int arraySize, byte[] arrayPointer,
out int outputInteger, out int outputArraySize,
out IntPtr outputArrayPointer);

[DllImport(...)]
internal static extern void freePointer(IntPtr pointer);

public static int DoSomething(byte[] inputArray, out int outputInteger, out float[] outputArray)
{
outputArray = null;
int outputArraySize;
IntPtr outputArrayPointer;

int result = doSomething(inputArray.Length, inputArray, out outputInteger, out outputArraySize, out outputArrayPointer);

if (result == 0)
{
outputArray = new float[outputArraySize];
Marshal.Copy(outputArrayPointer, outputArray, 0, outputArraySize);

freePointer(outputArrayPointer);
}

return result;
}

When a P/Invoke signature is written as taking an array, the marshalling layer automatically pins the array in memory (so it can't be freed or moved, making it safe to pass it to native code), then passes a pointer to the first element in the array to the native code. By contrast, ref inputArray passes a pointer to the managed array object (which is a different location in memory).

These changes will let you eliminate the fixed (byte* arrayPointer from your calling code, and you can use an IntPtr for the native code to pass the data back to you. Because the native code allocated the memory, the .NET Garbage Collector doesn't know how to free it, so calling freePointer after you've copied the data is very important. It will know how much data to free because the allocation routine that allocated the memory will have stored the size of the allocated block somewhere (often in a header block before the allocated memory); this means you don't need to pass the array size to freePointer.

P/Invoke g++ from mono in ubuntu OS

You need to compile the library as a shared library: a static library can't be loaded at runtime with P/Invoke.
The fact that you added a main() function suggests that you're compiling the code into an executable instead.
So the first thing for you is to learn how to compile a shared library, you can try something like:

gcc -shared -o libtestcpp.so testcpp.cpp

Then change the DllImport name to the path to the complete library name:

DllImport("/home/yourlogin/MonoCsTest/bin/Debug/libtestcpp.so")

The other mistake you made is not considering the C++ manadated name mangling: the simpler solution here is to export Sum() as a C function surrounding it with extern "C" {}.

To diagnose such mistakes it is often useful to enable the debug logging from mono using:

MONO_LOG_LEVEL="debug" MONO_LOG_MASK="dll" mono yourprog.exe

Why is a delegate function reference garbage collected when passed as parameter?

Remember that, in general, the system has no knowledge of which native functions you're going to P/Invoke into. For most circumstances, so long as the function pointer that you're passing into a native function is only (possibly) used whilst that function call is in progress, the .NET system already does enough to ensure that the function can be called.

But in your circumstances, you're passing a function pointer across that is going to be invoked at other points in time, well after SetWindowsHookEx has returned. In such a circumstance, it's your responsibility to ensure that the delegate stays alive for long enough.

And, by the look of things, you've already found a way to achieve that.


The alternatives would be a) Any delegate passed to any native function is never garbage collected, creating a possible memory leak, or b) The system would have to provide a mechanism for your code to indicate when it knows that the delegate is no longer required. But such a mechanism would still require the co-operation of your code, more-so than just having you manage the lifetime of the managed object.



Related Topics



Leave a reply



Submit