Marshalling complex struct from C++ to C# - c#

For a few days now I have been trying to Marshal a complex struct from C++ to C#, basically I have managed to get most of what I am trying to achieve done but now I'm stuck trying to marshal what I believe is a list.
In example I will include what I do get working and where I am stuck.
public: void __thiscall TransactionModule_t::GetTransaction(class Identity_t const &)const
Conformed as follwoing:
// public: void __thiscall TransactionModule_t::GetTransaction(class Identity_t const &)const
[DllImport("Transaction.dll", EntryPoint = "?GetTransaction#TransactionModule_t##Identity_t###Z", CallingConvention = CallingConvention.ThisCall)]
public static extern void GetTransaction(IntPtr iPtr,[Out, MarshalAs(UnmanagedType.LPStruct)] Identity transaction);
[StructLayout(LayoutKind.Sequential)]
[Serializable]
public class Identity
{
public uint Id;
public uint Type;
public Identity(uint id = 0, uint type = 0)
{
this.Id = id;
this.Type = type;
}
}
This is working just fine.
However I want to call a method which gives me the list.
public: void __thiscall TransactionModule_t::GetTransactions(class std::vector<class Identity_t,class std::allocator<class Identity_t> > &)const
And where i am getting stuck:
// public: void __thiscall TransactionModule_t::GetTransactions(class std::vector<class Identity_t,class std::allocator<class Identity_t> > &)const
[DllImport("Transaction.dll", EntryPoint = "long mangled entry point", CallingConvention = CallingConvention.ThisCall)]
public static extern void GetTransactions(IntPtr iPtr,[Out] Transactions transactions);
I tried making a class that fits in between the two.
[StructLayout(LayoutKind.Sequential)]
[Serializable]
public class Transactions
{
public Identity Identity;
public Identity[] List;
}
Is it even possible to call this method, am I missing something here?

Is it even possible to call this method?
No it is not. You cannot supply a std::vector from C# code.
Realistically you are going to need a C++/CLI wrapper.

Related

using nested structure pointers in C/C++ DLL in C#

let say we have following code in a dll written in C-lang, where i try to map some functions defined in the dll as functionpointers, map to their actual functions, i follow following link to get till here
https://learn.microsoft.com/en-us/dotnet/framework/interop/marshaling-different-types-of-arrays
Dlltest.h
typedef struct VersionInfo
{
UINT uMajor;
UINT uMinor;
UINT uMRevision;
} STRUCT_VERSION_INFO;
typedef struct DllTestFPStruct
{
int(*Close) (void);
int(*Init) (STRUCT_VERSION_INFO *sVersInfo);
int(*RegisterClient) (int id);
} STRUCT_DLL_TEST_FP_STRUCT;
typedef struct DllTestMgr
{
STRUCT_DLL_TEST_FP_STRUCT *psItf;
int iDummy;
} STRUCT_DLL_TEST_MGR;
extern "C"
{ __declspec(dllexport) void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP); }
Dlltest.c
static int Close(void);
static int Init(STRUCT_VERSION_INFO *sVersInfo);
static int RegisterClient(int id);
STRUCT_DLL_TEST_FP_STRUCT sFP =
{
&Close,
&Init,
&RegisterClient,
};
DLLTESTC_API void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP)
{ psFP->psItf = &sFP; }
static int Close(void)
{ printf("Closed called.\n"); }
static int Init(STRUCT_VERSION_INFO *sVersInfo)
{ printf("Init called.\n");}
static int RegisterClient(STRUCT_VERSION_INFO *sVersInfo)
{ printf("RegisterClient called.\n");}
Now i want to write a c# application which uses this DLL, specially it should make use of the "GetDllTestFP"-Function which maps the functionpointers to their actuall function. right now my C#-Application is as follow:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_CLOSE(ref VersionInfo sVersInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_INIT(ref VersionInfo sVersInfo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int FP_DLL_TEST_RC(ref VersionInfo sVersInfo);
[StructLayout(LayoutKind.Sequential)]
public struct DllTestFPStruct
{
public FP_DLL_TEST_CLOSE Close;
public FP_DLL_TEST_INIT Init;
public FP_DLL_TEST_RC RegisterClient;
}
[StructLayout(LayoutKind.Sequential)]
public struct DllTestMgr
{
public IntPtr psItf;
public int iDummy;
}
[DllImport("DllTestC.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void GetDllTestFP(ref DllTestMgr ps);
static void Main(string[] args)
{
VersionInfo vInfo = new VersionInfo();
DllTestFPStruct dllFPs = new DllTestFPStruct();
DllTestMgr dllTest = new DllTestMgr();
IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(dllFPs));
Marshal.StructureToPtr(dllFPs, buffer, false);
dllTest.psItf = buffer;
GetDllTestFP(ref dllTest); // Funtionpointers are not mapped, still null
dllFPs.Close(ref vInfo);
}
The problem is, that the functions do not get mapped to their actuall functions in the dll.
Any idea how can i achieve my goal?
Thanks
The C# code is:
DllTestMgr dllTest = new DllTestMgr();
GetDllTestFP(ref dllTest);
DllTestFPStruct dllFPs = (DllTestFPStruct)Marshal.PtrToStructure(dllTest.psItf, typeof(DllTestFPStruct));
VersionInfo vInfo = new VersionInfo();
dllFPs.Close(ref vInfo);
You don't need to allocate dllTest.psItf becase in GetDllTestFP you do:
DLLTESTC_API void GetDllTestFP(STRUCT_DLL_TEST_MGR *psFP)
{
psFP->psItf = &sFP;
}
So you copy the address of sFP.
Note that in general this is a bad idea, because you are giving to the "client" direct access to your data (the sFP struct). The alternative is that the client passes the memory (as you wrote before) and then you do:
(*psFP->psItf) = sFP;
(but then remember to free the allocated memory!)
Third alternative, the C-side allocates a block of memory through a shared allocator (one that can be used by C#, so no malloc/new here) and then the C# has to deallocate it.
wrong solution is
STRUCT_DLL_TEST_FP_STRUCT sFP2 = sFP;
psFP->psItf = &sFP2;
The lifetime of sFP2 ends when the method returns. psFP->psItf is now pointing to a piece of stack that doesn't exist anymore. don't do it!
Ah... as written by #Hans Passant, depending on who allocates the memory, the GetDllTestFP can be ref or out. If the memory is allocated by C# then it must be ref, if it isn't allocated (as is now) or is allocated by C++, then out is ok and you'll save on the marshaling in one direction.

Marshalling an integer pointer inside a structure as a callback

I have a C structure used for callback that I need to marshall to C# .NET:
struct CTMDeviceInfo {
enum CTMDeviceType eDeviceType;
char * szDeviceModel;
char * szDeviceSubModel;
int32_t * piDeviceID;
};
This is my C# version:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CTMDeviceInfo
{
public CTMDeviceType deviceType;
[MarshalAs(UnmanagedType.LPStr)]
public string deviceModel;
[MarshalAs(UnmanagedType.LPStr)]
public string deviceSubModel;
public IntPtr deviceId;
};
Which is used inside another structure:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CTMDeviceError
{
public CTMDeviceInfo deviceInfo;
[MarshalAs(UnmanagedType.I4)]
public Int32 resultCode;
[MarshalAs(UnmanagedType.I4)]
public Int32 extendedResultCode;
public IntPtr denomination;
public IntPtr changeDue;
};
My problem is that the "IntPtr deviceId" does not consistently return the correct value every time a callback was made.
I was expecting an integer value of 5, 15 or 16 but it keeps returning random values like 106, 865412, 652272, etc.
I don't know what I did wrong. What I did though is to prevent the callback in my managed code to be garbage collected using GCHandle.
Here is the sequence on how I did it:
From my unmanaged code I have this CDECL callback method:
void ctm_add_device_error_event_handler(CTMDeviceErrorCallback);
typedef void (CTMDeviceErrorCallback) (struct CTMEventInfo, struct CTMDeviceError );
This is my managed code:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void OnDeviceErrorCallBack(CTMEventInfo evtInfo, CTMDeviceError deviceError);
[DllImport("libctmclient-0.dll", EntryPoint = "ctm_add_device_error_event_handler", CallingConvention = CallingConvention.Cdecl)]
public static extern void AddDeviceErrorEventHandler([MarshalAs(UnmanagedType.FunctionPtr)] OnDeviceErrorCallBack deviceErrorCallBack);
OnDeviceErrorCallBack deviceErrorCallback;
GCHandle deviceErrorCallbackGCHandle;
deviceErrorCallback = new OnDeviceErrorCallBack(OnDeviceError);
deviceErrorCallbackGCHandle = GCHandle.Alloc(deviceErrorCallback);
AddDeviceErrorEventHandler(deviceErrorCallback);
And this is where the callback is handled:
public void OnDeviceError(CTMEventInfo evtInfo, CTMDeviceError deviceError)
{
int nDeviceId = Marshal.ReadInt32(deviceError.deviceInfo.deviceId);
}
I tried to use unsafe to use pointers directly but the issue is still the same.
public unsafe int *deviceId; //instead of IntPtr
int nDeviceId = 0;
unsafe
{
nDeviceId = *(deviceError.deviceInfo.deviceId);
}
I'm sure that my unmanaged code returned the correct value because I have logs but when it reached in my managed code, somehow another value was returned.
It's like it is reading on a different reference or something.
Hope somewhat could help me because I am stuck for a while now.
Thanks!
Use following c# structure. You can get the two string from the pointer later in the code. :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CTMDeviceInfo
{
public CTMDeviceType deviceType;
public IntPtr deviceModel;
public IntPtr deviceSubModel;
public IntPtr deviceId;
};

Marshalling type which references itself

I have following (shortened) function definition in my c++ code:
EXPORT_API Table* OpenTableExport();
where Table is a struct of the form:
typedef struct Table
{
int fCurrKey;
int fTableNo;
int fRecSize;
char fCreating;
Table* fNextTable;
Table* fPrevTable;
MyFileType fFile;
} Table;
So, to PInvoke this function from managed code I naturally tried following:
[DllImport("Export.dll", EntryPoint = "OpenTableExport", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern Table OpenTable();
With following table class:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class Table
{
public KeyDescription fCurrKey;
public int fTableNo;
public uint fRecSize;
public byte fCreating;
public Table fNextTable;
public Table fPrevTable;
public FileDescription fFile;
}
Now when using the method, I get following exception (translated from german):
The field "fNextTable" of type "Table" can not be marshalled, no marshalling support exists for this type.
Why can't .NET marshall the fNextTable and fPrevTable members of the same type it is marshalling in any case?
I can also just replace the references in the managed class definition with IntPtr... but is this really necessary? (The marshalling then will work more or less).
Define the structure by the following way:
public class Table
{
public KeyDescription fCurrKey;
public int fTableNo;
public uint fRecSize;
public byte fCreating;
public IntPtr fNextTable;
public IntPtr fPrevTable;
public FileDescription fFile;
}
Test IntPtr members for NULL (IntPtr.Zero) and handle them. Probably unmanaged API contains another functions expecting Table*. See also Marshal.PtrToStructure method and another low-level Marshal methods for managed-unmanaged memory exchange.

Marshaling unmanaged pointer from hooked function to managed object and calling its member functions using managed wrapper class

I have hooked QtGui4.dll's unmanaged function QPainter::drawTextItem(const QPointF &p, const QTextItem &ti); using EasyHook in C#. I have wrapped the QPointF and QTextItem classes into managed structs and can get the data from these parameters by marshaling them. However, I would like to call member functions of the unmanaged QPainter class using managed code, so I am trying to wrap the QPainter class into a managed class. I have access to the unmanaged code for testing purposes, but will not have access to it in production. I have looked at the memory layout of the unmanaged classes QPainter and QPaintDevice. The QPainter class is just a single pointer and the QPaintDevice class is just two pointers (one for the virtual function table). I tried following the techniques in this blog, but when I call the wrapped QPainter::device() method just to see if I can get a pointer, my target (unmanaged, hooked) application crashes with either an access violation error (Access violation reading location 0x00000078) or an argument exception error (EEArgumentException at memory location 0x003786f4), depending on whether my wrapped QPainter class uses a sequential layout or not, respectively. I don’t expect someone to correct all the source code, but I was hoping if someone could point out some of the mistakes I am making in my approach. By the way, this is the first time I've ever posted a question on Stack Overflow because I usually find the answers to my problems from one or more existing questions. I apologize for the long question, but I tried to simplify the problem as much as possible without leaving out potentially relevant information. Thanks in advance for your help.
Memory Dump:
class QPainter (size = 4):
(0) d_ptr
class QPaintDevice (size = 8):
(0) {vfptr}
(4) painters
Unmanaged Code (qpainter.h):
class QPainter
{
QPainter();
explicit QPainter(QPaintDevice *);
~QPainter();
QPaintDevice *device() const; // trying to call this function
void drawTextItem(const QPointF &p, const QTextItem &ti); // hooked function
// class has additional functions, but left out for brevity
}
Managed Hook (hook.cs):
[DllImport("QtGui4.dll", CharSet = CharSet.Unicode, SetLastError = true,
CallingConvention = CallingConvention.ThisCall,
EntryPoint = "?drawTextItem#QPainter##QAEXABVQPointF##ABVQTextItem###Z")]
public static extern void QPainter_drawTextItem(IntPtr obj, IntPtr pt, IntPtr ti);
[UnmanagedFunctionPointer(CallingConvention.ThisCall, CharSet = CharSet.Unicode,
SetLastError = true)]
delegate void D_QPainter_drawTextItem(IntPtr obj, IntPtr pt, IntPtr ti);
static unsafe void QPainter_drawTextItem_Hooked(IntPtr obj, IntPtr pt, IntPtr ti)
{
QTextItem qti = (QTextItem)Marshal.PtrToStructure(ti, typeof(QTextItem));
QPointF qpt = (QPointF)Marshal.PtrToStructure(pt, typeof(QPointF));
QPainter painter = (QPainter)Marshal.PtrToStructure(obj, typeof(QPainter));
__QPaintDevice* pd = painter.device(); // ERROR
QPainter_drawTextItem(obj, pt, ti);
}
Managed Wrapper (wrap.cs):
[StructLayout(LayoutKind.Sequential, Size=8)]
public unsafe struct __QPaintDevice
{
public IntPtr *vfptr;
public IntPtr painters;
};
[StructLayout(LayoutKind.Sequential, Size=4)]
public unsafe struct __QPainter
{
public IntPtr d_ptr;
};
// When the following line is included, I get this error:
// Access violation reading location 0x00000078.
// When the following line is excluded, I get this error:
// EEArgumentException at memory location 0x003786f4.
[StructLayout(LayoutKind.Sequential)] // is this useful?
public unsafe class QPainter : IDisposable
{
private __QPainter* _this;
private __QPaintDevice* _pd;
[DllImport("QtGui4.dll", EntryPoint = "??0QPainter##QAE#XZ",
CallingConvention = CallingConvention.ThisCall)]
private static extern int _QPainter_Constructor(__QPainter* ths);
[DllImport("QtGui4.dll",
EntryPoint = "??0QPainter##QAE#PAVQPaintDevice###Z",
CallingConvention = CallingConvention.ThisCall)]
private static extern int _QPainter_Constructor(__QPainter* ths,
__QPaintDevice* pd);
[DllImport("QtGui4.dll", EntryPoint = "??1QPainter##QAE#XZ",
CallingConvention = CallingConvention.ThisCall)]
private static extern int _QPainter_Destructor(__QPainter* ths);
[DllImport("QtGui4.dll",
EntryPoint = "?device#QPainter##QBEPAVQPaintDevice##XZ",
CallingConvention = CallingConvention.ThisCall)]
private static extern __QPaintDevice* _device(__QPainter* ths);
public QPainter()
{
_this = (__QPainter*)Marshal.AllocHGlobal(sizeof(__QPainter));
_QPainter_Constructor(_this);
}
public QPainter(__QPaintDevice* pd)
{
_this = (__QPainter*)Marshal.AllocHGlobal(sizeof(__QPainter));
_pd = (__QPaintDevice*)Marshal.AllocHGlobal(sizeof(__QPaintDevice));
_QPainter_Constructor(_this, pd);
}
public void Dispose()
{
_QPainter_Destructor(_this);
Marshal.FreeHGlobal((IntPtr)_this);
Marshal.FreeHGlobal((IntPtr)_pd);
_this = null;
_pd = null;
}
public __QPaintDevice* device()
{
return _device(_this);
}
};

passing structure from c# to C dll

I am trying to learn enough C# so that I can pass a strcture by reference to a C DLL; but it never gets to the "cFunction". As you can see in the cFunction, I am explicitly setting the streamSubset value to 44; but back in the c# portion it does not return "44".
Here is the C code:
typedef struct s_mPlot
{
double price[100];
int streamSubset;
} mPlot;
extern "C" __declspec( dllexport )
void cFunction(mPlot *Mplot){
Mplot->streamSubset = 44;}
// and here is the c# code
using System;
using Vibe.Function;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public class MPLOT
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public double [] price;
public int streamSubset;
}
namespace Vibe.Indicator{
public class myIdx : IndicatorObject {
[DllImport("C:\\Users\\joe\\mcDll.dll", CharSet = CharSet.Auto)]
public static extern void cFunction(
[In, MarshalAs(UnmanagedType.LPStruct)] MPLOT mPlot );
public myIdx(object _ctx):base(_ctx){}
private IPlotObject plot1;
protected override void Create()
{
MPLOT mPlot = new MPLOT();
mPlot.streamSubset = 2;
cFunction(mPlot);
if (mPlot.streamSubset == 44)
go();
}
}
}
I can see the following:
You almost certainly need to specify the cdecl calling convention in your DllImport attribute. Add CallingConvention=CallingConvention.Cdecl.
I believe that UnmanagedType.LPStruct adds an extra level of indirection. But you are passing a C# class which is a reference type. That means you are passing a pointer to a pointer. That's one level of indirection too many. First of all remove [In, MarshalAs(UnmanagedType.LPStruct)] altogether. Then your code should work. If you switched to a struct rather than a class for MPLOT then you'd need to pass by ref to get the indirection.
I think I would have the code like this:
[StructLayout(LayoutKind.Sequential)]
public struct MPLOT
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
public double [] price;
public int streamSubset;
}
[DllImport("dllname.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern void cFunction(
ref MPLOT mPlot
);
Try specifying the calling convention explicitly:
[DllImport("C:\\Users\\joe\\mcDll.dll", CallingConvention=CallingConvention.Cdecl CharSet = CharSet.Auto)]
VC exports with calling convention cdecl by default, but DllImport uses stdcall by default. So you have to specify at least one of them explicitly, or better, both.
Replace [In, MarshalAs(UnmanagedType.LPStruct)] with ref.

Categories

Resources