Udostępnij za pośrednictwem


Używanie IRPs z funkcjami Winsock Kernel

Winsock Kernel (WSK) Interfejs Programowania Sieciowego (NPI) używa IRP do asynchronicznego zakończenia operacji I/O w sieci. Każda funkcja WSK przyjmuje wskaźnik do IRP jako parametr. Podsystem WSK kończy IRP po zakończeniu operacji wykonywanej przez funkcję WSK.

Protokół IRP używany przez aplikację WSK do przekazywania do funkcji WSK może pochodzić z jednego z następujących sposobów.

  • Aplikacja WSK przydziela IRP, wywołując funkcję IoAllocateIrp. W takiej sytuacji aplikacja WSK musi przydzielić IRP z przynajmniej jednym miejscem stosu I/O.

  • Aplikacja WSK ponownie wykorzystuje zakończony IRP, który został wcześniej przydzielony. W takiej sytuacji WSK musi wywołać funkcję IoReuseIrp, aby ponownie zainicjować IRP.

  • Aplikacja WSK używa protokołu IRP przekazanego do niego przez sterownik wyższego poziomu lub przez menedżera we/wy. W takiej sytuacji IRP musi mieć co najmniej jedną dostępną lokalizację stosu wejścia/wyjścia dostępną do użycia przez podsystem WSK.

Gdy aplikacja WSK ma protokół IRP do użycia do wywoływania funkcji WSK, może ustawić IoCompletion procedurę wywołania protokołu IRP, gdy protokół IRP zostanie ukończony przez podsystem WSK. Aplikacja WSK ustawia procedurę IoCompletion dla protokołu IRP przez wywołanie funkcji IoSetCompletionRoutine. W zależności od sposobu utworzenia protokołu IRP wymagana jest procedury IoCompletion lub opcjonalna.

  • Jeśli aplikacja WSK przydzieliła IRP lub ponownie używa wcześniej przydzielonego IRP, musi ustawić procedurę IoCompletion dla IRP przed wywołaniem funkcji WSK. W takiej sytuacji aplikacja WSK musi określić TRUE dla InvokeOnSuccess, InvokeOnErrori InvokeOnCancel parametrów przekazywanych do funkcji IoSetCompletionRoutine w celu zapewnienia, że procedura IoCompletion jest zawsze wywoływana. Procedura IoCompletion, która jest ustawiona dla IRP, musi zawsze zwracać STATUS_MORE_PROCESSING_REQUIRED, aby zakończyć przetwarzanie IRP. Jeśli aplikacja WSK jest wykonywana przy użyciu protokołu IRP po wywołaniu procedury IoCompletion, należy wywołać funkcję IoFreeIrp, aby zwolnić protokół IRP przed powrotem z procedury IoCompletion. Jeśli aplikacja WSK nie zwalnia protokołu IRP, może ponownie użyć protokołu IRP dla wywołania innej funkcji WSK.

  • Jeśli aplikacja WSK używa IRP, które zostało mu przekazane przez sterownik wyższego poziomu lub przez menedżera we/wy, powinna ustawić IoCompletion procedurę dla IRP przed wywołaniem funkcji WSK tylko wtedy, gdy musi być powiadomiona, gdy operacja wykonywana przez funkcję WSK została ukończona. Jeśli aplikacja WSK nie ustawi IoCompletion procedury dla IRP, to po zakończeniu IRP zostanie ono przekazane z powrotem do sterownika wyższego poziomu lub do menedżera we/wy zgodnie z normalnym procesem kończenia IRP. Jeśli aplikacja WSK ustawia procedurę IoCompletion dla protokołu IRP, procedura IoCompletion może zwracać STATUS_SUCCESS lub STATUS_MORE_PROCESSING_REQUIRED. Jeśli procedury IoCompletion zwraca STATUS_SUCCESS, przetwarzanie ukończenia IRP będzie kontynuowane normalnie. Jeśli procedura IoCompletion zwraca STATUS_MORE_PROCESSING_REQUIRED, musi aplikacja WSK ukończyć IRP, wywołując IoCompleteRequest po ukończeniu przeanalizowania wyników operacji wykonanej przez funkcję WSK. Aplikacja WSK nigdy nie powinna zwolnić IRP, który został przekazany do niej przez sterownik wyższego poziomu lub przez menedżera we/wy.

Uwaga Jeśli aplikacja WSK ustawia procedurę IoCompletion dla IRP, który został przekazany do niej przez sterownik wyższego poziomu lub przez menedżera we/wy, wtedy procedura IoCompletion musi sprawdzić członka PendingReturned struktury IRP i wywołać funkcję IoMarkIrpPending, jeśli członek PendingReturned jest TRUE. Aby uzyskać więcej informacji, zobacz Implementowanie procedury IoCompletion.

Uwaga aplikacja WSK nie powinna wywoływać nowych funkcji WSK w kontekście procedury IoCompletion. Może to spowodować rekursywne wywołania i wyczerpanie stosu trybu jądra. Podczas wykonywania na IRQL = DISPATCH_LEVEL może to również powodować blokowanie innych wątków.

Aplikacja WSK nie inicjuje IRP, które przekazuje do funkcji WSK innych niż ustawienie procedury IoCompletion. Gdy aplikacja WSK przekazuje protokół IRP do funkcji WSK, podsystem WSK konfiguruje następną lokalizację stosu we/wy w imieniu aplikacji.

W poniższym przykładzie kodu pokazano, jak aplikacja WSK może przydzielić i użyć protokołu IRP podczas wykonywania operacji odbierania na gniazdach.

// 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;
}

Model pokazany w poprzednim przykładzie, w którym aplikacja WSK przydziela IRP, a następnie zwalnia ją w procedurze uzupełniania, jest modelem używanym w przykładach w pozostałej części dokumentacji WSK.

Poniższy przykład kodu pokazuje, jak aplikacja WSK może używać protokołu IRP przekazanego do niego przez sterownik wyższego poziomu lub przez menedżera we/wy podczas wykonywania operacji odbierania na gniazdach.

// 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;
}

Aby uzyskać więcej informacji na temat korzystania z IRPs, zobacz Handling IRPs.