Reputation: 963
I'm trying to call native function from managed code in order to read a return value which I pass as array sbyte argument. However, I cannot read any thing. The array sbyte is always the same and it isn't never modified by native function.
The following is the C++ function signature:
void __cdecl NativeFunction(int8_t StatusFlagOut[], int32_t *len);
and this other one is my C# Wrapper:
[DllImport("NativeDll.dll")]
internal static extern void NativeFunction([Out] sbyte[] statusFlagOut, ref int len);
Finally, this is the way I use to call function:
sbyte[] output = null;
int len = 0;
NativeFunction(output, ref len); // while len is being filled properly, output never being changed.
Upvotes: 1
Views: 1971
Reputation: 963
I've solved. You was right, there was a bug in native function.
However I also have to be add the calling convention specification like you suggeted me, so finally signature wrapper would appears like following:
[DllImport("NativeDll.dll", CallingConvention=CallingConvention.Cdecl)]
internal static extern void NativeFunction(
[Out] sbyte[] statusFlagOut,
ref int len
);
Thank you very much.
Upvotes: 0
Reputation: 612794
The function is cdecl and so you need to specify that in the p/invoke:
[DllImport("NativeDll.dll", CallingConvention=CallingConvention.Cdecl)]
internal static extern void NativeFunction(
[Out] sbyte[] statusFlagOut,
ref int len
);
The big problem is that you are not allocating memory for the native function to populate. It seems that you are expecting the native code to allocate the memory. That's clearly not how it has been designed, and rightly so. You always want caller to allocate memory for a host of reasons that I won't go in to here.
It looks like the native function is helpfully telling you how much to allocate. So it seems you can write the code like this:
int len = 0;
NativeFunction(null, ref len);
sbyte[] output = new sbyte[len];
NativeFunction(output, ref len);
Finally, a more general point. This is an interop question. They invariably require complete knowledge of both sides of the binary interop interface. You did not provide full details of the C++ side. You provided a function signature but did not provide the semantics of that function. So, I took an educated guess. It's really important to include these details, just in case our guesswork skills fail!
Upvotes: 2
Reputation: 3833
When calling a native function with the cdecl
calling convention, you should explicitly specify it:
[DllImport("NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
If you don't, the CLR will by default interpret it as a stdcall
, and expects the callee to clean the stack, where in the case of cdecl
, the caller does, and bad stuff will happen.
As mentioned in the comments, arrays
are kind of special when working with platform invokes, as they're passed by value. The following is described on MSDN on the [Out]
page:
For example, arrays passed by value, marshaled as In-only parameters by default, can be changed to Out-only. However, the behavior does not always provide expected semantics when the types include all-blittable elements or fields because the interop marshaler uses pinning. If you do not care about passing data into the callee, Out-only marshaling can provide better performance for non-blittable types. Combining the InAttribute and OutAttribute is particularly useful when applied to arrays and formatted, non-blittable types. Callers see the changes a callee makes to these types only when you apply both attributes. Since these types require copying during marshaling, you can use InAttribute and OutAttribute to reduce unnecessary copies.
Which means that your final signature should look like this:
[DllImport("NativeDll.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern void
NativeFunction([In, Out] sbyte[] statusFlagOut, ref int len);
Upvotes: 0