PInvoke Passing structure with a nested structure array C# - 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.

Related

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

Unexpected results while marshaling list of struct

I could not find out what I'm doing wrong. I think I'm doing everything by the numbers, but it looks like I'm missing something.
On the managed side:
public override List<TimerPair> GetNativeTimers()
{
var listPtr = HgrDll.GetNativeTimers(out var size, out var ptrShift);
if (listPtr == IntPtr.Zero)
throw new NullReferenceException("sounds like the list"
+ " pointer was not given");
var timerPairList = new List<TimerPair>(size);
var structSize = Marshal.SizeOf(typeof(TimerPairStruct2));
for (var i = 0; i < size; i++)
{
var o = Marshal.PtrToStructure<TimerPairStruct2>(listPtr).ToTimerPair();
timerPairList.Add(o);
listPtr += structSize;
}
return timerPairList;
}
The target struct (used for intermediate compute) and class:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal struct TimerPairStruct2 : ITimerPairStruct
{
public long Time;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)]
public string Descr;
public TimerPair ToTimerPair() => new TimerPair(Descr, Time);
}
public class TimerPair
{
public string Descr { get; set; }
public TimeSpan Time { get; set; }
public TimerPair(string descr, long time)
{
Descr = descr;
Time = new TimeSpan(0,0,0,0, (int)((double)time/1000000));
}
}
For the Pinvoke part:
[DllImport("DwarfHomograph.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr GetNativeTimers(out int size, out int ptrShift);
and now on the native side:
DwarfHgr* DwarfAsset = nullptr;
extern "C" __declspec(dllexport) list<TimerPair>* GetNativeTimers(
int* size,
int* ptrShift)
{
return DwarfAsset->GetNativeTimers(size, ptrShift);
}
the used method from the DwarfHgr class:
list<TimerPair>* DwarfHgr::GetNativeTimers(int* size, int* ptrShift)
{
TimerPairsList = Sw.AsTimerPairs();
*size = static_cast<int>(TimerPairsList.size());
*ptrShift = sizeof(TimerPair);
return &TimerPairsList;
}
and the member is list<TimerPair> TimerPairsList;
The struct I'm marshaling is:
//#pragma pack(push,8)
struct __declspec(dllexport) TimerPair final
{
long long Time{}; // ns
char Descr[64]{};
};
//#pragma pack(pop)
I'm having unit tests in both native and managed sides; the expected result looks like (native):
ImageA processing: 22.0704ms
ImageB processing: 22.0448ms
MatFullA.copyTo: 1.87452ms
MatToSparse: 1.93482ms
calcOpticalFlowFarneback: 1115.48ms
create flowSparse image: 10.1416ms
But I have this (managed):
°óŠqÉ: 1,964,675.000000ms
q$Fý: 140,725,780.000000ms
xr$Fý: 140,725,780.000000ms
: 549,755.000000ms
à¡CUÉ: 1,964,675.000000ms
: 0.000000ms

Passing Array of Structures from C#(.NET Core) to C++(unamnaged)

So I've read the documentation and countless examples online how to marshal array of structures. I've marshalled array of int's, I've marshalled structures, but now I'm completely stuck and can't get it to work no matter what I've try. Been stuck on it for over a day now.
Structure/class, tried as both
[StructLayout(LayoutKind.Sequential,CharSet = CharSet.Unicode)]
public class SaveDetails
{
[MarshalAs(UnmanagedType.LPWStr)]
public string Log;
public FILETIME FileTime;
[MarshalAs(UnmanagedType.Bool)]
public bool Saved;
}
Pinvoke and call delegate
public class LogSaveFiles : IDisposable
{
[UnmanagedFunctionPointer(CallingConvention.Winapi,CharSet = CharSet.Unicode)]
private delegate Status DLogSaveFiles([ In, Out] SaveDetails[] logsToSave, string destinationPath);
private static DLogSaveFiles _dLogSaveFiles;
private IntPtr PLogSaveFiles { get; set; }
public bool LogSaveFilesAvailable => PLogSaveFiles != IntPtr.Zero;
public LogSaveFiles(Importer importer)
{
if (importer.dllLibraryPtr!= IntPtr.Zero)
{
PLogSaveFiles = Importer.GetProcAddress(importer.dllLibrary, "LogSaveFiles");
}
}
public Status SaveFiles(SaveDetails[] logsToSave,string destinationPath)
{
Status result = Status.FunctionNotAvailable;
if (LogSaveFilesAvailable)
{
_dLogSaveFiles = (DLogSaveFiles)Marshal.GetDelegateForFunctionPointer(PLogSaveFiles, typeof(DLogSaveFiles));
result = _dLogSaveFiles(logsToSave, destinationPath);
}
return result;
}
public void Dispose()
{
}
}
Call
private void SaveLogs()
{
var logsToSave = new[]{
new SaveDetails{
FileTime = new FILETIME {dwHighDateTime = 3,dwLowDateTime = 5},
Log = LogTypes.logDeviceLog,
Saved = true},
new SaveDetails{
FileTime = new FILETIME {dwHighDateTime = 1,dwLowDateTime = 2},
Log = LogTypes.logDeviceLog,
Saved = false}
};
var pathToSave = "C:\\Logs";
_logSaveFiles.SaveFiles(logsToSave, pathToSave);
}
c++ exposed call
typedef struct _LOG_SAVE_DETAILS
{
LPTSTR szLog;
FILETIME fromFileTime;
BOOL bSaved;
} LOG_SAVE_DETAILS, *PLOG_SAVE_DETAILS;
/* Function definitions */
ULY_STATUS _API LogSaveFiles (PLOG_SAVE_DETAILS ppLogs [],
LPCTSTR szDestinationPath);
Path to destination gets passed properly, but array of structures never goes through resulting in access violation when trying to access it. At first I thought it was issue with LPTSTR not going through properly but I've implemented other calls with it on its own and succeeded marshalling it through.
I've read everything on https://learn.microsoft.com/en-us/dotnet/framework/interop/marshaling-data-with-platform-invoke , it all indicates that my approach is correct, but it doesn't work.
Any help is appreciated.
Simple solution: C side change PLOG_SAVE_DETAILS ppLogs [] to LOG_SAVE_DETAILS ppLogs [], then C#-side change public class SaveDetails to public struct SaveDetails.
Marshaling array of objects seems to be difficult (I wasn't able to do it). Marshaling array of structs works. An alternative is to do the marshaling manually, but it is a pain.
The "pain" of manual marshaling (only modified lines of code):
[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)]
private delegate Status DLogSaveFiles(IntPtr[] logsToSave, string destinationPath);
and then
public Status SaveFiles(SaveDetails[] logsToSave, string destinationPath)
{
Status result = Status.FunctionNotAvailable;
if (LogSaveFilesAvailable)
{
if (_dLogSaveFiles == null)
{
_dLogSaveFiles = (DLogSaveFiles)Marshal.GetDelegateForFunctionPointer(PLogSaveFiles, typeof(DLogSaveFiles));
}
int size = Marshal.SizeOf(typeof(SaveDetails));
IntPtr basePtr = IntPtr.Zero;
IntPtr[] ptrs = new IntPtr[logsToSave.Length + 1];
try
{
basePtr = Marshal.AllocHGlobal(size * logsToSave.Length);
for (int i = 0; i < logsToSave.Length; i++)
{
ptrs[i] = IntPtr.Add(basePtr, (i * size));
Marshal.StructureToPtr(logsToSave[i], ptrs[i], false);
}
result = _dLogSaveFiles(ptrs, destinationPath);
}
finally
{
if (basePtr != IntPtr.Zero)
{
for (int i = 0; i < logsToSave.Length; i++)
{
if (ptrs[i] != IntPtr.Zero)
{
Marshal.DestroyStructure(ptrs[i], typeof(SaveDetails));
}
}
Marshal.FreeHGlobal(basePtr);
}
}
}
return result;
}
Important: this is a marshaler C#->C++. The C++ mustn't modify the received array in any way or there will be a memory leak.

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;
}

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