views:

119

answers:

7
+2  Q: 

Marshalling Issues

I have a C++ DLL that interacts with a card reader. It requires a pointer to a data struct, which isn't a problem to do. However, when trying to interact with the DLL in C# I'm getting all kinds of problems. Errors writing to protected memory, the application just shutting down after executing the getData command, etc. Here's what we have.

C++ Method from header

void readCard(cardData* dataBuffer);

C# code

Wrapper.cs
public struct cardData{
   Byte[] data01;
   Byte[] data02;
}

[dllImport("card.dll")]
public static extern void readCard(ref cardData data);

form1.cs

Wrapper.cardData tmpData = new wrapper.cardData();
tmpData.data01 = new Byte[];
tmpData.data02 = new Byte[];
readCard(ref tmpData);

I've also tried passing cardData as an IntPtr using Marshal.StructureToPtr, which didn't return any data when I returned tried to read the ptr into a struct Marshal.PtrToStructure...

I've been trying to work this out using the help files and other posts because it seems that alot of people have trouble trying to work with C/C++ DLLs. I even tried to write the whole thing in C++ and have it return a string with the data parsed in the C++ DLL but that throws a reading/writing to protected memory error

A: 

Maybe your cardData needs to use the StructLayoutAttribute. Also you can use Dependency Walker to find the location in card.dll for that method, and add that as a named parameter.

Yuriy Faktorovich
A: 

I noticed that your Byte[] have no size associated with them. Do you know what size the arrays are supposed to be? IIRC, the space for the arrays needs to allocated when they're initialized.

If I'm way off base, let me know and I'll delete this post.

Josh E
A: 

The PInvoke Signature Toolkit has helped me in the past.

For example the following C/C++:

struct cardData{
 byte[] data01;
 byte[] data02;
}

void readCard(cardData* dataBuffer);

It has:

 System.Runtime.InteropServices.StructLayoutAttribute(  System.Runtime.InteropServices.LayoutKind.Sequential)]
 public struct cardData {

  /// byte[]
  public byte[] data01;

  /// byte[]
  public byte[] data02;
}

/// Return Type: cardData
///dataBuffer: cardData*
public delegate cardData readCard(ref cardData dataBuffer);
Mark
+1  A: 

The biggest problem I see with your code is that you have not given your byte[] members an explicit size. Without this size operator, the marshaller will treat them just like a simple reference type. The resulting struct will have a size of 8 bytes on a 32 bit platform and will almost certainly lead to writing of protected memory.

Assuming the byte arrays are of a fixed size in the C code, you should use the MarshalAs attribute to give the byte arrays the same semantics in managed code. This does entail giving them a fixed size.

[StructLayout(LayoutKind.Sequential)]
public struct cardData{
   [MarshalAs(UnmanagedType.ByValArray, SizeConst=300)]
   Byte[] data01;
   [MarshalAs(UnmanagedType.ByValArray, SizeConst=300)]
   Byte[] data02;
}

Change 300 to be whatever size is specified in the native code for the array.

Also you should add the StructLayout attribute as well.

JaredPar
A: 

Here's a snippet of a C-DLL wrapper in C# that I've made.

Like Yuriy mentioned, you're missing a StructLayout attribute, and you probably ought to be using native types in your struct and your function declaration. This will probably require you to use the unsafe keyword in a few places, which may or may not be acceptable to you - but it was fine for me.

[StructLayout(LayoutKind.Sequential)]
 public unsafe struct X_Message
 {
      public byte id;
      public byte* data;
      public DWORD data_length;
 }

 // ...

 [DllImport("x-driver.dll")]
 protected unsafe static extern int X_ReadMessage(void* h, X_Message* message);
Mark Rushakoff
A: 

Use IntPtr instead of byte[]. Your DLL can't handle managed data.

tinmaru
A: 

Ok. So the struct has been set as suggested with the struct layout.

Wrapper.cs

[StructLayout(LayoutKind.Sequential)]
public struct cardData{
   [MarshalAs(UnmanagedType.ByValArray, SizeConst=99)]
   Byte[] data01;
   [MarshalAs(UnmanagedType.ByValArray, SizeConst=101)]
   Byte[] data02;
}

[DllImport("card.Dll")]
public static extern void readCard(ref cardData data);

And now it just closes... No errors, no change in data, the app just shuts down.

Olewolfe