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.