搭配 Winsock 核心函式使用 IRP
Winsock 核心 (WSK) 網路程式設計介面 (NPI) 使用 IRP 進行網路 I/O 作業的非同步完成。 每個 WSK 函式都會以 IRP 的指標作為參數。 WSK 子系統會在 WSK 函式執行的作業完成之後完成 IRP。
WSK 應用程式用來傳遞至 WSK 函式的 IRP 可能採用下列其中一種方式。
WSK 應用程式會呼叫 IoAllocateIrp 函式來配置 IRP。 在此情況下,WSK 應用程式必須配置至少一個 I/O 堆疊位置的 IRP。
WSK 應用程式會重複使用先前配置的已完成 IRP。 在此情況下,WSK 必須呼叫 IoReuseIrp 函式,以重新初始化 IRP。
WSK 應用程式會使用由較高層級驅動程式或 I/O 管理員向下傳遞至它的 IRP。 在此情況下,IRP 必須至少有一個剩餘的 I/O 堆疊位置可供 WSK 子系統使用。
當 WSK 應用程式有 IRP 用於呼叫 WSK 函式之後,它可以設定 IRP 完成時呼叫 IRP 的 IoCompletion 常式。 WSK 應用程式會呼叫IoSetCompletionRoutine函式,為 IRP 設定IoCompletion常式。 根據 IRP 的產生方式, IoCompletion 常式是必要或選擇性的。
如果 WSK 應用程式已配置 IRP,或重複使用先前配置的 IRP,則必須先設定 IRP 的 IoCompletion 常式,再呼叫 WSK 函式。 在此情況下,WSK 應用程式必須針對傳遞至IoSetCompletionRoutine函式的InvokeOnSuccess、InvokeOnError和InvokeOnCancel參數指定TRUE,以確保一律呼叫IoCompletion常式。 此外,為 IRP 設定的 IoCompletion 常式必須一律傳回STATUS_MORE_PROCESSING_REQUIRED,以終止 IRP 的完成處理。 如果在呼叫 IoCompletion 常式之後使用 IRP 完成 WSK 應用程式,則應該先呼叫 IoFreeIrp 函式來釋放 IRP,再從 IoCompletion 常式傳回。 如果 WSK 應用程式沒有釋放 IRP,則可以重複使用 IRP 來呼叫另一個 WSK 函式。
如果 WSK 應用程式使用由較高層級驅動程式或 I/O 管理員向下傳遞的 IRP,則只有在 WSK 函式執行的作業完成時,才應該先設定 IRP 的 IoCompletion 常式,再呼叫 WSK 函式。 如果 WSK 應用程式未設定 IRP 的 IoCompletion 常式,則當 IRP 完成時,IRP 會根據一般 IRP 完成處理,將 IRP 傳回至較高層級的驅動程式或 I/O 管理員。 如果 WSK 應用程式設定 IRP 的 IoCompletion 常式, IoCompletion 常式可以傳回STATUS_SUCCESS或STATUS_MORE_PROCESSING_REQUIRED。 如果 IoCompletion 常式傳回STATUS_SUCCESS,IRP 完成處理會正常繼續。 如果 IoCompletion 常式傳回STATUS_MORE_PROCESSING_REQUIRED,WSK 應用程式必須在完成處理 WSK 函式所執行作業的結果之後呼叫 IoCompleteRequest 來完成 IRP。 WSK 應用程式絕對不應該釋放由較高層級驅動程式或 I/O 管理員向下傳遞的 IRP。
注意如果 WSK 應用程式設定IRP 的 IoCompletion常式,該 IRP 是由較高層級的驅動程式或 I/O 管理員向下傳遞,則 IoCompletion常式必須檢查 IRP 的PendingReturned 成員,並在 PendingReturned成員為TRUE時呼叫IoMarkIrpPending函式。 如需詳細資訊,請參閱 實作 IoCompletion 常式。
注意 WSK 應用程式不應該在 IoCompletion 常式的內容中呼叫新的 WSK 函式。 這樣做可能會導致遞迴呼叫並耗盡核心模式堆疊。 在 IRQL = DISPATCH_LEVEL執行時,這也會導致其他執行緒耗盡。
WSK 應用程式不會初始化它傳遞給 WSK 函式的 IRP,而不是設定 IoCompletion 常式。 當 WSK 應用程式將 IRP 傳遞至 WSK 函式時,WSK 子系統會代表應用程式設定下一個 I/O 堆疊位置。
下列程式碼範例示範 WSK 應用程式如何在通訊端上執行接收作業時配置和使用 IRP。
// Prototype for the receive IoCompletion routine
NTSTATUS
ReceiveComplete(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
);
// Function to receive data
NTSTATUS
ReceiveData(
PWSK_SOCKET Socket,
PWSK_BUF DataBuffer
)
{
PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch;
PIRP Irp;
NTSTATUS Status;
// Get pointer to the provider dispatch structure
Dispatch =
(PWSK_PROVIDER_CONNECTION_DISPATCH)(Socket->Dispatch);
// Allocate an IRP
Irp =
IoAllocateIrp(
1,
FALSE
);
// Check result
if (!Irp)
{
// Return error
return STATUS_INSUFFICIENT_RESOURCES;
}
// Set the completion routine for the IRP
IoSetCompletionRoutine(
Irp,
ReceiveComplete,
DataBuffer, // Use the data buffer for the context
TRUE,
TRUE,
TRUE
);
// Initiate the receive operation on the socket
Status =
Dispatch->WskReceive(
Socket,
DataBuffer,
0, // No flags are specified
Irp
);
// Return the status of the call to WskReceive()
return Status;
}
// Receive IoCompletion routine
NTSTATUS
ReceiveComplete(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
{
UNREFERENCED_PARAMETER(DeviceObject);
PWSK_BUF DataBuffer;
ULONG ByteCount;
// Check the result of the receive operation
if (Irp->IoStatus.Status == STATUS_SUCCESS)
{
// Get the pointer to the data buffer
DataBuffer = (PWSK_BUF)Context;
// Get the number of bytes received
ByteCount = (ULONG)(Irp->IoStatus.Information);
// Process the received data
...
}
// Error status
else
{
// Handle error
...
}
// Free the IRP
IoFreeIrp(Irp);
// Always return STATUS_MORE_PROCESSING_REQUIRED to
// terminate the completion processing of the IRP.
return STATUS_MORE_PROCESSING_REQUIRED;
}
上一個範例所示的模型,其中 WSK 應用程式會配置 IRP,然後在完成常式中釋放它,是 WSK 檔其餘部分的範例中使用的模型。
下列程式碼範例示範 WSK 應用程式如何在通訊端上執行接收作業時,使用由較高層級驅動程式或 I/O 管理員傳遞給它的 IRP。
// Prototype for the receive IoCompletion routine
NTSTATUS
ReceiveComplete(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
);
// Function to receive data
NTSTATUS
ReceiveData(
PWSK_SOCKET Socket,
PWSK_BUF DataBuffer,
PIRP Irp; // IRP from a higher level driver or the I/O manager
)
{
PWSK_PROVIDER_CONNECTION_DISPATCH Dispatch;
NTSTATUS Status;
// Get pointer to the provider dispatch structure
Dispatch =
(PWSK_PROVIDER_CONNECTION_DISPATCH)(Socket->Dispatch);
// Set the completion routine for the IRP such that it is
// only called if the receive operation succeeds.
IoSetCompletionRoutine(
Irp,
ReceiveComplete,
DataBuffer, // Use the data buffer for the context
TRUE,
FALSE,
FALSE
);
// Initiate the receive operation on the socket
Status =
Dispatch->WskReceive(
Socket,
DataBuffer,
0, // No flags are specified
Irp
);
// Return the status of the call to WskReceive()
return Status;
}
// Receive IoCompletion routine
NTSTATUS
ReceiveComplete(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
{
UNREFERENCED_PARAMETER(DeviceObject);
PWSK_BUF DataBuffer;
ULONG ByteCount;
// Since the completion routine was only specified to
// be called if the operation succeeds, this should
// always be true.
ASSERT(Irp->IoStatus.Status == STATUS_SUCCESS);
// Check the pending status of the IRP
if (Irp->PendingReturned == TRUE)
{
// Mark the IRP as pending
IoMarkIrpPending(Irp);
}
// Get the pointer to the data buffer
DataBuffer = (PWSK_BUF)Context;
// Get the number of bytes received
ByteCount = (ULONG)(Irp->IoStatus.Information);
// Process the received data
...
// Return STATUS_SUCCESS to continue the
// completion processing of the IRP.
return STATUS_SUCCESS;
}
如需使用 IRP 的詳細資訊,請參閱 處理 IRP。