Reputation: 381
I'm new to Interop and need to call a managed C++ method from C# which returns an instance of the following struct
:
typedef struct DataBlock_ {
unsigned char data[10240];
unsigned int numberOfBytes;
unsigned long int startAddr;
} DataBlock;
The C++ method which returns the instance is declared as follows:
__declspec(dllexport) DataBlock getDefaultPass( void )
{
DataBlock default_pass = {
{
(char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
(char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
(char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
(char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
(char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
(char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
(char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF,
(char)0xFF,(char)0xFF,(char)0xFF,(char)0xFF
},
32,
0xFFE0
};
return default_pass;
}
I've declared the struct and method in C# as follows:
public static partial class My
{
[StructLayout(LayoutKind.Sequential)]
public struct DataBlock
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10240)]
public byte[] data;
//public fixed byte data[10240]; <-- this requires 'unsafe' but still doesn't work
public UInt32 numberOfBytes;
public UInt32 startAddr;
}
[DllImport("my.dll")]
static public extern DataBlock getDefaultPass( );
[DllImport("my.dll")]
static public extern byte sendPassword(DataBlock data);
}
I call the method from C# as follows:
var defaultPassword = My.getDefaultPass();
var response = My.sendPassword(defaultPassword);
but the call to getDefaultPass()
throws
An unhandled exception of type 'System.Runtime.InteropServices.MarshalDirectiveException' occurred in ConsoleApplication1.exe
Additional information: Method's type signature is not PInvoke compatible.
Based on this question, I tried changing the declaration of data
to public fixed byte data[10240]
and marked the struct as unsafe
, but then the method returns an instance with numberOfBytes
and startAddr
set to 0, and the subsequent call to sendPassword()
fails (note that in this answer the subsequent calls use a pointer to the struct as opposed to the instance itself, as is my case). How then should I be calling the method(s) from C#?
The project targets .NET 3.5 and x86.
Thanks in advance for any help.
Upvotes: 0
Views: 157
Reputation: 381
For the sake of completeness and to complement Luaan's answer, since the C++ method in the question has no parameters, I wanted to cover the case where a method did have parameters, specifically as it relates to the position of the out
parameter when the method takes 2 or more arguments.
Consider the C++ method
__declspec(dllexport) DataBlock readText(char * dataArray , int bytesToRead)
It's not immediately obvious whether the out
parameter should be first or last in the C# method. Contrary to the framework's convention of placing the out
parameter as the last parameter (e.g. TryParse
), here it must be the first parameter, otherwise the call will fail:
[DllImport("my.dll", CallingConvention = CallingConvention.Cdecl)]
static public extern void readText(out DataBlock dataBlock, string dataArray, int bytesToRead);
Upvotes: 0
Reputation: 63752
The struct
is fine - it fulfills all the rules to be used as a return value in P/Invoke.
You need to use the proper calling convention (in your case, CallingConvention.Cdecl
).
There's also an extra optimization that some compilers use, where a big structure (such as yours) is passed by reference, rather than returned. You can replicate this in C# like so:
static public extern void getDefaultPass(out DataBlock data);
Upvotes: 1