DLLImport -> how to handle a HANDLE in C#

15,965

Solution 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.

Solution 2

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.

Solution 3

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 );
Share:
15,965
Toby
Author by

Toby

Updated on June 04, 2022

Comments

  • Toby
    Toby almost 2 years

    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
    Toby almost 13 years
    But the bytes I try to write aren't written correctly on the uC :(
  • Toby
    Toby almost 13 years
    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.
  • hsmiths
    hsmiths almost 13 years
    what is the value of bytesWritten after the call?
  • user1703401
    user1703401 almost 13 years
    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*).
  • user1703401
    user1703401 almost 13 years
    Don't hesitate to look this up in the MSDN Library. Post updated.
  • Toby
    Toby almost 13 years
    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
    Toby almost 13 years
    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!
  • user1703401
    user1703401 almost 13 years
    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.
  • Simon Mourier
    Simon Mourier almost 13 years
    @Toby - strange. Maybe the C++ implementation does something unusual. When you say it does not work is it still the AccessViolationException error?
  • Toby
    Toby almost 13 years
    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
    Toby almost 13 years
    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.
  • Simon Mourier
    Simon Mourier almost 13 years
    @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.
  • Toby
    Toby almost 13 years
    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!!!
  • Simon Mourier
    Simon Mourier almost 13 years
    @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.
  • Toby
    Toby almost 13 years
    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!!! =)