Create empty native C++ byte* in C# to PInvoke with - c#

I need to create a binary blob of empty data to PInvoke a native C++ dll that needs a unsigned char* of nulls.
The native C++ program is expecting a structure of data, and there's a nulled area of bytes in the middle, but in C# I can't just make a struct with an initialized byte[] in the middle.
My struct in C++ looks like this
struct myStruct
{
byte command;
byte returncode;
void* Source (a pointer to a string, rather a null term char*)
void* Destination (same thing)
byte filler [99]
byte options;
}
I've already figured that I can take a string and convert it to an array of bytes using myStruct.Source = (void*)Marshal.StringtToGlobalAnsi(source) (correct me if I'm wrong).
But I don't know how to fill out that empty array of bytes in the middle.
This is my C# struct so far.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct Trans_s
{
public byte command;
public byte returnCode;
public void* pSource;
public void* pDest;
public byte* filler;
public byte options;
}

For the array of bytes, you need to mark the field in the C# struct as a byvalarray. The default marshaller uses LPArray if you do not.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 99)]
public byte[] filler;
You need to allocate the memory for filler when you create an instance of the struct.

No, the C++ code doesn't expect an unsigned char* of nulls. It expects that many padding bytes inside the struct.
The C# fixed keyword might help you here.

Related

Having trouble marshalling an array of structs from c to c#

I'm trying to marshal an array of c structs into C# (Using Unity) but, no matter the method I use, I always get an exception or a crash.
I'm loading dlls (libretro cores) that conform (or should...) to the Libretro API, the c/c++ side is not available to me (more precisely, not allowed to be modified by me), which means I have to handle the data I get back from that dll no matter how it is laid out.
The C API structs are defined as follow (RETRO_NUM_CORE_OPTION_VALUES_MAX is a constant with a value of 128):
struct retro_core_option_value
{
const char *value;
const char *label;
};
struct retro_core_option_definition
{
const char *key;
const char *desc;
const char *info;
struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX];
const char *default_value;
};
struct retro_core_options_intl
{
struct retro_core_option_definition *us;
struct retro_core_option_definition *local;
};
My C# mappings look like this at the moment:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct retro_core_option_value
{
public char* value;
public char* label;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct retro_core_option_definition
{
public char* key;
public char* desc;
public char* info;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = RETRO_NUM_CORE_OPTION_VALUES_MAX)]
public retro_core_option_value[] values;
public char* default_value;
}
[StructLayout(LayoutKind.Sequential)]
public struct retro_core_options_intl
{
public IntPtr us;
public IntPtr local;
}
The callback function has the following signature on the C# side:
public unsafe bool Callback(retro_environment cmd, void* data)
retro_environment is an unsigned int converted to an enum, a switch is performed on it and then dictates how to handle the void* data pointer appropriately. Here data is a retro_core_options_intl*.
I'm able to do the void* conversion in 2 ways:
retro_core_options_intl intl = Marshal.PtrToStructure<retro_core_options_intl>((IntPtr)data);
or
retro_core_options_intl* intl = (retro_core_options_intl*)data;
I get a readable address with both approaches (intl.us for the first and intl->us for the second), the "local" part is empty in my particular case but the "us" part is defined as mandatory by the API. intl->us points to an array of retro_core_option_definition of variable length.
The issue I'm having is trying to read the values inside of this mandatory construct.
The array I'm trying to load right now can be seen here: https://github.com/visualboyadvance-m/visualboyadvance-m/blob/master/src/libretro/libretro_core_options.h at line 51.
The API defines a fixed size for the "struct retro_core_option_value values[RETRO_NUM_CORE_OPTION_VALUES_MAX]" struct member, but code that comes in is almost always defined as an array where the last element is "{ NULL, NULL }" to indicate the end, so they don't always (almost never) contain 128 values.
I tried:
retro_core_options_intl intl = Marshal.PtrToStructure<retro_core_options_intl>((IntPtr)data);
retro_core_option_definition us = Marshal.PtrToStructure<retro_core_option_definition>(intl.us);
This gives a NullReferenceException.
retro_core_options_intl intl = Marshal.PtrToStructure<retro_core_options_intl>((IntPtr)data);
retro_core_option_definition[] us = Marshal.PtrToStructure<retro_core_option_definition[]>(intl.us);
This gives a retro_core_option_definition array of 0 length.
retro_core_options_intl intl = Marshal.PtrToStructure<retro_core_options_intl>((IntPtr)data);
retro_core_option_definition us = new retro_core_option_definition();
Marshal.PtrToStructure(intl.us, us);
This gives a "destination is a boxed value".
That's basically where I'm at...
Any help would be much appreciated :)
The entire codebase can be found here: https://github.com/Skurdt/LibretroUnityFE
First thing I see is that you either need to use wchar_t types instead of char types in you C code, or you can use byte instead of char in C#. System.Char in C# is two bytes. char in C code is 1 byte.
You can also use System.String in the C# code and annotate it with a MarshalAs attribute to tell it what type of char data is coming in, such as Ansi or Unicode C strings.

Issues in structure conversion from C++ to C# .Net Compact Framework

I am converting c/c++ structures into C# standards.
C/C++ Structures:
typedef struct _sta_conn_info{
STA_CONNECT_STATE connect_state;//Enum
STA_ASSOC_STATE assoc_state;//Enum
unsigned char bssid[6];
unsigned char ssid[34];
unsigned long channel;
enum mode mode;//Enum
unsigned long signalStrength;
unsigned long noiseLevel;
STA_AUTH_ALG auth_alg;//enum
STA_ENCRYPT_ALG encrypt_alg;//enum
}STA_CONN_INFO;
typedef struct _NDISUIO_QUERY_OID
{
NDIS_OID Oid;
PTCHAR ptcDeviceName;
UCHAR Data[sizeof(ULONG)];
} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
Respective C# structures:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _sta_conn_info
{
public _sta_connect_state connect_state;
public _sta_assoc_state assoc_state;
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 6)]
public char[] bssid ;//= new char[6];
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 34)]
public char[] ssid ;//= new char[34]
public uint channel;
public mode mode;
public uint signalStrength;
public uint noiseLevel;
public _sta_auth_alg auth_alg;
public _sta_encrypt_alg encrypt_alg;
}
QUERY STRUCT:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _NDISUIO_QUERY_OID
{
public uint Oid;
[MarshalAs(UnmanagedType.LPWStr)]
public string ptcDeviceName;
public byte[] Data;
};
I converted the data types using this >>>reference
Marshal.SizeOf() is working in WIN CE. I tested it.
If my structure conversion is fine then definitely Marshal.SizeOf() will work to get the size of the structure, but it is throwing exceptions and returning error code 87 in DeviceIoControl() API.
Can anyone clarify me about the conversions and let me know If I did anything wrong.
For bssid and ssid the C++ declarations are:
unsigned char bssid[6];
unsigned char ssid[34];
Now, unsigned char is a single byte and is typically used for byte arrays rather than text. So the C# should be:
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 6)]
public byte[] bssid ;//= new byte[6];
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 34)]
public byte[] ssid ;//= new byte[34]
Your use of char in the C# is not correct because char is two bytes wide in C#.
In _NDISUIO_QUERY_OID where you have
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = sizeof(uint))]
public byte[] Data;
I believe that you need to use ByValArray rather than ByValTStr. But as we have discussed in many of your recent questions, the exact meaning of this member is unclear. Is it really a fixed length byte array, or is it a variable length buffer? Do you have sample C++ code that works? That would settle the debate once and for all.
OK, from the header nuiouser.h header file I have this:
//
// Structure to go with IOCTL_NDISUIO_QUERY_OID_VALUE.
// The Data part is of variable length, determined by
// the input buffer length passed to DeviceIoControl.
//
typedef struct _NDISUIO_QUERY_OID
{
NDIS_OID Oid;
#ifdef UNDER_CE
//
// In CE land app is allowed to query without having to do
// IOCTL_NDISUIO_OPEN_DEVICE
// Hence the device name to query argument needed..
// For app that does IOCTL_NDISUIO_OPEN_DEVICE this argument
// is then not necessary..
//
PTCHAR ptcDeviceName;
#endif
UCHAR Data[sizeof(ULONG)];
} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
Which tells you conclusively that Data is variable length. You'll need to allocate the struct with AllocHGlobal and do all the marshalling by hand I am afraid.

Convert IntPtr to byte[] c#

I receive IntPtr value from C++ library method and i need to get byte[] array from this received IntPtr. When I try do this:
byte[] myArray = new byte[Marshal.SizeOf(myReceivedIntPtr)];
Marshal.Copy(myReceivedIntPtr, myArray, 0, Marshal.SizeOf(myReceivedIntPtr));
I receive an exception: AccessViolationException.
What I missed?
EDIT:
Here is Method name and params in header of C++ library:
int MSR_DecodeTrack(char *AscBuff, unsigned char *BinBuff, unsigned char bpc, unsigned char parity, unsigned char ss, unsigned char es);
And I call it from C# this way:
UInt32 result;
IntPtr myReceivedIntPtr;
IntPtr BinBuff;
byte BPC1 = 7, Parity1 = 1, SS1 = 0x05, ES1 = 0x1F;
result = MSR_DecodeTrack(ref myReceivedIntPtr, ref BinBuff, BPC1, Parity1, SS1, ES1);
And here is else code:
byte[] myArray = new byte[Marshal.SizeOf(myReceivedIntPtr)];
Marshal.Copy(myReceivedIntPtr, myArray, 0, Marshal.SizeOf(myReceivedIntPtr));
I'm pretty sure Marshal.SizeOf(myReceivedIntPtr) will return the size of the IntPtr object, not the size of the unmanaged array. You will need to get the size of the unmanaged array from the c++ library.
I haven't had to do this in a while so I'm a bit rusty but from looking at the c++ headers I think it is expecting a pointer to a buffer. You are giving it a pointer to a pointer (intptr is a pointer and passing it by ref means you are passing the c++ code a pointer to that pointer). Firstly, don't use ref. Secondly, I suspect you are supposed to be passing it an already sized buffer - is there any documentation describing what you are supposed to pass to the function? What is the return code from the function? You are checking it for success aren't you? ;)
The method named MSR_DecodeTrack should also return the length of each array it allocates.
As far as I know, when you allocate an array in C/C++ environment, you get a pointer to the zero element of that array, nothing more. For that reason, you should also somehow return the length of the allocated array back to the caller. It can be done by an additional "ref" parameter.
HTH.

C++ <--> C# modify a marshalled array of bytes

I have an unmanaged C++ function which is calling a managed C# method in a DLL. The purpose of the C# method is to take an array of bytes (allocated by the C++ caller), populate the array, and return it. I can get the array INTO the C# method, but the populated data are lost when they get back to the C++ function. Right now, this is my test code to debug the process:
C# DLL Method:
// Take an array of bytes and modify it
public ushort GetBytesFromBlaster([MarshalAs(UnmanagedType.LPArray)] byte[] dataBytes)
{
dataBytes[0] = (byte)'a';
dataBytes[1] = (byte)'b';
dataBytes[2] = (byte)'c';
return 3;
}
C++ function which calls the DLL:
// bytes[] has been already allocated by its caller
short int SimGetBytesP2P(unsigned char bytes[])
{
unsigned short int numBytes = 0;
bytes[0] = 'x';
bytes[1] = 'y';
bytes[2] = 'z';
// bytes[] are {'x', 'y', 'z'} here
guiPtr->GetBytesFromBlaster(bytes, &numBytes);
// bytes[] SHOULD be {'a', 'b', 'c'} here, but they are still {'x', 'y', 'z'}
return(numBytes);
}
I believe it has something to do with C# turning the C++ pointer into a new managed array, but modifying the original one. I have tried several variations using the "ref" modifyer, etc., but no luck. Also, these data are NOT null-terminated strings; the date bytes are raw 1-byte values, not null-terminated.
Can anyone please shed some light on this? Thanks!
Stuart
You could do the marshaling yourself. Have the C# function accept a parameter by value of type IntPtr. Also a second parameter indicating array length. No special marshaling attributes are needed or wanted.
Then, use Marshal.Copy and copy the array from the unmanaged pointer to a managed byte[] array that you allocated. Do your thing, and then when you're done, use Marshal.Copy to copy it back to the C++ unmanaged array.
These particular overloads should get you started:
http://msdn.microsoft.com/en-us/library/ms146625.aspx
http://msdn.microsoft.com/en-us/library/ms146631.aspx
For example:
public ushort GetBytesFromBlaster(IntPtr dataBytes, int arraySize)
{
byte[] managed = new byte[arraySize];
Marshal.Copy(dataBytes, managed, 0, arraySize);
managed[0] = (byte)'a';
managed[1] = (byte)'b';
managed[2] = (byte)'c';
Marshal.Copy(managed, 0, dataBytes, arraySize);
return 3;
}
Alternatively you could implement a custom marshaller as described in http://msdn.microsoft.com/en-us/library/w22x2hw6.aspx if the default one isn't doing what you need it to. But that looks like more work.
I believe that you just need to add a SizeConst attribute:
public ushort GetBytesFromBlaster(
[MarshalAs(UnmanagedType.LPArray, SizeConst=3)]
byte[] dataBytes
)
and the default marshaller should do the rest for you.

Interop Structure: Should Unsigned Short be Mapped to byte[]?

I have such a C++ structure:
typedef struct _FILE_OP_BLOCK
{
unsigned short fid; // objective file ID
unsigned short offset; // operating offset
unsigned char len; // buffer length(update)
// read length(read)
unsigned char buff[240];
} FILE_OP_BLOCK;
And now I want to map it in .Net. The tricky thing is that the I should pass a 2 byte array for fid, and integer for len, even though in C# fid is an unsigned short and len is an unsigned char
I wonder whether my structure ( in C#) below is correct?
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Auto)]
public struct File_OP_Block
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] fid;
public ushort offset;
public byte length;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 240)]
public char[] buff;
}
Your CharSet property on the [DllImport] attribute is definitely wrong, you need CharSet.Ansi to get the P/Invoke marshaller to convert it to a char[]. Declare the buff member as a string for easier usage. While declaring the fid member as a byte[] isn't wrong, I really don't see the point of it. That the unmanaged code copies a char[] into it is an implementation detail. Thus:
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct File_OP_Block
{
public ushort fid;
public ushort offset;
public byte length;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 240)]
public string buff;
}
Given that the C++ short data type is actually two bytes, a two byte array should work. The integer sizes in C/C++ are not strictly defined, so the standard only says that a short is at least two bytes.
The C# char data type is a 16 bit unicode character, so that doesn't match the C++ char data type which is an 8 bit data type. You either need an attribute to specify how the characters are encoded into bytes, or use a byte array.
You might need an attribute to specify the packing, so that there is no padding between the members.

Categories