Victor Guyot
Victor Guyot

Reputation: 33

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 !

Upvotes: 1

Views: 268

Answers (1)

Hans
Hans

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

Related Questions