Marshaling C# struct for C++ dll function - c#

I have a c++ dll function that takes as a parameter a pointer to this struct:
struct tLBCSHREP_PARAMS
{
BYTE Ps;
char* Shift;
char* Cashier;
char* CashRegNr;
};
, where BYTE is an 8 bit integer.
I am calling this c++ function in C# code. I have created C# equivalent for that c++ struct:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct tLBCSHREP_PARAMS
{
public byte Ps;
public IntPtr Shift;
public IntPtr Cashier;
public IntPtr CashRegNr;
};
I am creating an instatce of this struct and then a pointer:
tLBCSHREP_PARAMS p_tLBCSHREP_PARAMS = new tLBCSHREP_PARAMS();
p_tLBCSHREP_PARAMS = rapkas;
rapkas.Ps = (byte)ps;
rapkas.Shift = Marshal.StringToHGlobalAnsi(shift);
rapkas.Cashier = Marshal.StringToHGlobalAnsi(cashier);
rapkas.CashRegNr = Marshal.StringToHGlobalAnsi(cashregnr);
IntPtr ptrLBTSRLN = Marshal.AllocHGlobal(Marshal.SizeOf(rapkas));
Marshal.StructureToPtr(rapkas, ptrLBTSRLN, false);
After passing pointer to the dll funcion I get error: 0xC0000001. To my knowledge it means segmentation fault, so probably struct is not created in right way.
I have tried many times adding differrent attributes to the struct,
adding '[MarshalAs(UnmanagedType.U1)]' phrase before 'public byte Ps;' variable and many more. Nothing worked ;(

Related

Passing a struct pointer in C# interop results in NULL

I am building a managed DLL for use in unmanaged environment (C/C++ app - FreeRDP). Interop works fine in most cases, but in one particular I am not able to pass a pointer to struct.
In the API I have a struct:
typedef struct _IWTSListenerCallback IWTSListenerCallback;
struct _IWTSListenerCallback
{
UINT(*OnNewChannelConnection)(IWTSListenerCallback* pListenerCallback,
IWTSVirtualChannel* pChannel,
BYTE* Data,
BOOL* pbAccept,
IWTSVirtualChannelCallback** ppCallback);
};
As well as a function I am calling:
UINT(*CreateListener)(IWTSVirtualChannelManager* pChannelMgr,
const char* pszChannelName,
ULONG ulFlags,
IWTSListenerCallback* pListenerCallback);
Both translated to C#:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint ListenerCallbackNewConnectionDelegate(IntPtr listenerCallback, IntPtr channel, [MarshalAs(UnmanagedType.LPArray)] byte[] data, IntPtr accept, ref IntPtr channelCallback);
[StructLayout(LayoutKind.Sequential)]
public struct IWTSListenerCallback
{
[MarshalAs(UnmanagedType.FunctionPtr)]
public ListenerCallbackNewConnectionDelegate OnNewChannelConnection;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint ChannelManagerCreateListenerDelegate(IntPtr channelManager, [MarshalAs(UnmanagedType.LPStr)] string channelName, ulong flags, IntPtr listenerCallback);
[MarshalAs(UnmanagedType.FunctionPtr)]
public ChannelManagerCreateListenerDelegate CreateListener;
And execution code:
var callback = new IWTSListenerCallback();
callback.OnNewChannelConnection = NewChannelConnection;
var pCallback = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IWTSListenerCallback)));
Marshal.StructureToPtr(callback, pCallback, false);
var ret = channelManager.CreateListener(pChannelManager, "TestChannel", 0, pCallback);
And while pChannelManager (which is a pointer I obtain from unmanaged code calling my DLL) and the string are sent through without any problems, the pointer I create here (pCallback) is assigned successfuly in C#, but it results in a NULL in unmanaged code.
I assume the problem is either with how I defined the struct, or how I defined the function (although the function is being called successfuly in unmanaged code). I use the method to create the pointer in exact same way as in another part of the DLL, and it works perfectly fine there when passed to unmanaged function.
EDIT:
By #jdweng suggestion:
[StructLayout(LayoutKind.Sequential)]
public struct TestCall
{
public IntPtr channelManager;
[MarshalAs(UnmanagedType.LPStr)]
public string channelName;
public ulong flags;
public IntPtr listenerCallback;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint ChannelManagerCreateListenerDelegate(IntPtr testStructure);
var test = new TestCall();
test.channelManager = pChannelManager;
test.channelName = "TestChannel";
test.flags = 0;
test.listenerCallback = pCallback;
var pTest = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FreeRDPTypes.TestCall)));
Marshal.StructureToPtr(test, pTest, false);
var ret = channelManager.CreateListener(pTest);
Didn't work.
EDIT2: Workaround! Only if you have access to original unmanaged code. I rearranged the function arguments so the structure pointers are first, like this:
UINT(*CreateListener)(IWTSVirtualChannelManager* pChannelMgr,
IWTSListenerCallback* pListenerCallback,
const char* pszChannelName,
ULONG ulFlags);
And it works! Probably a problem with offset.
It was a matter of offset. C/C++ ULONG was typedef unsigned long which I wrongly assumed corresponded to C# ulong, but in fact the first one is 4 bytes in Visual, while the other is 8 bytes, which resulted in 4 bytes offset. Fixed by changing ulong to uint and adding [MarshalAs(UnmanagedType.U4)] for good measure. Final look of the function I was calling inside C#:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint ChannelManagerCreateListenerDelegate(IntPtr channelManager, [MarshalAs(UnmanagedType.LPStr)] string channelName, [MarshalAs(UnmanagedType.U4)] uint flags, IntPtr listenerCallback);

Issue with native C++ dll in C#

I have native C++ dll with function that finds the number of cameras connected to the computer and returns their serial number. I am trying to use native C++ dll in C# application but I keep getting the Access Violation error(Attempted to read or write protected memory).
The function in question is
uint32_t GetSerialNumList(char** theBufList, int theBufSize, int theListLength);
The way I am using PInvoke is as follows:
[DllImport(CameraDll, EntryPoint = "GetSerialNumList", CallingConvention = CallingConvention.Cdecl)]
private static extern uint GetSerialNumList(out byte[] pBuf, int BufSize, int ListLength);
If I create native C++ application to use the dll and use the function as follows:
char* theSerialNumb;
theSerialNumb = (char *) malloc(sizeof(char)* 8);
status = TRI_GetSerialNumList(&theSerialNumb, 8, 1);
It works fine however, if I use as follows in C# it give me above mentioned error:
byte[] BufList;
BufList = new byte[8];
rv = GetSerialNumList(out BufList, 8, 1);
The parameter you're passing in c# is a pointer to a byte array. What you're passing in c++ is a pointer to a pointer to a byte array. Also, in the C++ example, you're passing data to the function, but in the C# example, you're passing it as an out instead of a ref.
Although I'm not sure this would work, I would try to create a struct containing a byte array and pass the struct to the external function.
To answer some of the above comments, these functions typically modify memory passed to it rather than try to allocate additional memory due to the different ways programs create heaps.
The first thing I'd check is the C# import signature being used. There's the P/Invoke Interop Assistant tool available for free here.
Loading your function signature into the tool, translates it to:
public partial class NativeMethods {
/// Return Type: unsigned int
///theBufList: char**
///theBufSize: int
///theListLength: int
[System.Runtime.InteropServices.DllImportAttribute("<Unknown>", EntryPoint="GetSerialNumList")]
public static extern uint GetSerialNumList(ref System.IntPtr theBufList, int theBufSize, int theListLength) ;
}
The second thing, is that since you are allocating memory for the buffer in the C++/native version; perhaps you need to pass a pre-allocated buffer as well, when using C#.
Hope this helps.
Okay, I took pointers from Russell and kvr and did some digging around and following is the scheme that I came up with.
Original native function call:
uint32_t GetSerialNumList(char** theBufList, int theBufSize, int theListLength);
The way I am using PInvoke is as follows:
[DllImport(CameraDll, EntryPoint = "GetSerialNumList", CallingConvention = CallingConvention.Cdecl)]
private static extern int GetSerialNumList(ref IntPtr pBuf, int BufSize, int ListLength);
byte[] BufIn;
BufIn = new byte[8 * ListLength];
IntPtr pBuf = IntPtr.Zero;
pBuf = Marshal.AllocHGlobal(8 * ListLength);
Console.WriteLine("Calling GetSerialNumList");
rv = GetSerialNumList(ref pBuf, 8, ListLength);
Marshal.Copy(pBuf, BufIn, 0, 8*ListLength);
I feel this is somewhat long, but it gives me the desired result.

Why after marshalling, just one element of the array contains a value?

I'm calling a c++ function from my c# code. And i'm using marshalling, but when returned from c++ code, in my c# code just one element is filled of this array.
My C++ struct:
typedef struct DEV_SUB_STATE_ITEM_s
{
char err_text[NAME_MAX_LENGTH];
uint32_t state;
char obj_name[NAME_MAX_LENGTH];
char name[NAME_MAX_LENGTH];
} DEV_SUB_STATE_ITEM_t;
My struct in C#:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DEVICE_Sub_State_Item
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public String err_text;
public UInt32 state;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public String obj_name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public String name;
}
My function prototype in C++:
int COMSpClient::GetSubSlotList (UINT32 obj_rid, DEV_SUB_STATE_ITEM_t** subSlotItems);
My function prototype in C#:
[DllImport(#"xxx_OMSpClient.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "?OMSpClient_GetSubSlotList##YAHPAXHPAPAUDEV_SUB_STATE_ITEM_s###Z", CharSet = CharSet.Auto)]
public static unsafe extern Int32 GetSubSlotList(Int32 p_hHandle, UInt32 obj_rid,[MarshalAs(UnmanagedType.LPArray)] ref DEVICE_Sub_State_Item[] sub_slot_items);
My usage in C#:
OMSpClientWrapper.DEVICE_Sub_State_Item[] sub_slots = new OMSpClientWrapper.DEVICE_Sub_State_Item[5];
// TODO : load subordinate list!!!
OMSpClientWrapper.GetSubSlotList(this.omsp_client_handle, MyDevice.DeviceRID, ref sub_slots);
This is a slightly awkward function to marshal. The unmanaged code allocates the array and returns a pointer to the array to the caller. Hence the double pointer in the signature. You cannot marshal that automatically using p/invoke.
You will need to use an IntPtr, passed as an out parameter, and then do the rest of the marshalling yourself.
[DllImport(...)]
public static extern int GetSubSlotList(
IntPtr p_hHandle,
uint obj_rid,
out IntPtr sub_slot_items
);
At this point, sub_slot_items points to the first element of the array. You'll then need to use Marshal.PtrToStructure to read out each item, incrementing the point as you go.
And you'll likely need to call back into the unmanaged code to ask it to deallocate the memory.
Of course, this is messy. If you have control over the interface a better design would be to let the caller allocate the array. The code would look like this:
int COMSpClient::GetSubSlotList(
UINT32 obj_rid,
DEV_SUB_STATE_ITEM_t subSlotItems[]
);
You'd also presumably want to pass the length of the array unless there is some other reason for it to be well known by both sides.
On the C# side the code would be:
[DllImport(...)]
public static extern int GetSubSlotList(
IntPtr p_hHandle,
uint obj_rid,
[Out] DEVICE_Sub_State_Item[] sub_slot_items
);
Marshalling to a string is infinitely more annoying than it initially looks. It would perhaps be easier to marshal your strings into a fixed byte buffer, and then constructing the string like so:
public unsafe struct DEVICE_Sub_State_Item
{
public fixed byte err_text[50];
public UInt32 state;
public fixed byte obj_name[50];
public fixed byte name[50];
public string ErrorText
{
get
{
byte[] buffer = new byte[50];
fixed (byte* b = err_text)
Marshal.Copy(new IntPtr(b), buffer, 0, buffer.Length);
return Encoding.UTF8.GetString(buffer);
}
}
}
Where your actual error text would be kept as a pointer in the struct, and it is only properly read and converted to a string when you call the ErrorTextproperty on it.
You will need to enable unsafe code under the project's build options if you decide to go about it this way though.

Importing C dll function to C#

this is my first stackoverflow post. I have been stucking in this issue for days. I try to import the usbi2cio.dll which is a C Dll to a C# based project. I went through most of the similar posts within the site, while still I couldn't fix my issue, since my case might be little different.
So here is the original definition of the API and related struct as a parameter:
LONG _stdcall DAPI_ReadI2c(HANDLE hDevInstance, I2C_TRANS * TransI2C);
typedef struct _I2C_TRANS {
BYTE byTransType;
BYTE bySlvDevAddr;
WORD wMemoryAddr;
WORD wCount;
BYTE Data[256];
}I2C_TRANS, *PI2C_TRANS;
//In my C# code, I did the translation like this:
[StructLayoutAttribute(LayoutKind.Sequential), Serializable]
public struct I2C_TRANS
{
public byte byTransType;
public byte bySlvDevAddr;
public ushort wMemoryAddr;
public ushort wCount;
[MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 256, ArraySubType = UnmanagedType.I1)]
public byte[] Data;
public I2C_TRANS(int size)
{
Data = new byte[size];
this.byTransType = 0x00;
this.bySlvDevAddr = 0x00;
this.wMemoryAddr = 0;
this.wCount = 0;
}
};
public I2C_TRANS TransI2C = new I2C_TRANS(256);
public IntPtr[] hDevice = new IntPtr[DAPI_MAX_DEVICES];
...
TransI2C.byTransType = byTransType;
TransI2C.bySlvDevAddr = bySlvDevAddr;
TransI2C.wMemoryAddr = wMemoryAddr;
TransI2C.wCount = wCount;// no larger than 64
...
if((hDevice[0] = DAPI_OpenDeviceInstance(devName, 0)) != INVALID_HANDLE_VALUE)
//the returned lReadCnt should be equal to wCount.
Public int lReadCnt = DAPI_ReadI2c(hDevice[0], ref TransI2C);
For some reason, the struct in the read I2C transaction can't be well passed through, As a result, the function returns 0 value without errors(what I expect is the same value with wCount). For some other similar API and struct, it works well. So what might be the cause for this issue?
//Here is the P/Invoke declaration:
[DllImportAttribute("UsbI2cIo.dll", EntryPoint = "DAPI_ReadI2c", CallingConvention = CallingConvention.StdCall)]
public static extern int DAPI_ReadI2c(IntPtr hDevInstance, ref I2C_TRANS TransI2C);
I had a similar problem, and I fixed it by writing my own C library called Bridge, that would deal with the complex C API but expose simple methods that could be interfaced with C# easily.
For example in the method below I could pass a byte array to my C code.
From a C# point of view I would only deal with byte, int16 or int32 or byte array.
[DllImport(DLL)]
private static extern System.Int32 __SPI_Helper_Write(IntPtr lpBuffer, System.Int32 len);

Interop C# to C++ struct

I am trying to call some legacy C code using interop in C#.
I am not too famliar with how interop works on C# yet but has to work with some confusing structs.
I got part of it working but the address messes up when I try to get the struct into the C layer.
I am trying to pass a struct to C code, it will do something to it, and I need to get a result back
I have these structs in C#
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RETURNBUFFER
public IntPtr records; //this is the struct RECORD
public IntPtr infoA; // this is the struct INFO
public IntPtr infoB;
public int number;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct INFO
{
public IntPtr doc; //this is a handle in C code
public int cpFirst;
public int cpLim;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RECORD
{
public int size;
}
Records is actually a pointer to another Struct STATS defined in C# like this,
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STATS
{
public int size;
public int a;
public int b;
public int c;
public int d;
public int e;
public int f;
public int g;
}
in the C# layer, i create the struct like the following
RETURNBUFFER returnBuffer = new RETURNBUFFER();
returnBuffer.infoA = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(INFO)));
returnBuffer.infoB = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(INFO)));
returnBuffer.records = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(STATS)));
When I run my code, I was only able to retrieve the first item in returnBuffer which is returnBuffer.records,
all the other item including the int value in returnBuffer is messed up.
I try to debug through it and look into the address value,
I found that when the code codes from C# -> C the address is shifted
I am not sure why the address is off,
Here is an example of what happened under a 64bit environment
C#
&ReturnBuffer
0x00000000046f05f8
&ReturnBuffer.records
0x00000000046f05f8
&ReturnBuffer.infoA
0x00000000046f0600
&ReturnBuffer.infoB
0x00000000046f0608
&ReturnBuffer.number
0x00000000046f0610
in C, let say the function I am calling is taking parameter RETURNBUFFER *pReturnBuffer,
i get these address,
pReturnBuffer
0x00000000046F05F8
&pReturnBuffer->records
0x00000000046F05F8
&pReturnBuffer->infoA
0x00000000046F0600
&pReturnBuffer->infoB
0x00000000046F0610 **This address is off by 8**
&pReturnBuffer->number
0x00000000046F0620 **this is off by 16**
So as a result,
when the code moves back to C# function,
I can construct the returnBuffer.records correctly,
but weren't able to construct neither infoA nor infoB nor get the correct value for returnBuffer.number
not sure what I am missing out here.
===============================================
I edited my code with help of, Fun Mun Pieng
[StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)]
public struct CRB
{
[FieldOffset(0)]
public IntPtr pcstat;//CSTAT
[FieldOffset(8)]
public IntPtr caProc;//NLCA
[FieldOffset(24)]
public IntPtr caSent;//NLCA
[FieldOffset(40)]
public int cnlch;
}
now, the address matches up when it goes to C# -> C++ -> C#
However I am still getting some garbage data back.
I did some investigation and here is the erratic behaviour I found.
in C# code i make the call like this
IntPtr text = Marshal.StringToCoTaskMemUni("I am here");
legacyFunction(text, ref returnBuffer)
in here, when I call the GetHashCode function, i get the following values
returnBuffer.records.GetHashCode() 473881344
returnBuffer.infoA.GetHashCode() 473898944
returnBuffer.infoB.GetHashCode() 473898784
text.GetHashCode() 468770816
upon returning from the function, these hash value changes,
returnBuffer.records.GetHashCode() 543431240
returnBuffer.infoA.GetHashCode() 473799988
returnBuffer.infoB.GetHashCode() 473799988
text.GetHashCode() 473799988
Now, I can actually do,
Marshal.PtrToStringUni(checkReturnBuffer.infoA) and I get "I am here"
C# now thinks, both infoA and infoB are the same as text .
====================================================
2nd edit
The c++ structure is in fact
typedef struct RETURNBUFFER
{
RECORD *precord;
INFO infoA;
INFO infoB;
UINT number;
} CRB;
Thanks for the reply, this was indeed my problem.
I was somehow under the impression,
for every struct/class/object in C++
I have to make an equivalent IntPtr in C#
One last question, while I am here so I dont have to re define all structs in a new question,
for the IntPtr in struct INFO.
in C++, it is of type HANDLE
Am i correct here to define it as IntPtr? It is only a handle, but it is not a *handle thought, or should i just let it be a uint value?
Here's what I read from a msdn site "Remember, any API function that returns or accepts a handle is really working with an opaque pointer. Your code should marshal handles in Windows as System.IntPtr values"
If i defined it as IntPtr,
How should I alloc memory for it? Will the below be correct?
returnBuffer.infoA.doc = Marshal.AllocCoTaskMem(System.IntPtr.Size);
to unmarshal
Marshal.PtrToStructure(returnBuffer.infoA.doc, typeof(IntPtr));
is this the right approach?
Thank you so much
It might be because any of the following:
your C++ is compiled to 16 bytes alignment
your type for infoA is different between C++ and C#, or the size of the types are different
possible solutions include:
[FieldOffset(24)] public IntPtr infoB;
OR
comparing IntPtr.Size against sizeof(infoB) on C++
Edit: It seems infoA is 16 bytes, but your INFO declaration is not 16 bytes. It's very likely that your C# and C++ declarations are different. It would be good if you can include your C++ declarations in the question.
In the meantime, I can only guess the best match to be:
public struct RETURNBUFFER
{
public RECORD records; //this is the struct RECORD
public INFO infoA; // this is the struct INFO
public INFO infoB;
public int number;
}
Your assumption that RETURNBUFFER contains structure pointers has to be wrong. That's the only way that infoA and infoB can take up 16 bytes. The INFO structure certainly is 16 bytes long, I can't see the type of infoB. Thus:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RETURNBUFFER
public IntPtr records;
public INFO infoA;
public DUNNO infoB;
public int number;
}
Update your question with the C declarations if you still have trouble. It should be easy to see from them.
Try to make sure the win32 struct and c# struct is bit(bit size) mapping. This can be achieved by using exact c# type for win32 type.

Categories