I am working on a VS 2015 MVC C# web application that loads a 3rd party C++ DLL I do not have source code for. The DLL requires an input param. The new spec requires a couple of array members. One is an int array and the other is a char array.
My C# struct defines the intended char array as byte to match the 8-bit C++ char:
[StructLayout(LayoutKind.Sequential)]
public unsafe struct MyDLLInput
{
public fixed int SomeList[288];
public fixed byte PathToData[256];
};
The struct seems correct to me, but now I need to set values and I'm not having any success.
MyDLLInput dllInput = new MyDLLInput()
{
SomeList = new int[] {0,12,33,67,93},
PathToData = "C:\\some\\path\\to\\data"
}
// Call 3rd Party DLL
MyDLLOutput = MyDLL.EntryPoint(MyDLLInput);
For both member arrays I am getting the following error:
Fixed size buffers can only be accessed through locals or fields.
There are at least a couple of things going on here - aside from the proper way of setting the values using a local I also have to make an encoding conversion from string to byte[].
Can someone provide me with a code example of a clean way to set these values?
Is there some reason you're using an unsafe struct? Can you not use marshalling attributes? See https://msdn.microsoft.com/en-us/library/eshywdt7(v=vs.110).aspx
Regardless, you need to know how you're converting from a C# string to a byte array, and that depends on what encoding your C++ DLL expects that string to be in. For example, on Windows, it is often the "ANSI code page", but on Linux/Unix it might be either "current locale" or explicitly "UTF-8".
So, one option that gives you the most control over the encoding would be to do somethiing like:
[StructLayout(LayoutKind.Sequential)]
public struct MyDllInput
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 288)]
public int[] SomeList;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public byte[] PathToData;
}
public static void Main()
{
MyDllInput dllInput = new MyDllInput()
{
SomeList = new int[288],
PathToData = new byte[256]
};
var listData = new int[] { 0, 12, 33, 67, 93 };
Array.Copy(listData, dllInput.SomeList, listData.Length);
var pathToDataBytes = Encoding.UTF8.GetBytes("C:\\some\\path\\to\\data");
Array.Copy(pathToDataBytes, dllInput.PathToData, pathToDataBytes.Length);
}
Alternatively, instead of doing the encoding conversion directly, you can try declaring the PathToData as a string and then using a marshalling attribute to have C# convert it for you; see https://msdn.microsoft.com/en-us/library/s9ts558h(v=vs.110).aspx:
[StructLayout(LayoutKind.Sequential)]
public struct MyDllInput
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 288)]
public int[] SomeList;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst =256)]
public String PathToData;
}
public static void Main()
{
MyDllInput dllInput = new MyDllInput()
{
SomeList = new int[288],
PathToData = "C:\\some\\path\\to\\data"
};
var listData = new int[] { 0, 12, 33, 67, 93 };
Array.Copy(listData, dllInput.SomeList, listData.Length);
}
In the second case, it's important that when you declare EntryPoint you set the CharSet property on the DllImportAttribute to get the string conversion to happen the way you want. In your case, you probably want CharSet.Ansi since your DLL takes a char* and not a wchar_t*. For example,
[DllImport("MyDll.dll", CharSet = CharSet.Ansi)]
private static extern void EntryPoint(ref MyDllInput input);
Related
I have an unmanaged C/C++ library that I use to decode a variable sized buffer of bytes. These bytes constituent a complex datagram that is extracted from a SQLite BLOB. In the C/C++ code it is very easy to overlay a struct on top of he data using pointers. Is there anyway to do the same thing in C#? I am passing a ref to the Byte[] to the unmanaged side by using the Marshal class. I can also pass references to structures on the managed side that get filled in. I would to pull all this over to C#. Any thoughts, anyone?
TIA,
Doug
This answer assumes a few things:
Your structs only contain primitive value types (i.e. no arrays, no reference types, no decimal values, no string and so on).
You have an array of bytes containing one or more structs at various offsets.
You are using .Net Core 3.1 or later.
You are using C# 7.0 or later.
It's possible to "overlay" a struct over a section of a byte array so that you can access the data in the array via the struct without making additional copies of the data. To do this, you use Span<byte> and MemoryMarshal.AsRef<T>.
When doing this, the structure packing is important of course. You must specify the packing for the structs to match the packing in the byte array. This is exactly the same as you have to do when declaring structs for use with P/Invoke.
(Despite what some may claim, the StructLayoutAttribute.Pack setting does affect the layout of structs in memory; it's not just for P/Invoke.)
If you are using char values in your structs, you must also specify the CharSet as Unicode. If your source structs are not using UTF16 for the chars, (if they are using ANSI or ASCII) then you will need to declare those fields as byte rather than char because C# chars are always UTF16.
Once you've laid out your structs correctly and you have a byte[] array containing one or more structs you can overlay a struct over a section of the array as follows:
Create a Span<byte> using the Span<byte>(byte[] array, int start, int length) constructor to reference the subset of the array that corresponds to the bytes of the struct.
Use MemoryMarshal.AsRef<T> to reference the struct within the byte array. This references the struct directly in the byte array without copying any data.
The following program demonstrates this approach.
It defines three different structs, Demo1, Demo2 and Demo3 each with a different packing.
It implements a toByteArray() method, the only purpose of which is to convert the structs to a byte array with some spare bytes at the start and end. The implementation of this isn't important; you'd get your byte array from P/Invoke.
It also implements a helper method AsSpan(). This is quite a useful method; it converts any blittable (unmanaged) struct to a Span<byte>().
The interesting method is consumeStructs(). This overlays three different structs over the given byte[] array and then prints out their contents.
If you run this program, you will see that the output matches the struct initialisation in Main().
Note that the structs are declared as ref var. This means that no data is copied; the structs are actually referencing data directly in the data[] array passed to the method.
I demonstrate that this is the case by modifying one of the bytes in the data[] array that corresponds to the start of the Demo2 struct like so:
data[prefixByteCount + Marshal.SizeOf<Demo1>()] = (byte)'*';
After making that change and reprinting the struct, the output shows that the CharValue has changed from 0 to *. This demonstrates that the struct really does reference data directly in the data[] array.
Here's the compilable Console application:
(Try it online)
using System;
using System.Linq;
using System.Runtime.InteropServices;
namespace Demo
{
static class Program
{
static void Main()
{
var demo1 = new Demo1
{
BoolValue = true,
DoubleValue = -1
};
var demo2 = new Demo2
{
CharValue = '0',
ShortValue = 1,
IntValue = 2,
LongValue = 3,
FloatValue = 4,
DoubleValue = 5.4321
};
var demo3 = new Demo3
{
ByteValue = 128,
FloatValue = 1.23f
};
const int PREFIX_BYTE_COUNT = 29;
const int POSTFIX_BYTE_COUNT = 17;
var bytes = toByteArray(PREFIX_BYTE_COUNT, POSTFIX_BYTE_COUNT, ref demo1, ref demo2, ref demo3);
consumeStructs(PREFIX_BYTE_COUNT, bytes);
}
static byte[] toByteArray(int prefixByteCount, int postfixByteCount, ref Demo1 demo1, ref Demo2 demo2, ref Demo3 demo3)
{
var demo1Bytes = AsSpan(ref demo1);
var demo2Bytes = AsSpan(ref demo2);
var demo3Bytes = AsSpan(ref demo3);
var prefixBytes = Enumerable.Repeat((byte)0, prefixByteCount);
var postfixBytes = Enumerable.Repeat((byte)0, postfixByteCount);
return prefixBytes
.Concat(demo1Bytes.ToArray())
.Concat(demo2Bytes.ToArray())
.Concat(demo3Bytes.ToArray())
.Concat(postfixBytes)
.ToArray();
}
static void consumeStructs(int prefixByteCount, byte[] data)
{
var demo1Bytes = new Span<byte>(data, prefixByteCount, Marshal.SizeOf<Demo1>());
var demo2Bytes = new Span<byte>(data, prefixByteCount + Marshal.SizeOf<Demo1>(), Marshal.SizeOf<Demo2>());
var demo3Bytes = new Span<byte>(data, prefixByteCount + Marshal.SizeOf<Demo1>() + Marshal.SizeOf<Demo2>(), Marshal.SizeOf<Demo3>());
ref var demo1Ref = ref MemoryMarshal.AsRef<Demo1>(demo1Bytes);
ref var demo2Ref = ref MemoryMarshal.AsRef<Demo2>(demo2Bytes);
ref var demo3Ref = ref MemoryMarshal.AsRef<Demo3>(demo3Bytes);
Console.WriteLine(demo1Ref);
Console.WriteLine(demo2Ref);
Console.WriteLine(demo3Ref);
Console.WriteLine("Modifying first byte of Demo2 struct in byte buffer.");
data[prefixByteCount + Marshal.SizeOf<Demo1>()] = (byte)'*';
Console.WriteLine(demo2Ref);
}
public static Span<byte> AsSpan<T>(ref T val) where T : unmanaged
{
var valSpan = MemoryMarshal.CreateSpan(ref val, 1);
return MemoryMarshal.Cast<T, byte>(valSpan);
}
}
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct Demo1
{
public bool BoolValue;
public double DoubleValue;
public override string ToString()
{
return $"byte = {BoolValue}, double = {DoubleValue}";
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
public struct Demo2
{
public char CharValue;
public short ShortValue;
public int IntValue;
public long LongValue;
public float FloatValue;
public double DoubleValue;
public override string ToString()
{
return $"char = {CharValue}, short = {ShortValue}, int = {IntValue}, long = {LongValue}, float = {FloatValue}, double = {DoubleValue}";
}
}
[StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct Demo3
{
public byte ByteValue;
public float FloatValue;
public override string ToString()
{
return $"byte = {ByteValue}, float = {FloatValue}";
}
}
}
I've seen the attempted to read or write protected memory error before.
Typically the error shows up when I don't set up the c# struct correctly. I do have other calls working properly but this one is not co-operating.
I'm almost certain that it could be both my function call and the struct that is causing the problem.
C Syntax
int CardTransaction(pTRequest req, char *ProductCodes)
Request structure (I condensed it b/c there were repetitive data types)
typedef struct _cardRequest
{
unsigned short RedemptionNum
long TotalAmount;
unsigned char filler1[257];
char CardNumber[80];
unsigned char cardType;
} TRequest, *pTRequest;
C# function call
[DllImport("card.dll"), CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int CardTransaction(ref CardRequest cardRequest, [MarshalAs(UnManagedType.LPStr)] StringBuilder productCodes);
ProductCodes is null so I just instantiated a stringbuilder object with nothing in it and passed it through. This is one place I'm thinking could be a problem.
C# structure
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)]
public struct CardRequest
{
public uint16 RedemptionNum
public int TotalAmount;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)]
public string filler1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string CardNumber;
public byte cardType;
}
The obvious problem is that the C code uses an aligned struct, but for some reason you have elected to pack the C# struct. Remove the Pack = 1 from the C# code to make the two structures match.
Beyond that the filler array looks more like a byte array than a string. I'd declare it like this:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 257)]
public byte[] filler1;
If you want to pass null to the productCodes parameter, then I expect you can do just that. I cannot recall every doing that myself, but generally when you pass null to a p/invoke, then the marshaller will pass NULL to the native code.
I am trying to call an unmanaged C++ function, that has a structure as an input parameter.
The structure is defined in the header file like this:
struct MyStruct
{
int siOrder;
char aaszNames[6][25];
int siId[6];
int siTones[6];
};
I tried to declare the managed struct as following:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct MyStruct {
public int siOrder;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst=150)]
public string aaszNames;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=6, ArraySubType=UnmanagedType.I4)]
public int[] siId;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=6, ArraySubType=UnmanagedType.I4)]
public int[] siTones;
}
But without any success. I am guessing that the marshaling fails, since the aaszNames is actually an array of six 25 long null-terminating strings.
I tried declaring aaszNames as
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=150)]
public char[] aaszNames;
filling the array with nulls where necessary. But, again, nothing.
Is there something I am missing? What am I dong wrong? What is the best way to marshal this 2-D char array?
Any hints, please.
Try using multiple C# structs:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public struct MyStruct_Name
{
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 25)]
public string name;
}
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MyStruct
{
public int siOrder;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 6)]
public MyStruct_Name aaszNames;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 6, ArraySubType = UnmanagedType.I4)]
public int[] siId;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 6, ArraySubType = UnmanagedType.I4)]
public int[] siTones;
}
This is how I've been passing arrays of C-style strings around.
Don't forget to create the contents of aaszNames! The marshaller hates null references.
MyStruct foo = new MyStruct();
for (int i = 0; i < 6; i++)
{
foo.aaszNames[i] = new MyStruct_Name();
foo.aaszNames[i].name = "";
}
Good luck!
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=150)]
public char[] aaszNames;
That marshalling type looks well. Probably issue in function call, or bad memory allocation/
I would write a small c-program to check the byte size of the C-structure.
Then I would go with the other suggestion to extract the data field by field.
From a C standpoint the /0 is treated as normal character included in the 6 bytes whereas C# would use a length of 5 and have the /0 hidden.
char aaszNames[6][25];
char of C++ Type is 8 bits~
but char of C# Type is Unicode ,(16 bits) !
so char of C++ Type <-> byte of C# type
I hope someone can assist me with the problem I'm currently experiencing. We have a lot of Delphi legacy code, and need to convert some of our Delphi applications to C#.
The legacy code I'm currently struggling with is that of calling a function from a 3rd party application's non-COM DLL.
Here is the C-style header and struct used for the specific function:
/*** C Function AwdApiLookup ***/
extern BOOL APIENTRY AwdApiLookup( HWND hwndNotify, ULONG ulMsg,
BOOL fContainer, CHAR cObjectType,
SEARCH_CRITERIA* searchCriteria,
USHORT usCount, USHORT usSearchType,
VOID pReserved );
/*** C Struct SEARCH_CRITERIA ***/
typedef struct _search_criteria
{
UCHAR dataname[4];
UCHAR wildcard;
UCHAR comparator[2];
UCHAR datavalue[75];
} SEARCH_CRITERIA;
In our Delphi code, we have converted the above function and structure as:
(*** Delphi implementation of C Function AwdApiLookup ***)
function AwdApiLookup(hwndNotify: HWND; ulMsg: ULONG; fContainer: Boolean;
cObjectType: Char; pSearchCriteria: Pointer; usCount: USHORT;
usSearchType: USHORT; pReserved: Pointer): Boolean; stdcall;
external 'AWDAPI.dll';
(*** Delphi implementation of C Struct SEARCH_CRITERIA ***)
TSearch_Criteria = record
dataname: array [0..3] of char;
wildcard: char;
comparator: array [0..1] of char;
datavalue: array [0..74] of char;
end;
PSearch_Criteria = ^TSearch_Criteria;
and the way we call the above mentioned code in Delphi is:
AwdApiLookup(0, 0, true, searchType, #criteriaList_[0],
criteriaCount, AWD_USE_SQL, nil);
where criteriaList is defined as
criteriaList_: array of TSearch_Criteria;
After all that is said and done we can now look at the C# code, which I cannot get to work. I'm sure I'm doing something wrong here, or my C header is not translated correctly. My project does compile correctly, but when the function is called, I get a "FALSE" value back, which indicates that the function did not execute correctly in the DLL.
My C# code thus far:
/*** C# implementation of C Function AwdApiLookup ***/
DllImport("awdapi.dll", CharSet = CharSet.Auto)]
public static extern bool AwdApiLookup(IntPtr handle, ulong ulMsg,
bool fContainer, char cObjectType,
ref SearchCriteria pSearchCriteria,
ushort usCount, ushort usSearchType,
Pointer pReserverd);
/*** C# implementation of C Struct SEARCH_CRITERIA ***/
[StructLayout(LayoutKind.Sequential)]
public struct SearchCriteria
{
private readonly byte[] m_DataName;
private readonly byte[] m_Wildcard;
private readonly byte[] m_Comparator;
private readonly byte[] m_DataValue;
public SearchCriteria(string dataName, string comparator, string dataValue)
{
m_DataName = Encoding.Unicode.GetBytes(
dataName.PadRight(4, ' ').Substring(0, 4));
m_Wildcard = Encoding.Unicode.GetBytes("0");
m_Comparator = Encoding.Unicode.GetBytes(
comparator.PadRight(2, ' ').Substring(0, 2));
m_DataValue = Encoding.Unicode.GetBytes(
dataValue.PadRight(75, ' ').Substring(0, 75));
}
public byte[] dataname { get { return m_DataName; } }
public byte[] wildcard { get { return m_Wildcard; } }
public byte[] comparator { get { return m_Comparator; } }
public byte[] datavalue { get { return m_DataValue; } }
}
My C# call to the C# function looks like this
var callResult = UnsafeAwdApi.CallAwdApiLookup(IntPtr.Zero, 0, true, 'W',
ref searchCriteria[0], criteriaCount,
66, null);
where searchCriteria and criteriaCount is defined as
List<SearchCriteria> criteriaList = new List<SearchCriteria>();
var searchCriteria = criteriaList.ToArray();
var criteriaCount = (ushort)searchCriteria.Length;
and adding data to searchCriteria:
public void AddSearchCriteria(string dataName, string comparator, string dataValue)
{
var criteria = new SearchCriteria();
criteria.DataName = dataName;
criteria.Wildcard = "0";
criteria.Comparator = comparator;
criteria.DataValue = dataValue;
criteriaList.Add(criteria);
}
Like I said, my code compiles correctly, but when the function executes, it returns "FALSE", which should not be the case as the Delphi function does return data with the exact same input.
I know I'm definitely doing something wrong here, and I've tried a couple of things, but nothing seems to be working.
Any assistance or nudge in the right direction would be greatly appreciated.
Thanks, Riaan
Several things here.
First of all C++ ULONG is a 32-bit integer, and becomes uint in C# - ulong is 64-bit.
For the struct, you don't need to mess with byte arrays. Use strings, and ByValTStr. Also, it's not really worth bothering with readonly and properties for interop structs. Yes, mutable value types are generally bad in a pure .NET API, but in this case it's the existing API, there's no point in masking it. So:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SearchCriteria
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 4]
public string m_DataName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1]
public string m_Wildcard;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2]
public string m_Comparator;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 75]
public string m_DataValue;
}
If you really want to do all the string conversions yourself, it may be easier to just use unsafe and fixed-size arrays:
public unsafe struct SearchCriteria
{
public fixed byte m_DataName[4];
public byte m_Wildcard;
public fixed byte m_Comparator[2];
public fixed byte m_DataValue[75];
}
[EDIT] Two more things.
CHAR cObjectType should become byte cObjectType, and not char cObjectType that you currently use.
Also, yes, there is a problem with array marshaling in your example. Since your P/Invoke declaration is ref SearchCriteria pSearchCriteria - i.e. a single value passed by reference - that's precisely what P/Invoke mashaler will do. Keep in mind that, unless your struct only has fields of unmanaged types in it (the one with fixed arrays above is that, the one with string is not), the marshaler will have to copy the structs. It won't just pass address to the first element of the array directly. And in this case, since you didn't tell it it's an array there, it will only copy the single element you reference.
So, if you use the version of the struct with string, fields, you need to change the P/Invoke declaration. If you only need to pass SEARCH_CRITERIA objects into the function, but won't need to read data from them after it returns, just use an array:
public static extern bool AwdApiLookup(IntPtr handle, uint ulMsg,
bool fContainer, byte cObjectType,
SearchCriteria[] pSearchCriteria,
ushort usCount, ushort usSearchType,
Pointer pReserverd);
And call it like this:
var callResult = UnsafeAwdApi.CallAwdApiLookup(
IntPtr.Zero, 0, true, (byte)'W',
searchCriteria, criteriaCount,
66, null);
If function writes data into that array, and you need to read it, use [In, Out]:
[In, Out] SearchCriteria[] pSearchCriteria,
If you use the version with fixed byte[] arrays, you can also change the P/Invoke declaration to read SearchCriteria* pSearchCriteria, and then use:
fixed (SearchCriteria* p = &searchCriteria[0])
{
AwdApiLookup(..., p, ...);
}
This will require unsafe as well, though.
I am doing some C# interop work. I have the following struct:
#pragma pack(push,1)
typedef struct
{
unsigned __int64 Handle;
LinkType_t Type;
LinkState_t State;
unsigned __int64 Settings;
signed __int8 Name[MAX_LINK_NAME];
unsigned __int8 DeviceInfo[MAX_LINK_DEVINFO];
unsigned __int8 Reserved[40];
} LinkInfo_t;
This is my attempt to convert it into a C# struct:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct LinkInfo_t
{
[MarshalAs(UnmanagedType.U8)]
public UInt64 Handle;
[MarshalAs(UnmanagedType.I4)]
public LinkType_t Type;
[MarshalAs(UnmanagedType.I4)]
public LinkState_t State;
[MarshalAs(UnmanagedType.U8)]
public UInt64 Settings;
[MarshalAs(UnmanagedType.LPStr, SizeConst = MAX_LINK_NAME)]
public string Name;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_LINK_DEVINFO, ArraySubType = UnmanagedType.U1)]
public byte[] DeviceInfo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 40, ArraySubType = UnmanagedType.U1)]
public byte[] Reserved;
}
However, whenever I initialize the struct the Name, DeviceInfo, and Reserved fields are all set to null. How do I fix this?
For the arrays, try to use the fixed keyword:
public fixed byte DeviceInfo[MAX_LINK_DEVINFO];
public fixed byte Reserved[40];
whenever I initialize the struct the
Name, DeviceInfo, and Reserved fields
are all set to null
This is correct, and your definition looks OK to me (BTW, you don't need [MarshalAs] on the primitive fields, the default behaviour is to do what you specified there). Because your array fields are null, the marshaler won't do anything about them when marshaling your struct to unmanaged memory, but it's going to create the strings and arrays when unmarshaling.
What Anton Tykhyy says is correct. I just want to clarify with some examples. Using 'fixed' works, but that forces you to use 'unsafe' as well. I like to avoid using unsafe wherever possible. Using Marshal is a way to get around that.
First, let's say that I have a library that was created in C with the following definitions.
typedef struct {
int messageType;
BYTE payload[60];
} my_message;
/**
* \param[out] msg Where the message will be written to
*/
void receiveMessage(my_message *msg);
/*
* \param[in] msg The message that will be sent
*/
void sendMessage(my_message *msg);
In C#, the following structure would be equivalent to the one in C.
[StructLayout(LayoutKind.Sequential, Size = 64), Serializable]
struct my_message
{
int messageType;
[MarshalAs(UnmanagedType.ByValArray,SizeConst = 60)]
byte[] payload;
public initializeArray()
{
//explicitly initialize the array
payload = new byte[60];
}
}
Since the msg in receiveMessage() is documented as [out], you don't need to do anything special to the array in the structure before passing it to the function. i.e.:
my_message msg = new my_message();
receiveMessage(ref msg);
byte payload10 = msg.payload[10];
Since the msg in sendMessage() is documented as [in], you will need to fill the array before calling the function. Before filling the array, the array needs to be explicitly instantiated before using it. i.e.:
my_message msg = new my_message();
msg.initializeArray();
msg.payload[10] = 255;
sendMessage(ref msg);
Calling initializeArray() should instantiate the array in the previously allocated space created within the struct for this array.