Passing structure to unmanaged code from C# - c#

I've been fighting this problem for some time now and am hoping someone can help. I'm converting a VB6 application to C#. Everything I'm about to show works perfect in VB6, but fails in C#.
I'm making an API call into a C dll, passing a structure. The structure gets values modified in the C side and I should see the modified values in my return structure.
Note, Sig is an int, Status is an int, and CLIENTID is a UINT_PTR.
Here are the C structures:
typedef struct
{
short vers;
short flags;
short cmd;
short objtype;
DWORD id;
DWORD client;
char buf[MAX_TOOLBUF];
DWORD toolID;
NMSG msg;
DWORD caller;
CLIENTID clientID;
DWORD ticket;
ToolResult PTR result;
long spare[4];
} Request;
typedef struct
{
DWORD hwnd;
DWORD message;
DWORD wParam;
DWORD lParam;
} NMSG;
typedef struct
{
Sig sig;
short cnt;
Status error;
DWORD ticket;
DWORD toolID;
long spare[4];
} ToolResult;
In my C# code, I have the following structs defined to map to the C structs above:
[StructLayout(LayoutKind.Sequential)]
public struct NMSG
{
public int hWnd;
public int msg;
public int wParam;
public int lParam;
}
[StructLayout(LayoutKind.Sequential)]
public struct Request
{
public Int16 vers;
public Int16 flags;
public Int16 cmd;
public int16 objType;
public int id;
public int Client;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string buf;
public int toolID;
public NMSG msg;
public int caller;
public IntPtr clientID;
public int ticket;
public ToolResult result;
[MarshalAs(UnmanagedType.ByValArray, SizeConst= 4) ]
public int64[] spare;
}
This is the method decoration in the C code:
SendRequest(Request PTR req)
{
....
}
Here is my PInvoke declaration in C# for the API in C that uses these structures:
[DllImport("TheCDLL.dll", EntryPoint = "_Request#4", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
public static extern void Request(ref Request req);
And here is my C# code that fills the structure and makes the API call:
Request req = new Request();
req.vers = 1;
req.toolID = 4000;
req.Client = 0;
req.cmd = 11;
req.objType = 1;;
req.id = 1;
req.msg.hWnd = Hwnd; // verified this is a valid window handle
req.msg.msg = 1327;
req.msg.wParam = 101;
req.msg.lParam = 0;
req.spare = new int[4];
req.buf = new string(' ', 260);
req.flags = 11;
Request(ref req);
The problem is, in the VB6 code the structure item 'result' gets filled with a value after I make the API call. In C#, 'result' is always just 0. I'm guessing I must have something wrong with the way I'm marshaling? I've read dozens of articles and have tried lots of different ways to get this working with no success. Any help is appreciated!
UPDATE: Now that I've updated the code based on the suggestions below (fixing types and cleaning up the code as originally written), I'm getting an exception:
System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
I've found that this happens as soon as I changed to 'public Int16 vers' from 'public int vers' in my Request structure. Thoughts?

UnmanagedType.Struct actually isn't for substructures; it makes the Marshaler pass the related object as a VARIANT. That isn't what you want here. Try taking off all the MarshalAs statements except for strings (for the length), arrays (also for the length), and enumerated types (I don't see any here, but in my DLLs they've been UnmanagedType.I4) and see if that works.
There's also some extra things in your C# struct that aren't in your C version, a couple variables don't match between the two, and your given C function doesn't return anything but your C# extern is expecting an IntPtr. These do need to match for everything to go smoothly. (I'm guessing it's just simplification for the site, but it is something to keep in mind.)

Related

C# interop returning System.Access.Violation

I am attempting to use a C function through C# Interop and I am receiving an access violation on one function. I have tried a number of things and I can't seem to solve this.
Here is the C code that needs to be changed into c# code:
typedef struct
{
char SerNo[64];
unsigned char hwVer;
HANDLE device; // Set by the API on return from SelectDevice()
} DeviceT;
This struct is used by the following function:
error = GetDevices(DeviceT *devices, unsigned int *numDevs, unsigned int maxDevs)
There is one other function in the C code:
error = SelectDevice(DeviceT *device)
So I began by defining DeviceT. I tried a few ways, but settled on this since it is simple:
[StructLayout(LayoutKind.Sequential)]
public struct DeviceT
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public char[] SerNo;
public byte hwVer;
public IntPtr device;
}
The GetDevices function was set to this:
[DllImport("file.dll", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern ErrT GetDevices([In, Out] DeviceT[] devices, uint* numDevs, uint maxDev);
The SelectDevices function was set to this:
[DllImport("file.dll", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern ErrT SelectDevice([In, Out] DeviceT devices);
The code goes like this:
uint numDevs = 6;
uint maxDev = 6;
uint chosenIdx = 0;
DeviceT[] devices = new DeviceT[6];
err = GetDevices(devices, &NumberOfDevices, maxDev))
At this point everything is correct. The devices array has the correct information in it.
I now continue with (I just hard code select the first device)
chosenIdx = 0;
var chosenDevice = devices[chosenIdx];
err = SelectDevice(chosenDevice);
This last function returns a System.Access Violation
I tried a whole bunch of things but all end up with the same result. I suspect it has something to do with the HANDLE but I am not sure.
Thanks for any help.
SelectDevice takes a DeviceT *, but your P/Invoke signature takes a DeviceT. That is, you're passing in DeviceT by value rather than passing a pointer.
Try:
[DllImport("file.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern ErrT SelectDevice([In, Out] ref DeviceT devices);
err = SelectDevice(ref chosenDevice);

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

SafeArrayTypeMismatchException when passing c# struct to unmanaged C++ DLL

Hi I'm new to C# and C++ programming and trying to use an unmanaged C++ dll in a C# project (.NET 3.5). I'm stuck on this error:
System.Runtime.InteropServices.SafeArrayTypeMismatchException: Specified array was not of the expected type.
Here is the header file for the DLL
#ifdef FUNCTIONLIB_EXPORTS
#define FUNCTIONLIB_API __declspec(dllexport)
#else
#define FUNCTIONLIB_API __declspec(dllimport)
#endif
typedef struct _FunctionOpt
{
char UserName[32];
char Password[32];
char ServerIP[128];
int ServerPort;
int Index;
char TargetSubChannel;
long Timeout;
char Filepath[256];
bool isFirst;
DWORD SyncTime;
char *pOutBuf;
int OutBufSize;
unsigned short OutImgResW;
unsigned short OutImgResH;
}STFUNCTIONOPT, *PSTFUNCTIONOPT;
FUNCTIONLIB_API int FunctionLib_Snapshot( PSTFUNCTIONOPT pstFunctionOpt );
I don't have access to the C++ code so I can only change the following C# code. My relevant code is as follows:
public unsafe struct PSTFUNCTIONOPT
{
public char[] UserName;
public char[] Password;
public char[] ServerIP;
public int ServerPort;
public int Index;
public char TargetSubChannel;
public long Timeout;
public char[] Filepath;
public bool isFirst;
public uint SyncTime;
public char *pOutBuf; // example C++ usage: myStruct.pOutBuf = (char*)malloc(1920 * 1080 * 3);
public int OutBufSize;
public ushort OutImgResW;
public ushort OutImgResH;
}
// need to use dumpbin to get entrypoint name due to c++ mangling
[DllImport(#"C:\Location\To\Project\bin\FunctionLibLib.dll", EntryPoint = "?FunctionLib_Snapshot##YAHPAU_FunctionOpt###Z")]
public static extern int FunctionLib_Snapshot(PSTFUNCTIONOPT pstFunctionOpt);
public unsafe int FunctionLib_Run()
{
PSTFUNCTIONOPT stFunctionOpt = new PSTFUNCTIONOPT();
stFunctionOpt.UserName = ("uname").ToCharArray();
stFunctionOpt.Password = ("pword").ToCharArray();
stFunctionOpt.ServerIP = ("192.168.1.1").ToCharArray();
stFunctionOpt.ServerPort = 80;
stFunctionOpt.Index = 255;
stFunctionOpt.Timeout = 15000;
stFunctionOpt.Filepath = ("c:\\temp\\test.jpg").ToCharArray();
stFunctionOpt.isFirst = true;
stFunctionOpt.SyncTime = 0;
//stFunctionOpt.pOutBuf = new char*[10000]; // not sure how to do this yet
stFunctionOpt.OutBufSize = 10000;
// get result from DLL
return FunctionLib_Snapshot(stFunctionOpt);
}
How do I properly pass the struct to this unmanaged DLL? The error seems like something simple but I haven't been able to narrow down the problem. Any help is appreciated!
Several problems:
public char[] UserName;
That needs to be marshaled as an embedded string:
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string UserName;
Repeat that for the other fields that are declared like this.
public char *pOutBuf; // example C++ usage: myStruct.pOutBuf = (char*)malloc(1920 * 1080 * 3);
It is not clear whether the C++ function allocates that buffer or the client code is supposed to do that. If it is the former then you have a Big Problem, you cannot call the free() function to release the buffer again. Your only hope is that your C# code is supposed to allocate it, not unlikely. In which case it is:
public byte[] pOutBuf;
...
stFunctionOpt.pOutBuf = new byte[10000];
stFunctionOpt.OutBufSize = stFunctionOpt.pOutBuf.Length;
The pinvoke declaration is wrong:
[DllImport(#"C:\Location\To\Project\bin\FunctionLibLib.dll", EntryPoint = "?FunctionLib_Snapshot##YAHPAU_FunctionOpt###Z")]
public static extern int FunctionLib_Snapshot(PSTFUNCTIONOPT pstFunctionOpt);
The argument is ref PSTFUNCTIONOPT pstFunctionOpt. Do drop the P from the structure name, it is not a pointer type. Avoid hard-coding the path to the DLL, that isn't going to work on your user's machine. Just make sure that you have a copy of the DLL in your build output directory.
I think you are missing a couple of things from your structure definition
The layout of struct in memory is different in c++ so you need to add [StructLayout(LayoutKind.Sequential)] attribute to your structure
In C++ you have fixed length char arrays, so you need to specify that also in c# using attribute [MarshalAs(UnmanagedType.ByValTStr, SizeConst = your_array_lenght)], moreover if you do that, the marshaler will handle conversions between string and char array.
Your struct definition should then look something like
[StructLayout(LayoutKind.Sequential)]
public struct PSTFUNCTIONOPT
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string UserName;
...
[MarshalAs(UnmanagedType.LPStr)]
public string pOutBuf;
}
For more info check out http://msdn.microsoft.com/en-us/library/s9ts558h.aspx

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

P/Invoke C# struct with strings to C void*

I'm having a problem creating a C# P/invoke wrapper around a third party C library. In particular, the library has a method with the signature
int command(SomeHandle *handle, int commandNum, void *data, int datasize);
It is a wildcard method that does different things depending on commandNum. data can be a pointer to anything, like a single integer, or a char[], or a struct of some kind (my problem).
I have declared the wrapper as follows:
[DllImport("LIBRARY.DLL", EntryPoint = "command")]
public static extern int Command(IntPtr Handle, int CommandNum, [In, Out] IntPtr Data, int DataSize);
Now, when i call it with an opcode to fill a byte[] it works:
//WORKS, Buffer contains "library 1.0" after the call
const int BUFFER_SIZE = 128;
byte[] Buffer = new byte[BUFFER_SIZE];
int BytesWritten = 0;
GCHandle BufferHandle = GCHandle.Alloc(Buffer, GCHandleType.Pinned);
try
{
BytesWritten = Command(MyHandle, GET_VERSION, BufferHandle.AddrOfPinnedObject(), BUFFER_SIZE);
}
finally
{
BufferHandle.Free();
}
However, when I try the same with a simple struct, I cannot make it work, no matter what I try. the struct looks like this:
public struct FormatInfoType
{
public int Format;
public IntPtr Name; //const char*
public IntPtr Extension; //const char*
}
Here I am supposed to fill "Format" with an int (say, 1) and then the call to "command(...)" is meant to give me back the name and extension fields
If I pass this struct, the code compiles and runs correctly, but the values in the struct are never modified. If I change the IntPtr's to Strings or StringBuilders (and I've tried a myriad of MarshalAs attributes), then I cannot get the IntPtr to the struct because the it becomes non-blittable and the GCHandle line throws an exception.
Any help on this would be greatly appreciated.
EDIT:
I've tried many ways to call command() with the structure, but currently it looks like this:
FormatInfoType f = new FormatInfoType();
f.Format = 1;
f.Name = IntPtr.Zero;
f.Extension = IntPtr.Zero;
GCHandle fHandle = GCHandle.Alloc(f, GCHandleType.Pinned);
try
{
Command(MyHandle, GET_FORMAT_INFO, fHandle.AddrOfPinnedObject(), Marshal.SizeOf(f));
}
finally
{
fHandle.Free();
}
You can overload p/invoke signatures, try:
[DllImport("LIBRARY.DLL", EntryPoint = "command")]
public static extern int Command(IntPtr Handle, int CommandNum, ref FormatInfoType Data, int DataSize);

Categories