1

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 !

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your help. In my header file there's a line #pragma pack (1) that's why I set the Pack parameter to 1. There was no compilation errors with your code but my communication still doesn't work. It returns me a buffer of random bytes, so I'm not sure whether the problem is the code or the communication between the device and the computer. I'm going to work on this and I will come back later with more informations.
Ok so finally it works ! the problem was that the data I was trying to access (a float coded on 4 bytes) was coded "back to front". So in order to read it I just had to reverse it, byte 1 is byte 4, and so on. Thanks again for your help

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.