如何使用連續讀取器從USB管道讀取數據
本主題描述 WDF 提供的連續讀取器物件。 本主題中的程式提供如何設定物件,並使用它從USB管道讀取數據的逐步指示。
Windows Driver Framework (WDF) 提供稱為 連續讀取器的特殊物件。 只要有可用的數據,此物件可讓USB用戶端驅動程式從大量和中斷端點持續讀取數據。 若要使用讀取器,用戶端驅動程序必須具有與驅動程式讀取數據端點相關聯的USB目標管道物件的句柄。 端點必須位於使用中組態中。 您可以透過下列兩種方式的其中一種方式來啟用組態:選取USB組態,或變更目前組態中的替代設定。 如需這些作業的詳細資訊,請參閱 如何選取USB裝置 的組態和 如何在USB介面中選取替代設定。
建立連續讀取器之後,用戶端驅動程式可以視需要啟動和停止讀取器。 持續讀取器,可確保目標管道物件上一律提供讀取要求,而且客戶端驅動程式一律準備好從端點接收數據。
連續讀取器不會自動由架構管理。 這表示當裝置進入較低電源狀態時,用戶端驅動程式必須停止讀取器,並在裝置進入工作狀態時重新啟動讀取器。
本文會利用:
在您開始使用 Intune 之前
在用戶端驅動程式可以使用連續讀取器之前,請確定符合下列需求:
您的 USB 裝置必須具有 IN 端點。 檢查USBView中的裝置組態。 Usbview.exe是一個應用程式,可讓您流覽所有 USB 控制器和連線到它們的 USB 裝置。 一般而言,USBView 會安裝在 Windows 驅動程式套件 (WDK) 的 [調試程式 ] 資料夾中。
用戶端驅動程序必須已建立架構 USB 目標裝置物件。
如果您使用 Microsoft Visual Studio Professional 2012 提供的 USB 範本,範本程式代碼會執行這些工作。 範本程式代碼會取得目標裝置物件的句柄,並儲存在裝置內容中。
KMDF 用戶端驅動程式:
KMDF 用戶端驅動程式必須藉由呼叫 WdfUsbTargetDeviceCreateWithParameters 方法來取得 WDFUSBDEVICE 句柄。 如需詳細資訊,請參閱瞭解USB用戶端驅動程式程式代碼結構(KMDF)中的。
UMDF 用戶端驅動程式:
UMDF 用戶端驅動程序必須藉由查詢架構目標裝置物件來取得 IWDFUsbTargetDevice 指標。 如需詳細資訊,請參閱瞭解USB用戶端驅動程式程式代碼結構(UMDF)中的<
IPnpCallbackHardware 實作和USB特定工作>。 裝置必須具有作用中的組態。
如果您使用 USB 範本,程式代碼會選取每個介面中的第一個組態和預設替代設定。 如需如何變更替代設定的資訊,請參閱 如何在USB介面中選取替代設定。
KMDF 用戶端驅動程式:
KMDF 用戶端驅動程序必須呼叫 WdfUsbTargetDeviceSelectConfig 方法。
UMDF 用戶端驅動程式:
針對UMDF用戶端驅動程式,架構會選取該組態中每個介面的第一個組態和預設替代設定。
用戶端驅動程序必須具有 IN 端點之架構目標管道物件的句柄。 如需詳細資訊,請參閱 如何列舉 USB 管道。
在 KMDF 用戶端驅動程式中使用連續讀取器
開始使用連續讀取器之前,您必須先初始化WDF_USB_CONTINUOUS_READER_CONFIG 結構來設定它。
在 KMDF 用戶端驅動程式中設定連續讀取器
呼叫 WDF_USB_CONTINUOUS_READER_CONFIG_INIT 宏,以初始化WDF_USB_CONTINUOUS_READER_CONFIG結構。
在 WDF_USB_CONTINUOUS_READER_CONFIG 結構中指定其組態選項。
呼叫 WdfUsbTargetPipeConfigContinuousReader 方法。
下列範例程式代碼會設定指定之目標管道對象的連續讀取器。
NTSTATUS FX3ConfigureContinuousReader( _In_ WDFDEVICE Device, _In_ WDFUSBPIPE Pipe) { NTSTATUS status; PDEVICE_CONTEXT pDeviceContext; WDF_USB_CONTINUOUS_READER_CONFIG readerConfig; PPIPE_CONTEXT pipeContext; PAGED_CODE(); pDeviceContext = WdfObjectGet_DEVICE_CONTEXT(Device); pipeContext = GetPipeContext (Pipe); WDF_USB_CONTINUOUS_READER_CONFIG_INIT( &readerConfig, FX3EvtReadComplete, pDeviceContext, pipeContext->MaxPacketSize); readerConfig.EvtUsbTargetPipeReadersFailed=FX3EvtReadFailed; status = WdfUsbTargetPipeConfigContinuousReader( Pipe, &readerConfig); if (!NT_SUCCESS (status)) { TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "%!FUNC! WdfUsbTargetPipeConfigContinuousReader failed 0x%x", status); goto Exit; } Exit: return status; }
用戶端驅動程式通常會在列舉使用中設定中設定目標管道對象之後, 於 EvtDevicePrepareHardware 回呼函式中設定連續讀取器。
在上述範例中,用戶端驅動程式會以兩種方式指定其組態選項。 先呼叫 WDF_USB_CONTINUOUS_READER_CONFIG_INIT,然後藉由設定WDF_USB_CONTINUOUS_READER_CONFIG成員。 請注意WDF_USB_CONTINUOUS_READER_CONFIG_INIT的參數。 這些值是必要的。 在這裡範例中,客戶端驅動程式會指定:
- 驅動程式所實作之完成例程的指標。 架構會在完成讀取要求時呼叫此例程。 在完成例程中,驅動程式可以存取包含已讀取數據的記憶體位置。 完成例程的實作會在步驟 2 中討論。
- 驅動程式定義內容的指標。
- 可以在單一傳輸中從裝置讀取的位元元組數目。 用戶端驅動程式可以藉由呼叫 WdfUsbInterfaceGetConfiguredPipe 或 WdfUsbTargetPipeGetInformation 方法來取得WDF_USB_PIPE_INFORMATION結構中的資訊。 如需詳細資訊,請參閱 如何列舉 USB 管道。
WDF_USB_CONTINUOUS_READER_CONFIG_INIT會將連續讀取器設定為使用 NumPendingReads 的預設值。 該值會決定架構新增至擱置佇列的讀取要求數目。 預設值已決定為許多處理器組態上的許多裝置提供相當良好的效能。
除了WDF_USB_CONTINUOUS_READER_CONFIG_INIT中指定的組態參數之外,此範例也會在 WDF_USB_CONTINUOUS_READER_CONFIG 中設定失敗例程。 此失敗例程是選擇性的。
除了失敗例程之外,還有其他成員WDF_USB_CONTINUOUS_READER_CONFIG客戶端驅動程式可用來指定傳輸緩衝區的配置。 例如,請考慮使用連續讀取器接收網路封包的網路驅動程式。 每個封包都包含標頭、承載和頁尾數據。 若要描述封包,驅動程序必須先在其呼叫中指定封包的大小,WDF_USB_CONTINUOUS_READER_CONFIG_INIT。 然後,驅動程式必須藉由設定WDF_USB_CONTINUOUS_READER_CONFIG的 HeaderLength 和 TrailerLength 成員來指定頁首和頁尾的長度。 架構會使用這些值來計算承載任一端的位元組位移。 從端點讀取承載數據時,架構會將該數據儲存在位移之間的緩衝區部分。
實作完成例程
架構會在每次要求完成時叫用用戶端驅動程序實作的完成例程。 架構會傳遞讀取的位元組數目和WDFMEMORY物件,其緩衝區包含從管道讀取的數據。
下列範例程式代碼顯示完成例程實作。
EVT_WDF_USB_READER_COMPLETION_ROUTINE FX3EvtReadComplete;
VOID FX3EvtReadComplete(
__in WDFUSBPIPE Pipe,
__in WDFMEMORY Buffer,
__in size_t NumBytesTransferred,
__in WDFCONTEXT Context
)
{
PDEVICE_CONTEXT pDeviceContext;
PVOID requestBuffer;
pDeviceContext = (PDEVICE_CONTEXT)Context;
if (NumBytesTransferred == 0)
{
return;
}
requestBuffer = WdfMemoryGetBuffer(Buffer, NULL);
if (Pipe == pDeviceContext->InterruptPipe)
{
KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"Interrupt endpoint: %s.\n",
requestBuffer ));
}
return;
}
架構會在每次要求完成時叫用用戶端驅動程序實作的完成例程。 架構會為每個讀取作業配置記憶體物件。 在完成例程中,架構會將讀取的位元元組數目和WDFMEMORY句柄傳遞給記憶體物件。 記憶體物件緩衝區包含從管道讀取的數據。 用戶端驅動程式不得釋放記憶體物件。 架構會在每次完成例程傳回之後釋放 物件。 如果客戶端驅動程式想要儲存收到的數據,驅動程式必須在完成例程中複製緩衝區的內容。
實作失敗例程
架構會叫用用戶端驅動程序實作的失敗例程,通知驅動程式連續讀取器在處理讀取要求時回報錯誤。 架構會將指標傳遞至要求失敗的目標管道對象和錯誤碼值。 根據這些錯誤碼值,驅動程式可以實作其錯誤復原機制。 驅動程式也必須傳回適當的值,指出架構是否應該重新啟動連續讀取器。
下列範例程式代碼顯示失敗例程實作。
EVT_WDF_USB_READERS_FAILED FX3EvtReadFailed;
BOOLEAN
FX3EvtReadFailed(
WDFUSBPIPE Pipe,
NTSTATUS Status,
USBD_STATUS UsbdStatus
)
{
UNREFERENCED_PARAMETER(Status);
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! ReadersFailedCallback failed NTSTATUS 0x%x, UsbdStatus 0x%x\n",
status,
UsbdStatus);
return TRUE;
}
在上述範例中,驅動程式會傳回 TRUE。 這個值會向架構指出它必須重設管道,然後重新啟動連續讀取器。
或者,如果管道發生停滯狀況,客戶端驅動程式可以傳回 FALSE 並提供錯誤復原機制。 例如,驅動程式可以檢查 USBD 狀態,併發出重設管道要求以清除停滯狀況。
如需管道中錯誤復原的相關信息,請參閱 如何從USB管道錯誤復原。
啟動和停止連續讀取器
指示架構在裝置進入工作狀態時啟動連續讀取器;當裝置離開工作狀態時,停止讀取器。 呼叫這些方法,並將目標管道物件指定為 I/O 目標物件。
連續讀取器不會自動由架構管理。 因此,當裝置電源狀態變更時,用戶端驅動程式必須明確啟動或停止目標管道物件。 驅動程式會在驅動程式的 EvtDeviceD0Entry 實作中呼叫 WdfIoTargetStart。 此呼叫可確保只有在裝置處於工作狀態時,佇列才會傳遞要求。 相反地,驅動程式會在驅動程式 EvtDeviceD0Exit 實作中呼叫 WdfIoTargetStop,讓佇列在裝置進入較低電源狀態時停止傳遞要求。
下列範例程式代碼會設定指定之目標管道對象的連續讀取器。
EVT_WDF_DEVICE_D0_ENTRY FX3EvtDeviceD0Entry;
NTSTATUS FX3EvtDeviceD0Entry(
__in WDFDEVICE Device,
__in WDF_POWER_DEVICE_STATE PreviousState
)
{
PDEVICE_CONTEXT pDeviceContext;
NTSTATUS status;
PAGED_CODE();
pDeviceContext = WdfObjectGet_DEVICE_CONTEXT(Device);
status = WdfIoTargetStart (WdfUsbTargetPipeGetIoTarget (pDeviceContext->InterruptPipe));
if (!NT_SUCCESS (status))
{
TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
"%!FUNC! Could not start interrupt pipe failed 0x%x", status);
}
}
EVT_WDF_DEVICE_D0_EXIT FX3EvtDeviceD0Exit;
NTSTATUS FX3EvtDeviceD0Exit(
__in WDFDEVICE Device,
__in WDF_POWER_DEVICE_STATE TargetState
)
{
PDEVICE_CONTEXT pDeviceContext;
NTSTATUS status;
PAGED_CODE();
pDeviceContext = WdfObjectGet_DEVICE_CONTEXT(Device);
WdfIoTargetStop (WdfUsbTargetPipeGetIoTarget (pDeviceContext->InterruptPipe), WdfIoTargetCancelSentIo));
}
上述範例顯示 EvtDeviceD0Entry 和 EvtDeviceD0Exit 回呼例程的實作。 WdfIoTargetStop 的 Action 參數可讓用戶端驅動程式在裝置離開工作狀態時,決定佇列中擱置要求的動作。 在此範例中,驅動程式會 指定 WdfIoTargetCancelSentIo。 此選項會指示架構取消佇列中的所有擱置要求。 或者,驅動程式可以指示架構等待擱置的要求完成,再停止 I/O 目標,或保留擱置的要求,並在 I/O 目標重新啟動時繼續。
在 UMDF 用戶端驅動程式中使用連續讀取器
開始使用連續讀取器之前,您必須在 IPnpCallbackHardware::OnPrepareHardware 方法的實作中設定讀取器。 取得與 IN 端點相關聯之目標管道物件的 IWDFUsbTargetPipe 介面指標之後,請執行下列步驟:
在 UMDF 用戶端驅動程式中設定連續讀取器
在目標管道物件上呼叫 QueryInterface (IWDFUsbTargetPipe),並查詢 IWDFUsbTargetPipe2 介面。
在裝置回呼物件上呼叫 QueryInterface,並查詢 IUsbTargetPipeContinuousReaderCallbackReadComplete 介面。 若要使用連續讀取器,您必須實作 IUsbTargetPipeContinuousReaderCallbackReadComplete。 本主題稍後會說明實作。
如果您已實作失敗回呼,請在裝置回呼物件上呼叫 QueryInterface,並查詢 IUsbTargetPipeContinuousReaderCallbackReadersFailed 介面。 本主題稍後會說明實作。
呼叫 IWDFUsbTargetPipe2::ConfigureContinuousReader 方法,並指定組態參數,例如標頭、預告片、擱置要求數目,以及完成和失敗回呼方法的參考。
方法會設定目標管道對象的連續讀取器。 連續讀取器會建立佇列,以管理一組從目標管道對象傳送和接收的讀取要求。
下列範例程式代碼會設定指定之目標管道對象的連續讀取器。 此範例假設呼叫端所指定的目標管道物件與 IN 端點相關聯。 連續讀取器已設定為讀取USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE個字節;表示使用架構使用的預設擱置要求數目;表示叫用客戶端驅動程式提供的完成和失敗回呼方法。 收到的緩衝區不會包含任何標頭或預告片數據。
HRESULT CDeviceCallback::ConfigureContinuousReader (IWDFUsbTargetPipe* pFxPipe)
{
if (!pFxPipe)
{
return E_INVALIDARG;
}
IUsbTargetPipeContinuousReaderCallbackReadComplete *pOnCompletionCallback = NULL;
IUsbTargetPipeContinuousReaderCallbackReadersFailed *pOnFailureCallback = NULL;
IWDFUsbTargetPipe2* pFxUsbPipe2 = NULL;
HRESULT hr = S_OK;
// Set up the continuous reader to read from the target pipe object.
//Get a pointer to the target pipe2 object.
hr = pFxPipe->QueryInterface(IID_PPV_ARGS(&pFxUsbPipe2));
if (FAILED(hr))
{
goto ConfigureContinuousReaderExit;
}
//Get a pointer to the completion callback.
hr = QueryInterface(IID_PPV_ARGS(&pOnCompletionCallback));
if (FAILED(hr))
{
goto ConfigureContinuousReaderExit;
}
//Get a pointer to the failure callback.
hr = QueryInterface(IID_PPV_ARGS(&pOnFailureCallback));
if (FAILED(hr))
{
goto ConfigureContinuousReaderExit;
}
//Get a pointer to the target pipe2 object.
hr = pFxUsbPipe2->ConfigureContinuousReader (
USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE, //size of data to be read
0, //Header
0, //Trailer
0, // Number of pending requests queued by WDF
NULL, // Cleanup callback. Not provided.
pOnCompletionCallback, //Completion routine.
NULL, //Completion routine context. Not provided.
pOnFailureCallback); //Failure routine. Not provided
if (FAILED(hr))
{
goto ConfigureContinuousReaderExit;
}
ConfigureContinuousReaderExit:
if (pOnFailureCallback)
{
pOnFailureCallback->Release();
pOnFailureCallback = NULL;
}
if (pOnCompletionCallback)
{
pOnCompletionCallback->Release();
pOnCompletionCallback = NULL;
}
if (pFxUsbPipe2)
{
pFxUsbPipe2->Release();
pFxUsbPipe2 = NULL;
}
return hr;
}
接下來,指定目標管道物件的狀態,當裝置進入並結束工作狀態時(D0)。
如果客戶端驅動程式使用電源管理的佇列將要求傳送至管道,則佇列只會在裝置處於 D0 狀態時傳遞要求。 如果裝置的電源狀態從 D0 變更為較低的電源狀態(在 D0 結束時),目標管道物件就會完成擱置的要求,而佇列會停止將要求提交至目標管道物件。 因此,不需要用戶端驅動程序來啟動和停止目標管道物件。
連續讀取器不會使用電源管理的佇列來提交要求。 因此,當裝置電源狀態變更時,您必須明確啟動或停止目標管道物件。 若要變更目標管道物件的狀態,您可以使用 架構所實作的 IWDFIoTargetStateManagement 介面。 取得與 IN 端點相關聯之目標管道物件的 IWDFUsbTargetPipe 介面指標之後,請執行下列步驟:
實作狀態管理
在您的 IPnpCallbackHardware::OnPrepareHardware 實作中,呼叫目標管道物件 (IWDFUsbTargetPipe) 上的 QueryInterface,並查詢 IWDFIoTargetStateManagement 介面。 將參考儲存在裝置回呼類別的成員變數中。
在 裝置回呼對象上實作 IPnpCallback 介面。
在 IPnpCallback::OnD0Entry 方法的實作中,呼叫 IWDFIoTargetStateManagement::Start 以啟動連續讀取器。
在 IPnpCallback::OnD0Exit 方法的實作中,呼叫 IWDFIoTargetStateManagement::Stop 以停止連續讀取器。
裝置進入工作狀態 (D0) 之後,架構會呼叫客戶端驅動程式提供的 D0 專案回呼方法,以啟動目標管道物件。 當裝置離開 D0 狀態時,架構會呼叫 D0 結束回呼方法。 目標管道物件會完成客戶端驅動程式所設定的暫止讀取要求數目,並停止接受新的要求。 下列範例程式代碼會在 裝置回呼對象上實作 IPnpCallback 介面。
class CDeviceCallback :
public IPnpCallbackHardware,
public IPnpCallback,
{
public:
CDeviceCallback();
~CDeviceCallback();
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvObject);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
virtual HRESULT STDMETHODCALLTYPE OnPrepareHardware(IWDFDevice* pDevice);
virtual HRESULT STDMETHODCALLTYPE OnReleaseHardware(IWDFDevice* pDevice);
virtual HRESULT STDMETHODCALLTYPE OnD0Entry(IWDFDevice* pWdfDevice, WDF_POWER_DEVICE_STATE previousState);
virtual HRESULT STDMETHODCALLTYPE OnD0Exit(IWDFDevice* pWdfDevice, WDF_POWER_DEVICE_STATE previousState);
virtual void STDMETHODCALLTYPE OnSurpriseRemoval(IWDFDevice* pWdfDevice);
virtual HRESULT STDMETHODCALLTYPE OnQueryRemove(IWDFDevice* pWdfDevice);
virtual HRESULT STDMETHODCALLTYPE OnQueryStop(IWDFDevice* pWdfDevice);
private:
LONG m_cRefs;
IWDFUsbTargetPipe* m_pFxUsbPipe;
IWDFIoTargetStateManagement* m_pFxIoTargetInterruptPipeStateMgmt;
HRESULT CreateUSBTargetDeviceObject (IWDFDevice* pFxDevice, IWDFUsbTargetDevice** ppUSBTargetDevice);
HRESULT ConfigureContinuousReader (IWDFUsbTargetPipe* pFxPipe);
};
下列範例程式代碼示範如何取得 IPnpCallback::OnPrepareHardware 方法中目標管道物件的 IWDFIoTargetStateManagement 介面指標
//Enumerate the endpoints and get the interrupt pipe.
for (UCHAR index = 0; index < NumEndpoints; index++)
{
hr = pFxInterface->RetrieveUsbPipeObject(index, &pFxPipe);
if (SUCCEEDED (hr) && pFxPipe)
{
if ((pFxPipe->IsInEndPoint()) && (pFxPipe->GetType()==UsbdPipeTypeInterrupt))
{
//Pipe is for an interrupt IN endpoint.
hr = pFxPipe->QueryInterface(IID_PPV_ARGS(&m_pFxIoTargetInterruptPipeStateMgmt));
if (m_pFxIoTargetInterruptPipeStateMgmt)
{
m_pFxUsbPipe = pFxPipe;
break;
}
}
else
{
//Pipe is NOT for an interrupt IN endpoint.
pFxPipe->Release();
pFxPipe = NULL;
}
}
else
{
//Pipe not found.
}
}
下列範例程式代碼示範如何取得 IPnpCallbackHardware::OnPrepareHardware 方法中目標管道物件的 IWDFIoTargetStateManagement 介面指標。
HRESULT CDeviceCallback::OnD0Entry(
IWDFDevice* pWdfDevice,
WDF_POWER_DEVICE_STATE previousState
)
{
if (!m_pFxIoTargetInterruptPipeStateMgmt)
{
return E_FAIL;
}
HRESULT hr = m_pFxIoTargetInterruptPipeStateMgmt->Start();
if (FAILED (hr))
{
goto OnD0EntryExit;
}
OnD0EntryExit:
return hr;
}
HRESULT CDeviceCallback::OnD0Exit(
IWDFDevice* pWdfDevice,
WDF_POWER_DEVICE_STATE previousState
)
{
if (!m_pFxIoTargetInterruptPipeStateMgmt)
{
return E_FAIL;
}
// Stop the I/O target always succeeds.
(void)m_pFxIoTargetInterruptPipeStateMgmt->Stop(WdfIoTargetCancelSentIo);
return S_OK;
}
連續讀取器完成讀取要求之後,用戶端驅動程序必須提供一種方式,以在要求成功完成讀取要求時收到通知。 用戶端驅動程序必須將此程式代碼新增至裝置回呼物件。
實作 IUsbTargetPipeContinuousReaderCallbackReadComplete 來提供完成回呼
在裝置回呼對象上實作 IUsbTargetPipeContinuousReaderCallbackReadComplete 介面。
請確定 裝置回呼物件的 QueryInterface 實作會遞增回呼對象的參考計數,然後傳 回 IUsbTargetPipeContinuousReaderCallbackReadComplete 介面指標。
在 IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion 方法的實作中,存取從管道讀取的數據。 pMemory 參數會指向包含數據的架構所配置的記憶體。 您可以呼叫 IWDFMemory::GetDataBuffer 來取得包含數據的緩衝區。 緩衝區包含標頭,但是 OnReaderCompletion 的 NumBytesTransferred 參數所指示的數據長度不包含標頭長度。 標頭長度是由客戶端驅動程式指定,同時在驅動程式呼叫 IWDFUsbTargetPipe2::ConfigureContinuousReader 時設定連續讀取器。
在 IWDFUsbTargetPipe2::ConfigureContinuousReader 方法的 pOnCompletion 參數中提供完成回呼的指標。
每當裝置上的端點上提供數據時,目標管道物件就會完成讀取要求。 如果讀取要求成功完成,架構會呼叫 IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion 來通知用戶端驅動程式。 否則,當目標管道對象回報讀取要求時,架構會呼叫客戶端驅動程式提供的失敗回呼。
下列範例程式代碼會在裝置回呼對象上實作 IUsbTargetPipeContinuousReaderCallbackReadComplete 介面。
class CDeviceCallback :
public IPnpCallbackHardware,
public IPnpCallback,
public IUsbTargetPipeContinuousReaderCallbackReadComplete
{
public:
CDeviceCallback();
~CDeviceCallback();
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvObject);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
virtual HRESULT STDMETHODCALLTYPE OnPrepareHardware(IWDFDevice* pDevice);
virtual HRESULT STDMETHODCALLTYPE OnReleaseHardware(IWDFDevice* pDevice);
virtual HRESULT STDMETHODCALLTYPE OnD0Entry(IWDFDevice* pWdfDevice, WDF_POWER_DEVICE_STATE previousState);
virtual HRESULT STDMETHODCALLTYPE OnD0Exit(IWDFDevice* pWdfDevice, WDF_POWER_DEVICE_STATE previousState);
virtual void STDMETHODCALLTYPE OnSurpriseRemoval(IWDFDevice* pWdfDevice);
virtual HRESULT STDMETHODCALLTYPE OnQueryRemove(IWDFDevice* pWdfDevice);
virtual HRESULT STDMETHODCALLTYPE OnQueryStop(IWDFDevice* pWdfDevice);
virtual VOID STDMETHODCALLTYPE OnReaderCompletion(IWDFUsbTargetPipe* pPipe, IWDFMemory* pMemory, SIZE_T NumBytesTransferred, PVOID Context);
private:
LONG m_cRefs;
IWDFUsbTargetPipe* m_pFxUsbPipe;
IWDFIoTargetStateManagement* m_pFxIoTargetInterruptPipeStateMgmt;
HRESULT CreateUSBTargetDeviceObject (IWDFDevice* pFxDevice, IWDFUsbTargetDevice** ppUSBTargetDevice);
HRESULT ConfigureContinuousReader (IWDFUsbTargetPipe* pFxPipe);
};
下列範例程式代碼顯示裝置回呼物件的QueryInterface實作。
HRESULT CDeviceCallback::QueryInterface(REFIID riid, LPVOID* ppvObject)
{
if (ppvObject == NULL)
{
return E_INVALIDARG;
}
*ppvObject = NULL;
HRESULT hr = E_NOINTERFACE;
if( IsEqualIID(riid, __uuidof(IPnpCallbackHardware)) || IsEqualIID(riid, __uuidof(IUnknown)) )
{
*ppvObject = static_cast<IPnpCallbackHardware*>(this);
reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
hr = S_OK;
}
if( IsEqualIID(riid, __uuidof(IPnpCallback)))
{
*ppvObject = static_cast<IPnpCallback*>(this);
reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
hr = S_OK;
}
if( IsEqualIID(riid, __uuidof(IUsbTargetPipeContinuousReaderCallbackReadComplete)))
{
*ppvObject = static_cast<IUsbTargetPipeContinuousReaderCallbackReadComplete*>(this);
reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
hr = S_OK;
}
return hr;
}
下列範例程式代碼示範如何從 IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion 所 傳回的緩衝區取得數據。 每次目標管道物件成功完成讀取要求時,架構都會呼叫 OnReaderCompletion。 此範例會取得包含數據的緩衝區,並在調試程序輸出上列印內容。
VOID CDeviceCallback::OnReaderCompletion(
IWDFUsbTargetPipe* pPipe,
IWDFMemory* pMemory,
SIZE_T NumBytesTransferred,
PVOID Context)
{
if (pPipe != m_pFxUsbInterruptPipe)
{
return;
}
if (NumBytesTransferred == 0)
{
// NumBytesTransferred is zero.
return;
}
PVOID pBuff = NULL;
LONG CurrentData = 0;
char data[20];
pBuff = pMemory->GetDataBuffer(NULL);
if (pBuff)
{
CopyMemory(&CurrentData, pBuff, sizeof(CurrentData));
sprintf_s(data, 20, "%d\n", CurrentData);
OutputDebugString(data);
pBuff = NULL;
}
else
{
OutputDebugString(TEXT("Unable to get data buffer."));
}
}
當目標管道對象發生失敗時,用戶端驅動程式可以在完成讀取要求時,從架構取得通知。 若要取得通知,用戶端驅動程式必須實作失敗回呼,並在設定連續讀取器時提供回呼的指標。 下列程序說明如何實作失敗回呼。
實作 IUsbTargetPipeContinuousReaderCallbackReadersFailed 來提供失敗回呼
在 裝置回呼對象上實作 IUsbTargetPipeContinuousReaderCallbackReadersFailed 介面。
請確定 裝置回呼物件的 QueryInterface 實作會遞增回呼對象的參考計數,然後傳 回 IUsbTargetPipeContinuousReaderCallbackReadersFailed 介面指標。
在 IUsbTargetPipeContinuousReaderCallbackReadersFailed::OnReaderFailure 方法的實作中,提供失敗讀取要求的錯誤處理。
如果連續讀取器無法完成讀取要求,且客戶端驅動程式提供失敗回呼,架構會 叫用 IUsbTargetPipeContinuousReaderCallbackReadersFailed::OnReaderFailure 方法。 架構會在 hrStatus 參數中提供 HRESULT 值,指出目標管道對象中發生的錯誤碼。 根據該錯誤碼,您可能會提供特定錯誤處理。 例如,如果您想要讓架構重設管道,然後重新啟動連續讀取器,請確定回呼傳回 TRUE。
注意 請勿在失敗回呼內呼叫 IWDFIoTargetStateManagement::Start 和 IWDFIoTargetStateManagement::Stop。
在 IWDFUsbTargetPipe2::ConfigureContinuousReader 方法的 pOnFailure 參數中提供失敗回呼的指標。
下列範例程式代碼會在裝置回呼對象上實作 IUsbTargetPipeContinuousReaderCallbackReadersFailed 介面。
class CDeviceCallback :
public IPnpCallbackHardware,
public IPnpCallback,
public IUsbTargetPipeContinuousReaderCallbackReadComplete,
public IUsbTargetPipeContinuousReaderCallbackReadersFailed
{
public:
CDeviceCallback();
~CDeviceCallback();
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvObject);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
virtual HRESULT STDMETHODCALLTYPE OnPrepareHardware(IWDFDevice* pDevice);
virtual HRESULT STDMETHODCALLTYPE OnReleaseHardware(IWDFDevice* pDevice);
virtual HRESULT STDMETHODCALLTYPE OnD0Entry(IWDFDevice* pWdfDevice, WDF_POWER_DEVICE_STATE previousState);
virtual HRESULT STDMETHODCALLTYPE OnD0Exit(IWDFDevice* pWdfDevice, WDF_POWER_DEVICE_STATE previousState);
virtual void STDMETHODCALLTYPE OnSurpriseRemoval(IWDFDevice* pWdfDevice);
virtual HRESULT STDMETHODCALLTYPE OnQueryRemove(IWDFDevice* pWdfDevice);
virtual HRESULT STDMETHODCALLTYPE OnQueryStop(IWDFDevice* pWdfDevice);
virtual VOID STDMETHODCALLTYPE OnReaderCompletion(IWDFUsbTargetPipe* pPipe, IWDFMemory* pMemory, SIZE_T NumBytesTransferred, PVOID Context);
virtual BOOL STDMETHODCALLTYPE OnReaderFailure(IWDFUsbTargetPipe * pPipe, HRESULT hrCompletion);
private:
LONG m_cRefs;
IWDFUsbTargetPipe* m_pFxUsbInterruptPipe;
IWDFIoTargetStateManagement* m_pFxIoTargetInterruptPipeStateMgmt;
HRESULT CreateUSBTargetDeviceObject (IWDFDevice* pFxDevice, IWDFUsbTargetDevice** ppUSBTargetDevice);
HRESULT RetrieveUSBDeviceDescriptor (IWDFUsbTargetDevice* pUSBTargetDevice, PUSB_DEVICE_DESCRIPTOR DescriptorHeader, PULONG cbDescriptor);
HRESULT ConfigureContinuousReader (IWDFUsbTargetPipe* pFxPipe);
};
下列範例程式代碼顯示裝置回呼物件的QueryInterface實作。
HRESULT CDeviceCallback::QueryInterface(REFIID riid, LPVOID* ppvObject)
{
if (ppvObject == NULL)
{
return E_INVALIDARG;
}
*ppvObject = NULL;
HRESULT hr = E_NOINTERFACE;
if( IsEqualIID(riid, __uuidof(IPnpCallbackHardware)) || IsEqualIID(riid, __uuidof(IUnknown)) )
{
*ppvObject = static_cast<IPnpCallbackHardware*>(this);
reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
hr = S_OK;
}
if( IsEqualIID(riid, __uuidof(IPnpCallback)))
{
*ppvObject = static_cast<IPnpCallback*>(this);
reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
hr = S_OK;
}
if( IsEqualIID(riid, __uuidof(IUsbTargetPipeContinuousReaderCallbackReadComplete)))
{
*ppvObject = static_cast<IUsbTargetPipeContinuousReaderCallbackReadComplete*>(this);
reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
hr = S_OK;
}
if( IsEqualIID(riid, __uuidof(IUsbTargetPipeContinuousReaderCallbackReadersFailed)))
{
*ppvObject = static_cast<IUsbTargetPipeContinuousReaderCallbackReadersFailed*>(this);
reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
hr = S_OK;
}
return hr;
}
下列範例程式代碼示範失敗回呼的實作。 如果讀取要求失敗,此方法會在調試程式中列印架構所報告的錯誤碼,並指示架構重設管道,然後重新啟動連續讀取器。
BOOL CDeviceCallback::OnReaderFailure(
IWDFUsbTargetPipe * pPipe,
HRESULT hrCompletion
)
{
UNREFERENCED_PARAMETER(pPipe);
UNREFERENCED_PARAMETER(hrCompletion);
return TRUE;
}
如果客戶端驅動程式未提供失敗回呼併發生錯誤,架構會重設USB管道並重新啟動連續讀取器。