How to use UnmanagedMemoryAccessor.ReadArray<T> (Int64, T[], Int32, Int32) - c#

I want to read/write data to Memory Mapped File .How do I use UnmanagedMemoryAccessor.ReadArray (Int64, T[], Int32, Int32)
struct Data{public int a; public int b; public byte[] ;}
static Data _sdata = new Data();
static Data _mydata = new Data();
_mdata.byte = _sdata.byte = new byte[2] ;
_mmf = MemoryMappedFile.CreateNew( "test", 10);
var ired = _mmf.CreateViewAccessor();
ired.Read( 0, out mdata );here has Error
So, what is the T struct should correct ??

The array cannot be a reference it has to be contained within the struct. You have to enable unsafe code for your assembly and use the following struct definition :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
unsafe struct Data {
public int a;
public int b;
public fixed byte bytes[100];
}

Related

How to read a struct containing array of structs from TwinCat to C#

Using TwinCAT 3 ADS.Net for reading from PLC, I'm trying to read a struct containing array of structs, but the ReadAny command crashes with "Unable to marshal type" exception.
Reading directly an array of structs works fine though.
public object ReadAny(long indexGroup, long indexOffset, Type type, int[] args);
The header remark of the ReadAny method says:
“If the Type of the object to be read is an array type, the number of elements for each dimension has to be specified in the parameter args."
But what should args be for a struct containing array of structs?
(Without 'args' it fails too.)
I currently work with .NET 4.7, VS 2013.
Is there an option?
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public class WholeData
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public Station[] StationArray;
// Potentially more fields...
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public class Station
{
[MarshalAs(UnmanagedType.I1)]
public bool isPass;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 81)]
public string name;
// More fields...
}
// -- Main --
int[] args = { 5 };
// Works fine:
Station[] stationArray = (Station[])m_AdsClient.ReadAny(indexGroup, indexOffset, typeof(Station[]), args);
// Fail:
WholeData wholeData = (WholeData)m_AdsClient.ReadAny(indexGroup, indexOffset, typeof(WholeData), args);
// - OR -
WholeData wholeData = (WholeData)m_AdsClient.ReadAny(m_VarHandle, typeof(WholeData), args);
I tested successfully following code:
c# code:
class Program
{
public static TcAdsClient client;
static void Main(string[] args)
{
// Create the ADS Client
using (client = new TcAdsClient())
{
// Establish Connection
client.Connect(new AmsAddress("10.1.2.95.1.1", 851));
int handle = client.CreateVariableHandle("PRG_AIS.stAds");
AdsClass ads = (AdsClass)client.ReadAny(handle, typeof(AdsClass));
ads.boolArr[0] = 1;
client.WriteAny(handle, ads);
Console.ReadLine();
}
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
class AdsClass
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] boolArr = new byte[10];
}
ST code:
TYPE AdsStruct :
STRUCT
bTestArray : ARRAY[0..9] OF BOOL;
END_STRUCT
END_TYPE
AdsStruct is defined as stAds in PRG_AIS.
OR if you have an array of structs modify the code the following way:
c# code:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
class AdsClass
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public InnerStruct[] strArr = new InnerStruct[10];
}
struct InnerStruct
{
public byte bBoolTest;
public int nIntTest;
}
ST code:
TYPE AdsStruct :
STRUCT
stTestArray : ARRAY[0..9] OF InnerStruct;
END_STRUCT
END_TYPE
TYPE InnerStruct :
STRUCT
bBoolTest : BOOL;
nIntTest : DINT;
END_STRUCT
END_TYPE

Marshal array of struct into Ptr

I am calling a C library from a C# code. The function I am calling take as parameter a struct containing arrays of struct :
struct Example1Struct
{
char* a;
uint16_t b;
AnotherStruct* c;
}
c here is an array of pointer to AnotherStruct.
the struct in my C# code look like this
public struct Example1Struct
{
public IntPtr StationName;//is char*
public UInt16 IdCode;
public IntPtr AnotherStruct; //array of struct AnotherStruct
}
public static IntPtr MarshalToPointer(object data)
{
Type valueType = data.GetType();
IntPtr buf = IntPtr.Zero;
if (valueType.IsArray)
{
if (data is char[])
{
var d = data as char[];
buf = Marshal.AllocHGlobal(Marshal.SizeOf(d.GetType().GetElementType()) * d.Length);
}
else if (data is char[,])
{
var d = data as char[,];
buf = Marshal.AllocHGlobal(Marshal.SizeOf(d.GetType().GetElementType()) * d.Length);
}
else
{
buf = Marshal.AllocHGlobal(Marshal.SizeOf(data.GetType().GetElementType()) * count);
long LongPtr = buf.ToInt64(); // Must work both on x86 and x64
for (int I = 0; I < data.Lenght; I++)
{
IntPtr RectPtr = new IntPtr(LongPtr);
Marshal.StructureToPtr(data[I], RectPtr, false); // You do not need to erase struct in this case
LongPtr += Marshal.SizeOf(typeof(Rect));
}
}
return buf;
}
else
buf = Marshal.AllocHGlobal(Marshal.SizeOf(data));
Marshal.StructureToPtr(data, buf, false);
return buf;
}
my problem here is that I cannot cast data (who is an array of AnotherStruct) to object[] , neither in IEnumerable. So I cannot access to data[I] and don't have data.Lenght
Any idea ?
Usually I'd recommend using the MarshalAs attribute rather than writing manual marshalling code. It looks like:
public struct Example1Struct
{
public IntPtr StationName;//is char*
public UInt16 IdCode;
public IntPtr AnotherStruct; //array of struct AnotherStruct
}
Could be:
public struct Example1Struct
{
[MarshalAs(UnmanagedType.LPStr)]
public string StationName;
public UInt16 IdCode;
[MarshalAs(UnmanagedType.LPArray)]
public AnotherStruct[] OtherStructs;
}
And the marshaller should do the right thing for you when you pass it to unmanaged code.
You can get the length of the array like this:
if (data is Array a)
Console.WriteLine(a.Length);
Arrays in c# always derive from Array, so you can cast it to that.
But if possible in your real code, I'd recommend Damien's answer

C# write class object to binary file in one step

Ok, In C++ I could create a struct object that contains a bunch of value types and when needed write it out to a binary file in a single write statement like this:
struct DataFileHeader {
unsigned short Id;
unsigned short Type;
unsigned int Count;
unsigned int Offset;
};
...
struct DataFileHeader dataFileHeader;
...
rc = _write(fileHandle, &dataFileHeader, 12);
Is there any way to do that in c#? I've converted my structs to classes in c# and am working with the BinaryFormatter and trying to serialize the object but that adds a bunch of text and other stuff to the stream. I just want to write out the all the value fields in the object. Is this possible or do I have to write out each field on the object separatly?
void Main()
{
var fileHeader = new DataFileHeader()
{
Count = 10,
Id = 10,
Offset = 10,
Type = 10
};
Serialize(fileHeader).Dump();
}
// Define other methods and classes here
public byte[] Serialize<TObject>(TObject obj)
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
byte[] ret = null;
using (MemoryStream ms = new MemoryStream())
{
binaryFormatter.Serialize(ms, obj);
ret = ms.ToArray();
}
return ret;
}
[Serializable]
public struct DataFileHeader
{
public short Id;
public short Type;
public int Count;
public int Offset;
};
If that doesn't work I think doing it individually might be the only way.
BitConverter.GetBytes(fileHeader.Count).Concat(BitConverter.GetBytes(fileHeader.Count)).Concat(BitConverter.GetBytes(fileHeader.Count)).Concat(BitConverter.GetBytes(fileHeader.Count)).Dump();

PInvoke Passing structure with a nested structure array C#

I have a structure in C# which needs to be passed to a C++ DLL.
typedef struct
{
TDate fDate;
double fRate;
} TRatePt;
typedef struct _TCurve2
{
int fNumItems; /* Number of TRatePts in fArray */
TRatePt *fArray;
TDate fBaseDate;
} TCurve2;
The following is the structure I've created in C#.
[StructLayout(LayoutKind.Sequential), Serializable]
public struct TRatePt
{
public int fDate;
public double fRate;
}
[StructLayout(LayoutKind.Sequential), Serializable]
public struct TCurve2
{
public int fNumItems;
public IntPtr fArray;
public int fBaseDate;
}
I have a simple method in the C DLL.
EXPORT int TestMethodForZC(TCurve2 *discCurve)
{
printf("\n\nIn TestMethodForZC\n");
printf("discCurve->fBaseDate = %d\n\n\n",discCurve->fBaseDate );
return 0;
}
And I'm using PInvoke to call it using the following:
[DllImport("clibrary.dll", EntryPoint = "TestMethodForZC", , CallingConvention = CallingConvention.Cdecl)]
private static extern int _TestMethodForZC(ref TCurve2 discCurve);
Marshaling of TRatePt:
TRatePt[] items = new TRatePt[2];
items[0].fDate = 200;
items[1].fDate = 300;
items[0].fRate = 0.2d;
items[1].fRate = 0.3d;
TCurve2 tc2 = new TCurve2() { fBaseDate = 12000, fNumItems = 2 };
tc2.fNumItems = items.Length;
tc2.fArray = Marshal.AllocHGlobal(items.Length * Marshal.SizeOf(typeof(TRatePt)));
IntPtr item = tc2.fArray;
for (int i = 0; i < items.Length; i++)
{
Marshal.StructureToPtr(items[i], item, false);
item = new IntPtr(item.ToInt32() + Marshal.SizeOf(typeof(TRatePt)));
}
_TestMethodForZC(ref tc2);
I'm marshaling the TRatePt manually and passing the IntPtr to the method but it is printing junk values. I tried Int32 and Int64 on the Marshaling but it makes no difference. Am I doing something wrong? Any suggestions?
PS: I tried looking up for this question in stackoverflow and I found one with quite a bit of discussion but none of the suggestions worked for me.

Passing parameters from c# to c++

As the topic says, trying to pass a struct from c# environnement to c++.
c++ code that defines both the struct and the interface:
#pragma pack(push, 4)
struct CEA708CONFIG
{
BYTE b608Service;
BYTE bCompactStream;
BYTE pActiveServices[63];
LONG lActiveServiceCount; //
POINT ptAlignmentPosition;
};
#pragma pack(pop)
interface
__declspec(uuid("{some clsid}"))
ICEA708Decoder : IUnknown {
virtual HRESULT SetConfig(IN const CEA708CONFIG* pConfig) = 0;
virtual HRESULT GetConfig(OUT CEA708CONFIG* pConfig) = 0;
};
now to the c# code, i defined the same struct in c#
[StructLayout(LayoutKind.Sequential, Pack = 4), Serializable]
public struct CEA708CONFIG
{
public byte is608Service;
public byte isCompactStream;
//[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)]
public IntPtr activeServices;
public long activeServiceCount;
public Point alignmentPosition;
};
and the corresponding interface that accepts the config structure
[ComVisible(true), ComImport, Guid("same clsid as above"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICEA708Decoder
{
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int SetConfig([In, MarshalAs(UnmanagedType.Struct)] ref CEA708CONFIG config);
[return: MarshalAs(UnmanagedType.I4)]
[PreserveSig]
int GetConfig([Out, MarshalAs(UnmanagedType.Struct)] out CEA708CONFIG config);
}
my problem occurs whenever i try to pass the structure, i can clearly see that while executing the c# code the entire struct is intialized with "reasonable" values, but once passed to the c++, i see that something has happened during the transaction.
the c# code that makes the magic happen:
CEA708CONFIG cc708Config;
ICEA708Decoder CC708DecoderConfig = CC708Filter as ICEA708Decoder;
if (CC708DecoderConfig == null)
{
throw new ApplicationException("Couldn't get ICEA708Decoder structure");
}
byte[] dataByte = new byte[63];
int size = Marshal.SizeOf(dataByte[0]) * dataByte.Length;
IntPtr pnt = Marshal.AllocHGlobal(size);
dataByte[0] = 1;
Marshal.Copy(dataByte, 0, pnt, dataByte.Length);
cc708Config.activeServices = pnt;
if (0 != (hr = CC708DecoderConfig.SetConfig(ref cc708Config)))
{
throw new ApplicationException("Couldn't SetConfig() because: " + DirectShowLib.DsError.GetErrorText(hr));
}
and the exception triggered by the SetConfig is:
{"Cannot marshal field 'activeServices' of type
'CCReIndexer.Graphs.CEA708CONFIG': Invalid managed/unmanaged type
combination (Int/UInt must be paired with SysInt or SysUInt).":""}
thanks for your help!!
Have you tried transfer array as array?
[StructLayout(LayoutKind.Sequential, Pack = 4), Serializable]
public struct CEA708CONFIG
{
public byte is608Service;
public byte isCompactStream;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)]
public byte[] activeServices;
public long activeServiceCount;
public Point alignmentPosition;
};
byte[] dataByte = new byte[63];
cc708Config.activeServices = dataByte;

Categories