4

in my C# code I want to import a C++ DLL. I use the dllimport and it works fine with a some of the functions. But in one function I get a HANDLE which I need later to call another function.

   [DllImport("SiUSBXp.dll")]
   public static extern int SI_Open(UInt32 deviceNum,ref IntPtr devHandle );   // this function gets the HANDLE
   [DllImport("SiUSBXp.dll")]
   public static extern int SI_Write([In]IntPtr devHandle, [In, Out] byte[] inputByte, UInt32 size,ref UInt32 bytesWritten); // this function needs the HANDLE

In my code these functions are called like this:

   IntPtr devHandle = new IntPtr();
   UInt32 bytesWritten = new UInt32();
   byte[] byteArr = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
   SI_Open(0, ref devHandle);
   SI_Write(devHandle, byteArr, 10, ref bytesWritten);

If I do it like this I get an "System.AccessViolationException". I searched here and in the internet but didnt find a specific answer. How do I use the IntPtr correctly, so it works? Best Regards

Toby

Toby
  • 43
  • 1
  • 1
  • 4
  • 2
    What do the C++ function prototypes for SI_Open and SI_Write look like? – Stu Mackellar May 08 '11 at 15:54
  • You are initializing the byte array to 10 elements but is this sufficient for the SI_Write function? – Darin Dimitrov May 08 '11 at 15:58
  • SI_STATUS WINAPI SI_Open( DWORD dwDevice, HANDLE* cyHandle ); – Toby May 08 '11 at 15:59
  • SI_STATUS WINAPI SI_Write( HANDLE cyHandle, LPVOID lpBuffer, DWORD dwBytesToWrite, LPDWORD lpdwBytesWritten ); – Toby May 08 '11 at 16:00
  • To the byte array => it actuall worked with the "same" array in C++ Code. Also I tried something -> I initialized a IntPtr like this " IntPtr Test = new IntPtr() " - When I call the Write function with this IntPtr it doesnt throw an exception...so I actually think the error is related to the IntPtr – Toby May 08 '11 at 16:02

4 Answers4

1

try this:

   [DllImport("SiUSBXp.dll")]
   public static extern int SI_Open(UInt32 deviceNum, ref IntPtr devHandle);   // this function gets the HANDLE
   [DllImport("SiUSBXp.dll")]
   public static extern int SI_Write(IntPtr devHandle, ref byte[] inputByte, UInt32 size, ref UInt32 bytesWritten); // this function needs the HANDLE

EDIT:

@Hans Passant is right. This is the correct way to pass a byte[] into a LPVOID parameter. ref used to coerce an object into LPVOID, but isn't needed for an array. What happens when you try this?

   [DllImport("SiUSBXp.dll")]
   public static extern int SI_Write(IntPtr devHandle, byte[] inputByte, UInt32 size, ref UInt32 bytesWritten); // this function needs the HANDLE

Did you try the answer @Simon Mourier gave? He was first to provide this declaration and his answer deserves to be accepted.

hsmiths
  • 1,257
  • 13
  • 17
  • But the bytes I try to write aren't written correctly on the uC :( – Toby May 08 '11 at 18:44
  • what is the value of bytesWritten after the call? – hsmiths May 08 '11 at 18:46
  • This answer cannot be correct, an array is already passed as a pointer (LPVOID). Declaring it *ref* makes it a pointer to a pointer (LPVOID*). – Hans Passant May 08 '11 at 18:58
  • To your edit --> IN this case I get the "usual" AccessViolation Exception.... so at least we pointed out that the errors lies within the byte array and not the IntPtr! – Toby May 08 '11 at 20:12
1

Your SI_Write function looks quite like Windows Kernel32's WriteFile.

So, I would do this:

[DllImport("SiUSBXp.dll", SetLastError = true)]
static extern int SI_Open(uint dwDevice, ref IntPtr cyHandle);  

[DllImport("SiUSBXp.dll", SetLastError = true)]
static extern int SI_Write(IntPtr cyHandle, byte[] lpBuffer,
   uint dwBytesToWrite, out uint lpdwBytesWritten);

EDIT: I found this documentation USBXPRESS® PROGRAMMER’S GUIDE on the web, and it states that the SI_Write prototype looks actually much closer to WriteFile than I thought. The doc states this:

SI_STATUS SI_Write (HANDLE Handle, LPVOID Buffer, DWORD NumBytesToWrite,
DWORD *NumBytesWritten, OVERLAPPED* o = NULL)

It means the .NET prototype should be this instead:

[DllImport("SiUSBXp.dll")]
static extern int SI_Write(IntPtr Handle, byte[] Buffer,
   uint NumBytesToWrite, out uint NumBytesWritten, IntPtr o);

o is optional so you can pass IntPtr.Zero.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • @Toby - strange. Maybe the C++ implementation does something unusual. When you say it does not work is it still the AccessViolationException error? – Simon Mourier May 09 '11 at 07:04
  • Ye exactly.... I don't know where's the problem. I can work around the problem if I a) declare the buffer in SI_Write as out ( but like already mentioned in another answer this is actually wrong ) - and if I do so I get strange values arriving at the device. and b) If I declare the number of bytes to write as zero, but hey that's no solution either ^^ - But I think the problem lies within in the byte array, since I can use the IntPtr to call the SI_Close function ( not mentioned here ) without problems! – Toby May 09 '11 at 07:46
  • Okay well I got one good and one bad news - the good one is, that error doesnt occur - FOR NOW ^^ - the bad one is that now the SI_Write Function returns 12 ( 0x0C ) which is defined in the header as: SI_SYSTEM_ERROR_CODE 0x0c - gonna do some trial and error now and gonna report back then. – Toby May 09 '11 at 08:13
  • @Toby - From the doc, SI_SYSTEM_ERROR_CODE means you can now use Marshal.GetLastWin32Error and get the "real" underlying windows error. Note I have added SetLastError to the C# prototypes in my answer so Marshal.GetLastWin32Error will work. – Simon Mourier May 09 '11 at 08:30
  • Oh my dear god - it works!!! I actually made a little change - I removed the "out" keyword from the last IntPtr. --> static extern int SI_Write(IntPtr Handle, byte[] Buffer, uint NumBytesToWrite, out uint NumBytesWritten,IntPtr o); Now the data arrives correctly at the device and the function closes properly! So I would actually say that's it...? Or was it wrong to remove the out keyword??! Anyways I already want to tell you -> thank you very very much for the time helping me!!! – Toby May 09 '11 at 08:32
  • @Toby - happy for you :-) well, in fact you're right about the 'out', it's better not to put it, I've edited again my answer. – Simon Mourier May 09 '11 at 08:40
  • This program is part of a project for the university - if I get an A, I'm gonna dedicate it to you!! I actually thought we won't find an answer! Well I'm quite relieved now!! Thank you again!!! =) – Toby May 09 '11 at 08:45
1

You are making a classic C programmer mistake, you don't check the return value of the functions. Which tells you whether or not the function failed. A likely scenario is that SI_Open() returned a failure code. You ignore it and use the uninitialized handle value anyway. A kaboom is not unusual.

The next possible mistake is that you don't use the CallingConvention property in the [DllImport] statement. It is fairly likely to be needed, Cdecl is the default unless the native function is declared with __stdcall. Also an excellent way to invoke a kaboom. If you still have trouble then you are going to have to debug the native code.

Btw, you get rid of the awkward syntax by using out instead of ref. In both functions.

   [DllImport("SiUSBXp.dll", CallingConvention = CallingConvention.Cdecl)]
   public static extern int SI_Open(UInt32 deviceNum, out IntPtr devHandle );
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I actually did check the return values ( and SI_Open succeeds ), but I just erased it so you have a clearer view on the critical points of the source code. – Toby May 08 '11 at 18:45
  • Don't hesitate to look this up in the MSDN Library. Post updated. – Hans Passant May 08 '11 at 19:00
  • But shouldn't it be "CallingConvention.Stdcall" instead of Cdecl? Because on the MSDN it sais "The callee cleans the stack. This is the default convention for calling unmanaged functions with platform invoke." ... ? – Toby May 08 '11 at 20:07
  • Yes, it is the default. Which only works as long as responsible C/C++ programmers use the __stdcall keyword in the declaration. Which is not that common. I can't see that code from here, you are a lot closer. – Hans Passant May 08 '11 at 20:13
0

bad: static extern void DoStuff(**byte[] inputByte**);

good: static extern void DoStuff(**[In, MarshalAs(UnmanagedType.LPArray)] byte[] inputByte**);

Adi Lester
  • 24,731
  • 12
  • 95
  • 110
strike
  • 1
  • 1