Importing C dll function to C# - 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);

Related

Struct returned from C++ DLL missing first element in array data

I have a Unity5 program that is using a common structure (smMsg) to send data from a C++ DLL into C#.
The structure contains an array:
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public float[] mtx;
This array is to be considered as a 4x4 Matrix, hence the constant size of 16.
The program works, receiving data into the C# corresponding structure; however, it seems that from my array I am missing the first element (mtx[0]) each time I run the program. It seems every other element has shifted to the left with an extra 0 for the last element, maintaining the sequential order they are to be in.
I considered it to be because of the UnmanagedType I was using, however other sources tell me that UnmanagedType.ByValArray is the correct type.
Would anyone have a direction or lead I can follow to help solve this problem?
Process of Copying Data
C#:
// DLL Import
[DllImport(DLL_NAME, EntryPoint = "smCopy")]
private static extern void smCopyData(IntPtr dest, IntPtr len); //const VOID *dest, SIZE_T len);
// Copying data logic
{
// allocate intptr to buffer
var len = Marshal.SizeOf(typeof(smMsg));
IntPtr msg_intptr = Marshal.AllocHGlobal(len);
try
{
// Copy data
smCopyData(msg_intptr, (IntPtr)(len));
// Set POINTER data to struct
return_msg = (smMsg)Marshal.PtrToStructure(msg_intptr, typeof(smMsg));
}
finally
{
// free unmanaged memory!
Marshal.FreeHGlobal(msg_intptr);
}
}
C++
// Function called via DLL
void DLL_API smCopy(const VOID *dest, SIZE_T len)
{
CopyMemory((PVOID)(dest), (PVOID)(mapBuffer), len);
}
Struct Definition
The main piece of data I am interested in is float[] mtx
[StructLayout(LayoutKind.Sequential)]
public struct smMsg
{
public smHeader header;
public smData data;
}
[StructLayout(LayoutKind.Sequential)]
public struct smData
{
// Event ID.
public int evtId;
public int status;
// Floating point values. Actual data to be transmitted.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public float[] mtx;
}
[StructLayout(LayoutKind.Sequential)]
public struct smHeader
{
public ushort chkSum;
public char numWords;
public char msgType;
}
UPDATE 2/29
Thanks to #Zastai, I ended up able to recover the missing element. As it turns out, I wanted to be using "byte" as the data type instead of char, as char is a C# unicode type (short).
What I ended up doing is changing my smHeader as:
[StructLayout(LayoutKind.Sequential)]
public struct smHeader
{
public ushort chkSum;
public byte numWords;
public byte msgType;
}
.. which in turn lowered the smHeader size from 6 to 4, setting the smMsg struct size equal in terms of both C# & C++.
Thanks to #Zastai for helping out a ton.
Turns out that when using char in C++ for a struct, it's C# counterpart is a byte. This resolved the difference in structure size between C# & C++. Because of this error, the smMsg struct in C# allocated more memory than it needed.
The question is updated with the new smHeader definition.

Marshaling C# struct for C++ dll function

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

using c lib in c# - struct containing pointer to first element of array

I have to use a C library in C#. Here is the part that causes me trouble. The C function with the struct definition :
extern "C"__declspec(dllexport)unsigned short _stdcall read(unsigned short orderid, struct read_rb * request_ptr);
struct read_rb
{
// in
unsigned long C_Ref;
unsigned char Slot_Number;
unsigned char Index;
// out
unsigned char Length_s;
unsigned char * Data_s;
struct error _error;
};
So the unsigned char * Data_s is a pointer to an array that will contain output data. My C# code is below :
[DllImport("dpc2lib.dll")]
private static extern ushort read(ushort orderid, [In, Out] read_rb request_ptr);
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct read_rb
{
// in
public uint C_Ref;
public byte Slot_Number;
public byte Index;
// out
public byte Length_s;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public byte[] Data_s;
public error _error;
}
I call it in my program like this
public float readData(byte slot_Number, byte index, byte data_Length)
{
read_rb Read_rb = new read_rb();
byte[] _dataReceived = new byte[5];
Read_rb.Data_s = _dataReceived;
int result = read(0, Read_rb);
}
It simply doesn't work. When read() function is called a AccessViolationEsception is thrown. The message is : Attempt to read or write Protected Memory. This is often an indicating that other memory is corrupt.
I tried various things but I really don't know how to handle this...
Thanks for the help !
First of all you should study the header file of the C library very carefully (I do not have a C header file for the dpc2lib). Please note I based my answer on the SDK documentation found here.
Is the read_rb struct really defined with a data alignment on byte boundaries (you have set the Pack member
of the StructLayout attribute to 1). If not you should define the
read_rb struct without setting the Pack member:
[StructLayout(LayoutKind.Sequential)]
struct read_rb
{
public uint C_Ref; // in
public byte Slot_Number; // in
public byte Index; // in
public byte Length_s; // inout
public IntPtr Data_s; // out
public error error;
}
[StructLayout(LayoutKind.Sequential)]
struct error
{
... your error struct members
}
By omitting the Pack member a default value of 0 is used which means
that the packing alignment is set to the default for the current platform.
Furthermore you should define the Data_s member of the read_rb structure as IntPtr.
Define the read() function as follows:
[DllImport("dpc2lib.dll", CallingConvention=CallingConvention.StdCall)]
private static extern ushort read(ushort orderid, ref read_rb request_ptr);
The ref parameter tells the CLR to marshal data in both directions (to native code and back to
managed code again). Use StdCall as the calling convention because _stdcall is defined in
the header file for the read function.
Then, use the read_rb structure and the read() function as follows:
const ushort DPC2_DATA_LEN_S = ..; // See SDK documentation and header file
read_rb readRb = new read_rb();
readRb.C_Ref = ..; // Set identifier for connection.
readRb.Slot_Number = ..; // Set required slot on destination device.
readRb.Index = ..; // Set the index parameter.
// Set the length field to at least DPC2_DATA_LEN_S
// See SDK documentation for more information.
readRb.Length_s = DPC2_DATA_LEN_S;
try
{
// Allocate memory for data pointer.
readRb.Data_s = Marshal.AllocHGlobal(DPC2_DATA_LEN_S);
// Call the read function
ushort result = read(ref readRb);
// Check return value here.
if (result != ../*DPC2_OK*/)
{
// Handle error case
}
else
{
// Use Marshal.Copy to copy the received
// data to a byte buffer.
byte[] buffer = new byte[DPC2_DATA_LEN_S];
Marshal.Copy(readRb.Data_s, buffer, 0, buffer.Length);
// Do something with data ...
}
}
finally
{
// Finally, release the allocated memory.
if(readRb.Data_s !+ IntPtr.Zero)
{
Marshal.FreeHGlobal(readRb.Data_s);
}
}
With the help of the Marshal.Copy function you can copy the
received data to a managed byte array.

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

Passing structure to unmanaged code from 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.)

Categories