使用PInvoke在.NET中进行WinUSB异步调用
我用USB编程了一个微控制器,并用WinUSB将它连接到一台PC上。我能用C++和VB.NET与UCL通信,使用同步(阻塞,重叠=空)调用。我刚在C++中得到异步调用,但我不知道如何在.NET中编码它,还有一个问题我不确定: 问题1: 在C++中,我最初尝试使用IO完成回调对象进行异步调用,但是当我调用WiUSB读取它时,返回了一个错误,即异步IO已经在文件句柄(句柄Hoice)上进行了,CreateThreadpoolIo不会接受WiUSB句柄(pWuSubIInFruteHead HWiuSb句柄)。我猜WinUSB使用这个文件句柄作为IO,所以我不能。我最终使用Wait回调对象实现了它。它现在正在工作,我只是想澄清一下,我所做的是正确的用法 我有点困惑的是CreateThreadpoolWait调用。起初我以为这是在创建一个线程池,就像MSDN中的其他示例一样,但现在我认为它只是一个在默认线程池上运行的对象。此外,objConext是用于将调用与其回调同步的变量,即回调指向objConext的PVOID上下文 以下是c代码片段:使用PInvoke在.NET中进行WinUSB异步调用,.net,callback,pinvoke,winusb,.net,Callback,Pinvoke,Winusb,我用USB编程了一个微控制器,并用WinUSB将它连接到一台PC上。我能用C++和VB.NET与UCL通信,使用同步(阻塞,重叠=空)调用。我刚在C++中得到异步调用,但我不知道如何在.NET中编码它,还有一个问题我不确定: 问题1: 在C++中,我最初尝试使用IO完成回调对象进行异步调用,但是当我调用WiUSB读取它时,返回了一个错误,即异步IO已经在文件句柄(句柄Hoice)上进行了,CreateThreadpoolIo不会接受WiUSB句柄(pWuSubIInFruteHead HWiuS
// Receive data asynchronously
void BeginReceiveData(UCHAR bytPipeId, UCHAR *bytData, ULONG intLength, PTP_WAIT_CALLBACK cbCallback)
{
// Create the event
HANDLE hEventCallback = CreateEvent(NULL, FALSE, FALSE, NULL);
// Check for an error
if (!hEventCallback)
{
// Set the error
printf(strError, "Error creating callback event: %d.", GetLastError());
}
// Create the thread pool wait object
tpWait = CreateThreadpoolWait(cbCallback, &objConext, NULL);
// Check for an error
if (!tpWait)
{
// Set the error
printf(strError, "Error creating thread pool: %d.", GetLastError());
}
// Place the wait object in the thread pool
SetThreadpoolWait(tpWait, hEventCallback, NULL);
// Clear the callback
ZeroMemory(&oCallback, sizeof(oCallback));
// Set the event
oCallback.hEvent = hEventCallback;
// Read
BOOL bResult = WinUsb_ReadPipe(*hWinUsbHandle, bytPipeId, bytData, intLength, NULL, &oCallback);
// Check for an error
if (!bResult)
{
if (GetLastError() != ERROR_IO_PENDING)
{
// Set the error
printf(strError, "Error reading pipe: %d.", GetLastError());
}
}
}
// Receive data callback
void CALLBACK UsbCallback(PTP_CALLBACK_INSTANCE Instance, PVOID Context, PTP_WAIT Wait, TP_WAIT_RESULT WaitResult)
{
}
问题2:
如何将上述内容转换为.NET?这是我目前的猜测:
Dim wait As New WaitCallback(AddressOf UsbCallback)
' Create the event
Dim eEventCallback As New AutoResetEvent(False)
ThreadPool.QueueUserWorkItem(wait, Nothing)
Dim oCallback As New Overlapped(0, 0, eEventCallback.SafeWaitHandle.DangerousGetHandle, Nothing)
'Dim oCallback As New NativeOverlapped
oCallback.EventHandleIntPtr = eEventCallback.SafeWaitHandle.DangerousGetHandle
' Read
Dim bResult As Boolean = WinUsb_ReadPipe(hWinUsbHandle, bytPipeId, bytData, intLength, intLength, oCallback.EventHandleIntPtr)
<DllImport("winusb.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Public Function WinUsb_ReadPipe(InterfaceHandle As IntPtr, PipeID As Byte, Buffer() As Byte, BufferLength As UInteger, ByRef LengthTransferred As UInteger, Overlapped As IntPtr) As Boolean
End Function
虽然我还没有真正找到问题1的答案:这是否是一种“正确”的方法,但它似乎表现得相当好。以下是主要代码片段:
' Receive data asynchronously
Private Sub BeginReceiveData(bytPipeId As Byte, bytData() As Byte, intLength As UInteger)
' Allocate memory for the callback
ptrContext = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(TestContext)))
' Copy the structure to memory
Marshal.StructureToPtr(objConext, ptrContext, True)
' Create the callback
cbCallback = New UsbCallbackDelegate(AddressOf UsbCallback)
' Create the event
Dim areEvent As New AutoResetEvent(False)
' Set the event
hEventCallback = areEvent.SafeWaitHandle.DangerousGetHandle
' Create the thread pool wait object
tpWait = CreateThreadpoolWait(Marshal.GetFunctionPointerForDelegate(cbCallback), ptrContext, IntPtr.Zero)
' Place the wait object in the thread pool
SetThreadpoolWait(tpWait, hEventCallback, IntPtr.Zero)
' Set the event
oCallback.EventHandle = hEventCallback
' Read
Dim bResult As Boolean = WinUsb_ReadPipe(hWinUsbHandle, bytPipeId, bytData, intLength, intLength, oCallback)
If Not bResult Then
If Not Marshal.GetLastWin32Error = 997 Then
' Get the error
Dim intError As Integer = Marshal.GetLastWin32Error
Throw New Win32Exception(intError, "Error getting device information.")
End If
End If
End Sub
' Delegate for USB callbacks
Private Delegate Sub UsbCallbackDelegate(Instance As IntPtr, Context As IntPtr, Wait As IntPtr, WaitResult As UInteger)
' Callback
Private Sub UsbCallback(Instance As IntPtr, Context As IntPtr, Wait As IntPtr, WaitResult As UInteger)
' Number of bytes transferred
Dim intTransferred As UInteger
' Get the number of bytes transferred
WinUsb_GetOverlappedResult(hWinUsbHandle, oCallback, intTransferred, False)
' Get the context from memory
Dim objConext As TestContext = Marshal.PtrToStructure(Context, GetType(TestContext))
' Free the memory
Marshal.FreeHGlobal(Context)
' Do some work on the data
End Sub
主API DLL导入
<DllImport("winusb.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Public Function WinUsb_GetOverlappedResult(InterfaceHandle As IntPtr, ByRef lpOverlapped As Threading.NativeOverlapped, ByRef lpNumberOfBytesTransferred As UInteger, bWait As Boolean) As IntPtr
End Function
<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Public Function CreateThreadpoolWait(pfnwa As IntPtr, pv As IntPtr, pcbe As IntPtr) As IntPtr
End Function
<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Public Function SetThreadpoolWait(pwa As IntPtr, h As IntPtr, pftTimeout As IntPtr) As IntPtr
End Function
<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Public Function CloseThreadpoolWait(pwa As IntPtr) As IntPtr
End Function
警告:这里没有很多清理代码,请查看MSDN中的c代码,了解如何正确执行此操作。我认为一个面向对象的方法可能是最好的保持句柄的活动性,并实现IDIsposable,以便在完成时进行清理
有人对开源DLL感兴趣吗P
<DllImport("winusb.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Public Function WinUsb_GetOverlappedResult(InterfaceHandle As IntPtr, ByRef lpOverlapped As Threading.NativeOverlapped, ByRef lpNumberOfBytesTransferred As UInteger, bWait As Boolean) As IntPtr
End Function
<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Public Function CreateThreadpoolWait(pfnwa As IntPtr, pv As IntPtr, pcbe As IntPtr) As IntPtr
End Function
<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Public Function SetThreadpoolWait(pwa As IntPtr, h As IntPtr, pftTimeout As IntPtr) As IntPtr
End Function
<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Public Function CloseThreadpoolWait(pwa As IntPtr) As IntPtr
End Function
' Receive data asynchronously on endpoint 1
BeginReceiveData(129, bytData, 8)
While True
' Garbage collection
GC.Collect()
GC.WaitForFullGCComplete()
End While
' Callback
Private Sub UsbCallback(Instance As IntPtr, Context As IntPtr, Wait As IntPtr, WaitResult As UInteger)
' ...
' Once data is received, receive more
BeginReceiveData(129, bytData, 8)
'...
End Sub