Winsock カーネル アプリケーションの登録

WSK クライアント オブジェクトの登録

Winsock カーネル (WSK) アプリケーションは、WskRegister 関数を呼び出して WSK クライアントとして登録する必要があります。 WskRegister では、その WSK クライアントのネットワーク プログラミング インターフェイス (NPI)(WSK_CLIENT_NPI 構造) と WSK 登録オブジェクト (WSK_REGISTRATION 構造) (正常に戻ったときに WskRegister によって初期化されます) へのポインターを WSK アプリケーションが初期化して渡す必要があります。

次のコード例は、WSK アプリケーションを WSK クライアントとして登録する方法を示しています。

// Include the WSK header file
#include "wsk.h"

// WSK Client Dispatch table that denotes the WSK version
// that the WSK application wants to use and optionally a pointer
// to the WskClientEvent callback function
const WSK_CLIENT_DISPATCH WskAppDispatch = {
  MAKE_WSK_VERSION(1,0), // Use WSK version 1.0
  0,    // Reserved
  NULL  // WskClientEvent callback not required for WSK version 1.0
};

// WSK Registration object
WSK_REGISTRATION WskRegistration;

// DriverEntry function
NTSTATUS
  DriverEntry(
    PDRIVER_OBJECT DriverObject,
    PUNICODE_STRING RegistryPath
    )
{
  NTSTATUS Status;
  WSK_CLIENT_NPI wskClientNpi;

  .
  . 
  .

  // Register the WSK application
  wskClientNpi.ClientContext = NULL;
  wskClientNpi.Dispatch = &WskAppDispatch;
  Status = WskRegister(&wskClientNpi, &WskRegistration);

  if(!NT_SUCCESS(Status)) {
      .
      .
      .
      return Status;
  }

  .
  . 
  .
}

WSK アプリケーションは、DriverEntry 関数内から WskRegister を呼び出す必要はありません。 たとえば、WSK アプリケーションが複雑なドライバーのサブコンポーネントである場合、WSK アプリケーション サブコンポーネントがアクティブ化される際にのみ、アプリケーションの登録が発生する可能性があります。

wskDeregister が呼び出され、登録が無効になるまで、WSK アプリケーションは、WskRegister に渡された WSK_CLIENT_DISPATCH 構造を有効な状態に保ち、メモリ内に常駐させる必要があります。 さらに、WSK アプリケーションが他の WSK 登録関数の呼び出しを停止するまで、WSK_REGISTRATION 構造は有効であり、メモリ内に常駐している必要があります。 前のコード例では、ドライバーのグローバル データ セクションにこれら 2 つの構造を保持しており、ドライバーがアンロードされるまで構造データをメモリに保持しています。

WSK プロバイダー NPI キャプチャ

WSK アプリケーションは、WskRegister に WSK クライアントとして登録されると、WSK インターフェイスの使用を開始するため、WskCaptureProviderNPI 関数を使用して WSK サブシステムから WSK プロバイダー NPI をキャプチャする必要があります。

WSK アプリケーションが WSK プロバイダー NPI のキャプチャを試みたとき、WSK サブシステムの準備がまだできていない可能性があるため、WskCaptureProviderNPI 関数を使用することで、WSK アプリケーションが WSK サブシステムをポーリングしたり、WSK サブシステムの準備が整うのを待機したりできるようになります。

  • WaitTimeout パラメーターが WSK_NO_WAIT の場合は必ず、関数は待機せずにすぐに戻ります。

  • WaitTimeout が WSK_INFINITE_WAIT の場合、関数は WSK サブシステムの準備ができるまで待機します。

  • WaitTimeout が他の値である場合、関数は WSK サブシステムの準備が整ったとき、または待機時間 (ミリ秒単位) が WaitTimeout の値に達したとき (どちらか早い方) に値を返します。

重要 他のドライバーやサービスの開始に悪影響を与えないようにするため、その DriverEntry 関数から WskCaptureProviderNPI を呼び出す WSK アプリケーションで、WaitTimeout パラメーターを WSK_INFINITE_WAIT または過剰な待機時間に設定しないでください。 さらに、WSK アプリケーションがシステム起動フェーズの非常に早い段階で開始される場合、DriverEntry が実行されるワーカー スレッドとは異なるワーカー スレッドで WSK サブシステムが準備できるようになるのを待つ必要があります。

wskCaptureProviderNPI の呼び出しが STATUS_NOINTERFACE で失敗した場合、WSK アプリケーションは WskQueryProviderCharacteristics 関数を使用して、WSK サブシステムによってサポートされている WSK NPI バージョンの範囲を検出できます。 WSK アプリケーションは、WskDeregister を呼び出して現在の登録インスタンスの登録を解除してから、サポートされている WSK NPI バージョンを使用する別の WSK_CLIENT_DISPATCH インスタンスを使用して再登録できます。

WskCaptureProviderNPI が値を正常に返すと、その WskProviderNpi パラメーターは、WSK アプリケーションによって使用できる WSK プロバイダー NPI (WSK_PROVIDER_NPI) をポイントします。 WSK_PROVIDER_NPI 構造には、WSK クライアント オブジェクト (WSK_CLIENT) へのポインターと、WSK アプリケーションが WSK ソケットの作成やその他の操作を WSK クライアント オブジェクトで実行するために使用できる WSK 関数の WSK_PROVIDER_DISPATCH ディスパッチ テーブルが含まれています。 WSK アプリケーションは、WSK_PROVIDER_DISPATCH 関数の使用が完了したら、WskReleaseProviderNPI を呼び出して WSK プロバイダー NPI を解放する必要があります。

次のコード例は、WSK アプリケーションが WSK プロバイダー NPI をキャプチャし、それを使用してソケットを作成した後、それを解放する方法を示しています。

// WSK application routine that waits for WSK subsystem
// to become ready and captures the WSK Provider NPI
NTSTATUS
  WskAppWorkerRoutine(
    )
{
  NTSTATUS Status;
  WSK_PROVIDER_NPI wskProviderNpi;
 
  // Capture the WSK Provider NPI. If WSK subsystem is not ready yet,
  // wait until it becomes ready.
  Status = WskCaptureProviderNPI(
    &WskRegistration, // must have been initialized with WskRegister
    WSK_INFINITE_WAIT,
    &wskProviderNpi
    );

  if(!NT_SUCCESS(Status))
  {
    // The WSK Provider NPI could not be captured.
    if( Status == STATUS_NOINTERFACE ) {
      // WSK application's requested version is not supported
    }
    else if( status == STATUS_DEVICE_NOT_READY ) {
      // WskDeregister was invoked in another thread thereby causing
      // WskCaptureProviderNPI to be canceled.
    } 
    else {
      // Some other unexpected failure has occurred
    }

    return Status;
  }

  // The WSK Provider NPI has been captured.
  // Create and set up a listening socket that accepts
   // incoming connections.
  Status = CreateListeningSocket(&wskProviderNpi, ...);

  // The WSK Provider NPI will not be used any more.
  // So, release it here immediately.
  WskReleaseProviderNPI(&WskRegistration);

  // Return result of socket creation routine
  return Status;

}

WSK アプリケーションは、WskCaptureProviderNPI を複数回呼び出すことができます。 WskCaptureProviderNPI を呼び出して値が正常に返されるたびに、WskReleaseProviderNPI への対応する呼び出しが必要です。 WSK アプリケーションは、WskReleaseProviderNPI を呼び出した後、WSK_PROVIDER_DISPATCH 内の関数をそれ以上呼び出してはなりません。