DLLImport -> how to handle a HANDLE in C#
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 );
Toby
Updated on June 04, 2022Comments
-
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 almost 13 yearsBut the bytes I try to write aren't written correctly on the uC :(
-
Toby almost 13 yearsI 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 almost 13 yearswhat is the value of bytesWritten after the call?
-
user1703401 almost 13 yearsThis 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 almost 13 yearsDon't hesitate to look this up in the MSDN Library. Post updated.
-
Toby almost 13 yearsBut 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 almost 13 yearsTo 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 almost 13 yearsYes, 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 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 almost 13 yearsYe 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 almost 13 yearsOkay 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 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 almost 13 yearsOh 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 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 almost 13 yearsThis 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!!! =)