Menggunakan IRP dengan Fungsi Kernel Winsock

Antarmuka Pemrograman Jaringan (NPI) Winsock Kernel (WSK) menggunakan IRP untuk penyelesaian operasi I/O jaringan asinkron. Setiap fungsi WSK mengambil pointer ke IRP sebagai parameter. Subsistem WSK menyelesaikan IRP setelah operasi yang dilakukan oleh fungsi WSK selesai.

IRP yang digunakan aplikasi WSK untuk meneruskan ke fungsi WSK dapat berasal dari salah satu cara berikut.

  • Aplikasi WSK mengalokasikan IRP dengan memanggil fungsi IoAllocateIrp . Dalam situasi ini, aplikasi WSK harus mengalokasikan IRP dengan setidaknya satu lokasi tumpukan I/O.

  • Aplikasi WSK menggunakan kembali IRP lengkap yang sebelumnya dialokasikan. Dalam situasi ini, WSK harus memanggil fungsi IoReuseIrp untuk menginisialisasi ulang IRP.

  • Aplikasi WSK menggunakan IRP yang diturunkan ke aplikasi tersebut baik oleh driver tingkat yang lebih tinggi atau oleh manajer I/O. Dalam situasi ini, IRP harus memiliki setidaknya satu lokasi tumpukan I/O yang tersisa yang tersedia untuk digunakan oleh subsistem WSK.

Setelah aplikasi WSK memiliki IRP yang digunakan untuk memanggil fungsi WSK, aplikasi ini dapat mengatur rutinitas IoCompletion agar IRP dipanggil ketika IRP selesai oleh subsistem WSK. Aplikasi WSK menetapkan rutinitas IoCompletion untuk IRP dengan memanggil fungsi IoSetCompletionRoutine . Tergantung pada bagaimana IRP berasal, rutinitas IoCompletion diperlukan atau opsional.

  • Jika aplikasi WSK mengalokasikan IRP, atau menggunakan kembali IRP yang sebelumnya dialokasikan, aplikasi tersebut harus mengatur rutinitas IoCompletion untuk IRP sebelum memanggil fungsi WSK. Dalam situasi ini, aplikasi WSK harus menentukan TRUE untuk parameter InvokeOnSuccess, InvokeOnError, dan InvokeOnCancel yang diteruskan ke fungsi IoSetCompletionRoutine untuk memastikan bahwa rutinitas IoCompletion selalu dipanggil. Selain itu, rutinitas IoCompletion yang ditetapkan untuk IRP harus selalu mengembalikan STATUS_MORE_PROCESSING_REQUIRED untuk mengakhiri pemrosesan penyelesaian IRP. Jika aplikasi WSK dilakukan menggunakan IRP setelah rutinitas IoCompletion dipanggil, maka harus memanggil fungsi IoFreeIrp untuk membebaskan IRP sebelum kembali dari rutinitas IoCompletion . Jika aplikasi WSK tidak membebaskan IRP, aplikasi tersebut dapat menggunakan kembali IRP untuk panggilan ke fungsi WSK lain.

  • Jika aplikasi WSK menggunakan IRP yang diteruskan ke dalamnya oleh driver tingkat yang lebih tinggi atau oleh manajer I/O, aplikasi tersebut harus mengatur rutinitas IoCompletion untuk IRP sebelum memanggil fungsi WSK hanya jika harus diberi tahu ketika operasi yang dilakukan oleh fungsi WSK telah selesai. Jika aplikasi WSK tidak menetapkan rutinitas IoCompletion untuk IRP, maka ketika IRP selesai, IRP akan diteruskan kembali ke driver tingkat yang lebih tinggi atau ke manajer I/O sesuai pemrosesan penyelesaian IRP normal. Jika aplikasi WSK menetapkan rutinitas IoCompletion untuk IRP, rutinitas IoCompletion dapat mengembalikan STATUS_SUCCESS atau STATUS_MORE_PROCESSING_REQUIRED. Jika rutinitas IoCompletion mengembalikan STATUS_SUCCESS, pemrosesan penyelesaian IRP akan berlanjut secara normal. Jika rutinitas IoCompletion mengembalikan STATUS_MORE_PROCESSING_REQUIRED, aplikasi WSK harus menyelesaikan IRP dengan memanggil IoCompleteRequest setelah selesai memproses hasil operasi yang dilakukan oleh fungsi WSK. Aplikasi WSK tidak boleh membebaskan IRP yang diteruskan oleh driver tingkat yang lebih tinggi atau oleh manajer I/O.

Catatan Jika aplikasi WSK menetapkan rutinitas IoCompletion untuk IRP yang diteruskan oleh driver tingkat yang lebih tinggi atau oleh manajer I/O, maka rutinitas IoCompletion harus memeriksa anggota IRP TertundaReturned dan memanggil fungsi IoMarkIrpPending jika anggota TertundaReturned adalah TRUE. Untuk informasi selengkapnya, lihat Menerapkan Rutinitas IoCompletion.

Catatan Aplikasi WSK tidak boleh memanggil fungsi WSK baru dalam konteks rutinitas IoCompletion . Melakukannya dapat mengakibatkan panggilan rekursif dan menghabiskan tumpukan mode kernel. Saat mengeksekusi di IRQL = DISPATCH_LEVEL, ini juga dapat menyebabkan kelaparan utas lainnya.

Aplikasi WSK tidak menginisialisasi RUN yang diteruskannya ke fungsi WSK selain mengatur rutinitas IoCompletion . Ketika aplikasi WSK meneruskan IRP ke fungsi WSK, subsistem WSK menyiapkan lokasi tumpukan I/O berikutnya atas nama aplikasi.

Contoh kode berikut menunjukkan bagaimana aplikasi WSK dapat mengalokasikan dan menggunakan IRP saat melakukan operasi penerimaan pada soket.

// 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 yang ditunjukkan dalam contoh sebelumnya, di mana aplikasi WSK mengalokasikan IRP dan kemudian membebaskannya dalam rutinitas penyelesaian, adalah model yang digunakan dalam contoh di seluruh sisa dokumentasi WSK.

Contoh kode berikut menunjukkan bagaimana aplikasi WSK dapat menggunakan IRP yang telah diteruskan ke aplikasi tersebut oleh driver tingkat yang lebih tinggi atau oleh manajer I/O saat melakukan operasi penerimaan pada soket.

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

Untuk informasi selengkapnya tentang menggunakan IRP, lihat Menangani IRP.