Utilisation des runtimes d’intégration avec les fonctions du noyau Winsock

L’interface de programmation réseau (NPI) du noyau Winsock (WSK) utilise des irps pour l’achèvement asynchrone des opérations d’E/S réseau. Chaque fonction WSK prend un pointeur vers un IRP en tant que paramètre. Le sous-système WSK termine l’IRP une fois l’opération effectuée par la fonction WSK terminée.

Une IRP qu’une application WSK utilise pour passer à une fonction WSK peut provenir de l’une des manières suivantes.

  • L’application WSK alloue l’IRP en appelant la fonction IoAllocateIrp . Dans ce cas, l’application WSK doit allouer l’IRP à au moins un emplacement de pile d’E/S.

  • L’application WSK réutilise un IRP terminé qu’elle a précédemment alloué. Dans ce cas, WSK doit appeler la fonction IoReuseIrp pour réinitialiser l’IRP.

  • L’application WSK utilise un IRP qui lui a été transmis par un pilote de niveau supérieur ou par le gestionnaire d’E/S. Dans ce cas, l’IRP doit avoir au moins un emplacement de pile d’E/S restant disponible pour une utilisation par le sous-système WSK.

Une fois qu’une application WSK dispose d’un IRP à utiliser pour appeler une fonction WSK, elle peut définir une routine IoCompletion pour que l’IRP soit appelée lorsque l’IRP est terminé par le sous-système WSK. Une application WSK définit une routine IoCompletion pour un IRP en appelant la fonction IoSetCompletionRoutine . Selon l’origine de l’IRP, une routine IoCompletion est obligatoire ou facultative.

  • Si l’application WSK a alloué l’IRP ou réutilisation un IRP qu’elle a précédemment alloué, elle doit définir une routine IoCompletion pour l’IRP avant d’appeler une fonction WSK. Dans ce cas, l’application WSK doit spécifier TRUE pour les paramètres InvokeOnSuccess, InvokeOnError et InvokeOnCancel passés à la fonction IoSetCompletionRoutine pour garantir que la routine IoCompletion est toujours appelée. En outre, la routine IoCompletion définie pour l’IRP doit toujours retourner STATUS_MORE_PROCESSING_REQUIRED pour arrêter le traitement d’achèvement de l’IRP. Si l’application WSK est effectuée à l’aide de l’IRP après l’appel de la routine IoCompletion , elle doit appeler la fonction IoFreeIrp pour libérer l’IRP avant de revenir de la routine IoCompletion . Si l’application WSK ne libère pas l’IRP, elle peut réutiliser l’IRP pour un appel à une autre fonction WSK.

  • Si l’application WSK utilise un IRP qui lui a été transmis par un pilote de niveau supérieur ou par le gestionnaire d’E/S, elle doit définir une routine IoCompletion pour l’IRP avant d’appeler la fonction WSK uniquement si elle doit être avertie de la fin de l’opération effectuée par la fonction WSK. Si l’application WSK ne définit pas de routine IoCompletion pour l’IRP, une fois l’IRP terminé, l’IRP est repassé au pilote de niveau supérieur ou au gestionnaire d’E/S en fonction du traitement normal de la fin de l’IRP. Si l’application WSK définit une routine IoCompletion pour l’IRP, la routine IoCompletion peut retourner STATUS_SUCCESS ou STATUS_MORE_PROCESSING_REQUIRED. Si la routine IoCompletion retourne STATUS_SUCCESS, le traitement de l’achèvement IRP se poursuit normalement. Si la routine IoCompletion retourne STATUS_MORE_PROCESSING_REQUIRED, l’application WSK doit terminer l’IRP en appelant IoCompleteRequest une fois qu’elle a terminé de traiter les résultats de l’opération effectuée par la fonction WSK. Une application WSK ne doit jamais libérer un IRP qui lui a été transmis par un pilote de niveau supérieur ou par le gestionnaire d’E/S.

Note Si l’application WSK définit une routine IoCompletion pour une IRP qui lui a été transmise par un pilote de niveau supérieur ou par le gestionnaire d’E/S, la routine IoCompletion doit case activée le membre PendingReturned de l’IRP et appeler la fonction IoMarkIrpPending si le membre PendingReturned a la valeur TRUE. Pour plus d’informations, consultez Implémentation d’une routine IoCompletion.

Note Une application WSK ne doit pas appeler de nouvelles fonctions WSK dans le contexte de la routine IoCompletion . Cela peut entraîner des appels récursifs et épuiser la pile du mode noyau. Lors de l’exécution à IRQL = DISPATCH_LEVEL, cela peut également entraîner une pénurie d’autres threads.

Une application WSK n’initialise pas les IRP qu’elle transmet aux fonctions WSK, à l’exception de la définition d’une routine IoCompletion . Lorsqu’une application WSK transmet un IRP à une fonction WSK, le sous-système WSK configure l’emplacement de pile d’E/S suivant pour le compte de l’application.

L’exemple de code suivant montre comment une application WSK peut allouer et utiliser un IRP lors de l’exécution d’une opération de réception sur un socket.

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

Le modèle présenté dans l’exemple précédent, où l’application WSK alloue un IRP, puis le libère dans la routine d’achèvement, est le modèle utilisé dans les exemples tout au long du reste de la documentation WSK.

L’exemple de code suivant montre comment une application WSK peut utiliser une IRP qui lui a été passée par un pilote de niveau supérieur ou par le gestionnaire d’E/S lors de l’exécution d’une opération de réception sur un socket.

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

Pour plus d’informations sur l’utilisation des irps, consultez Gestion des irps.