Marshal array of struct into Ptr - c#

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

Related

How to Marshal C pointer to C# array of struct

I am trying to turn a pointer from a c dll into its equivelant C# struct array.
C Code
RECORD locked[MAX+1]; //MAX is a constant
typedef struct
{
State state; //enum
unsigned long allocated;
unsigned long lastUsed;
unsigned int useCount;
} RECORD;
API RECORD* __stdcall GetLocks( char* password )
{
if(strcmp(password, secret) == 0)
return locked;
else
return 0;
}
C# Code
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] // Pretty sure CharSet isnt actually needed here
public struct RECORD
{
public State state;
public UInt32 allocated;
public UInt32 lastUsed;
public UInt16 useCount;
}
[DllImport("gatewayapi.dll", CharSet = CharSet.Ansi)] // or here
static extern IntPtr GetLocks(string password);
public RECORD[] GetLocks(string password)
{
RECORD[] recs = new RECORD[MAX+1];
recs =(RECORD[])Marshal.PtrToStructure( GetLocks(password), typeof(RECORD[]));
if (recs.Length == 0)
{
throw new Exception();
}
return recs;
}
The above unfortunetly returns me a MissingMethodException -> No parameterless constructor defined for this object.
So in all im 100% new to Marshalling and would appreciate some advice on how to turn the pointer I receive from C into the actual C# struct array it represents.
Thanks
Given the originally posted C code, here is the answer I came up with that doesn't require compiling the code in unsafe mode:
[DllImport("gatewayapi.dll", CharSet = CharSet.Ansi)]
static extern IntPtr AMTgetLocks(string password);
public RECORD[] GetLocks(string password)
{
var channels = new RECORD[MAXCHANS + 1];
try
{
var c = AMTgetLocks(password);
var crSize = Marshal.SizeOf(typeof(RECORD));
for (int i = 0; i < MAXCHANS + 1; i++)
{
channels[i] = (CHANNELRECORD)Marshal.PtrToStructure(c, typeof(RECORD));
c = new IntPtr(c.ToInt64() + crSize);
}
}
catch (Exception)
{
throw new Exception();
}
if (channels.Length == 0)
{
throw new Exception();
}
return channels;
}

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;

Marshalling pointer to an array in struct from c++ to c#

How do I convert this expression from C++ to C#?
struct MyStruct
{
uint8_t *rcSource;
uint8_t *rcMask;
uint32_t *clientAuthSchemes;
}
The structure is initialised in C++ this way:
MyStruct st;
st.rcSource = (uint8_t*) malloc(width*height);
st.rcMask = (uint8_t*) malloc(width*height);
st.clientAuthSchemes = (uint32_t*) malloc(sizeof(uint32_t)*(size+1));
If those are 1-dimensional arrays, you probably want something like this:
struct MyStruct
{
public byte[] rcSource;
public byte[] rcMask;
public uint[] clientAuthSchemes;
}
Initialization:
MyStruct st;
st.rcSource = new byte[width*height];
st.rcMask = new byte[width*height];
st.clientAuthSchemes = new uint[size+1];
I believe this is the way:
struct MyStruct
{
sbyte rcSource, rcMask;
int clientAuthSchemes;
}
:)

Copying a string to a fixed length byte buffer in a structure

given this structure in c#:
[StructLayout(LayoutKind.Sequential)]
unsafe public struct AppVPEntry
{
public int Num;
public fixed byte CompName[256];
public int VPBeginAddress;
}
Whats the easiest way to copy a string ("c:\path\file.txt") to the fixed length buffer 'CompName'. This is in a structure thats being sent over to an archaic DLL that we've got no choice but to use. Ideally I'd love to use a .NET function but since it's fixed which implies 'unsafe' I know I'm limited here. A more generic function would help since we've got strings like this all over the DLL import space.
// C# to convert a string to a byte array.
public static byte[] StrToByteArray(string str)
{
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
return encoding.GetBytes(str);
}
You probably want to check to see if the size of the string isn't longer than the size of the buffer.
Try this out. Use an IntPtr in your DllImport wherever you might pass a VPEntry. Pass the "unmanaged" field wherever you call your DLL method.
public sealed class AppVPEntry : IDisposable {
[StructLayout(LayoutKind.Sequential, Size = 264)]
internal struct _AppVPEntry {
[MarshalAs(UnmanagedType.I4)]
public Int32 Num;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public Byte[] CompName;
[MarshalAs(UnmanagedType.I4)]
public Int32 VPBeginAddress;
}
private readonly IntPtr unmanaged;
private readonly _AppVPEntry managed = new _AppVPEntry();
public AppVPEntry(Int32 num, String path, Int32 beginAddress) {
this.managed.Num = num;
this.managed.CompName = new byte[256];
Buffer.BlockCopy(Encoding.ASCII.GetBytes(path), 0, this.managed.CompName, 0, Math.Min(path.Length, 256));
this.managed.VPBeginAddress = beginAddress;
this.unmanaged = Marshal.AllocHGlobal(264);
Marshal.StructureToPtr(this.managed, this.unmanaged, false);
}
public void Dispose() {
Marshal.FreeHGlobal(this.unmanaged);
}
}

Categories