Reputation: 33
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 !
Upvotes: 1
Views: 268
Reputation: 13040
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.
Upvotes: 1