0

I have a function in C++ and exported in DLL. the function is

LONG LOGIN(LPDEVINFO info);

the struct of LPDEVINFO is:

struct{  
       BYTE sSerialNumber[20];
} *LPDEVINFO;

to pass LPDEVINFO parameter, I have defined a class in managed code:

class DEVINFO{
     Byte[] sSerialNumber = new Byte[20];
}

and then P/Invoke like this:

[DllImport ('MyDll.dll')]
public static extern Int32 LOGIN(DEVINFO info);

and then call it in C#:

DEVINFO info = new DEVINFO();
Int id = LOGIN(info)

When I run this code, I got following error:

An unhandled exception of type 'System.AccessViolationException' occurred in WindowsFormsApplication1.exe

Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

I think the problem is caused by the array sSerialNumber. But I do not know how to define it in a right way.

Thanks in advance!

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
englefly
  • 41
  • 1
  • 8
  • If `LONG` is defined as a 64-bit integer type in C++, you'll have to either map export the function as `extern Int64 LOGIN(DEVINFO info);` or `extern long LOGIN(DEVINFO info);`. As for your `DEVINFO` class, I would make it a struct and apply the attribute `[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]` to the byte array. [see this answer](http://stackoverflow.com/questions/1354275/marshaling-a-byte-array-to-a-c-sharp-structure) – Joseph Yaduvanshi Dec 23 '11 at 17:48
  • @Jim `LONG` is 32 bit integer in C and C++ on Windows. – David Heffernan Dec 23 '11 at 17:58
  • Ah, I've seen it redefined. I don't know why it would be, though. – Joseph Yaduvanshi Dec 23 '11 at 20:01
  • @Jim It's a very common trap, `LONG` is the same as `LONG` and is 32 bits in C on Windows. In C# `long` is 64 bits. – David Heffernan Dec 23 '11 at 20:03
  • Thanks @JimSchubert, [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] works for me. – englefly Dec 24 '11 at 01:41

3 Answers3

3

Use the fixed keyword to declare a struct that contains a fixed-size buffer:

public unsafe struct DevInfo
{
    public fixed byte sSerialNumber[20];
}

For more information, see Fixed Size Buffers.

Also, to pass the struct by pointer (corresponding to LPDEVINFO on the native side), declare the function as follows:

[DllImport ('MyDll.dll')]
public static extern Int32 LOGIN(ref DevInfo info);

and call it:

DevInfo info = new DevInfo();
int id = LOGIN(ref info)
Bradley Grainger
  • 27,458
  • 4
  • 91
  • 108
  • with keyword "fixed", I get the error CS0214: Pointers and fixed size buffers may only be used in an unsafe context – englefly Dec 24 '11 at 01:41
  • @englefly You'll probably need to put `unsafe` in the struct declaration (amended my answer) and (maybe) in the code that uses it. You'll also need to enable unsafe code in the C# Project Properties. – Bradley Grainger Dec 24 '11 at 02:20
3

I would use UmanagedType.ByValArray here:

class DEVINFO {
    [MarshalAs(UnmanagedType.ByValArray, SizeConst=20)]
    public byte[] sSerialNumber;
}

Otherwise it all looks fine to me. In particular it is perfectly fine to do this with a class rather than a struct.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I'm puzzled by the downvote on this answer and the downvote on Bradley's answer. I can't see anything wrong with either of them. If there is something wrong I'd like to correct it. – David Heffernan Dec 23 '11 at 18:57
0

The function is expecting a pointer to a structure, not the actual structure.

Use the Marshal.StructureToPtr() function to convert your structure to an IntPtr.

Example in C#:

[DllImport("MyDll.dll")]
public static extern Int32 LOGIN(IntPtr info);

...

DEVINFO info = new DEVINFO();
IntPtr infoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(info));
Marshal.StructureToPtr(info, infoPtr, false);
LOGIN(infoPtr);

If this is an OUT parameter, that is you need to read from it after the function is called since it is modified by the callee, then you use the Marshal.PtrToStructure to read it back into a managed structure like follows:

DEVINFO info = (DEVINFO)Marshal.PtrToStructure(infoPtr, typeof(DEVINFO));
Trevor Elliott
  • 11,292
  • 11
  • 63
  • 102