搭配 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函式的InvokeOnSuccessInvokeOnErrorInvokeOnCancel參數指定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