関数の typedef を C++ ドライバ コードで使用して、PREƒast の結果を向上させる方法

最終更新日: 2007年5月2日

関数の typedef 宣言 (別名 : "役割型") を使用してコールバック関数が宣言されている場合、PREƒast for Drivers や Static Driver Verifier などの静的ツールは、よりよい結果を生成します。C で作成されたドライバの場合、これを行うのは容易です。しかし、C++ で作成されたドライバの場合、いくつかの変形があり、これについては例で示すのが最善でしょう。

このヒントでは、関数の typedef 宣言を C++ ドライバのコードに実装する方法の例をいくつか示します。ここで示す関数は、DriverEntry (typedef DRIVER_INITIALIZE) 関数です。しかし、同じ使用法パターンが、関数の typedef 宣言がある他のシステム定義のコールバックにも当てはまります。最初の数例は、特定のポイントを示しますが、動作するドライバに必要なすべての要素を含んでいるとは限らない点に注意してください。最後の例は、より完全なコードです。

*
トピック
DRIVER_INITIALIZE なしで宣言された DriverEntryDRIVER_INITIALIZE なしで宣言された DriverEntry
DRIVER_INITIALIZE 付きで宣言された DriverEntryDRIVER_INITIALIZE 付きで宣言された DriverEntry
静的メンバ関数として実装された DriverEntry静的メンバ関数として実装された DriverEntry
通常のメンバ関数として実装された DriverEntry通常のメンバ関数として実装された DriverEntry
より完全な例より完全な例

DRIVER_INITIALIZE なしで宣言された DriverEntry

最初の例は、DriverEntry 関数のように見えますが、DRIVER_INITIALIZE typedef を使用して宣言されてはいません。

NTSTATUS
DriverEntry(
   __in PDRIVER_OBJECT DriverObject,
   __in PUNICODE_STRING RegistryPath
   )
{   //... }

この関数を分析すると、PREƒast は、警告 28101「The Drivers module has inferred that the current function is a DriverEntry function.」を報告します。DriverEntry 関数が特別の場合である点に注意してください。PREƒast が DriverEntry 関数を認識する他の方法はないので、この関数は名前とシグネチャからの推論によって認識されます。PREƒast は、他の種類のコールバックを、コンテキストから識別できます。したがって、DriverEntry は、PREƒast がこの種の推論を行う唯一の関数です。

DRIVER_INITIALIZE 付きで宣言された DriverEntry

PREƒast は通常正しい推論を行うので、下記のスタイルでコールバックを宣言するのは正しいアクションです。これにより、警告 28101 がなくなり、チェックも強化されます。C++ で作成されたドライバ コードの場合、C スタイルのリンケージを作成し、リンカがこれをドライバ エントリのコールバックとして認識するには、extern "C" が必要です。この関数は DRIVER_INITIALIZE 関数であると宣言されているので、警告 28101 が出ることはありません。

extern "C"
DRIVER_INITIALIZE DriverEntry;

extern "C"
NTSTATUS
DriverEntry(
   __in PDRIVER_OBJECT DriverObject,
   __in PUNICODE_STRING RegistryPath
   )
{ //... }

静的メンバ関数として実装された DriverEntry

静的メンバ関数を使用して、DriverEntry を実装できますが、直接には行えません。C++ は、この関数のリンカ名をクラス名で修飾するので、この関数は正しくリンクされません。("Linker magic" を使用してこの問題を避けることもできますが、お勧めはしません。)

下記の例で示すように、クラス メンバ関数を呼び出す単純な間接ラッパーは、最も単純なソリューションです。それは、DRIVER_INITIALIZE 関数型として静的メンバ関数 sf_driverEntry を宣言します。extern "C" が現れていない点に注意してください。これは許容されていません。静的メンバ関数が DRIVER_INITIALIZE を使用して宣言されていない場合、PREƒast は警告 28101 を発行します。

class MyDriverClass
{
public:
    static DRIVER_INITIALIZE sf_DriverEntry;

};

NTSTATUS
MyDriverClass::sf_DriverEntry(
   __in PDRIVER_OBJECT DriverObject,
   __in PUNICODE_STRING RegistryPath
   )
{ //... }

通常のメンバ関数として実装された DriverEntry

PREƒast は、DriverEntry 関数のように見えるメンバ関数を、実際に DriverEntry 関数であると認識しません。なぜなら、言語がそれを許可していませんし、呼び出しの時点で "この" 有効なポインタをオペレーティング システムが提供しないからです。

たとえば、下記のクラス宣言での mf_DriverEntry メンバ関数は、通常のメンバ関数なので、DriverEntry 関数として認識されません。

class MyDriverClass
{
public:
	NTSTATUS
	mf_DriverEntry
	   __in PDRIVER_OBJECT DriverObject,
	   __in PUNICODE_STRING RegistryPath);

};

NTSTATUS
MyDriverClass::mf_DriverEntry(
   __in PDRIVER_OBJECT DriverObject,
   __in PUNICODE_STRING RegistryPath
   )
{  //... }

ただし、メンバ関数が明示的な typedef 宣言で宣言されている場合、PREƒast はこの関数を、DriverEntry 関数として認識します。なぜなら、この関数が DriverEntry 関数のコントラクト要件を満たすよう開発者が意図しているのは、typedef によって明らかだからです。この関数は、適切なメンバ ポインタにアクセスできる extern "C" ラッパー関数から呼び出された場合、それを使用して、必要な DriverEntry 機能を実際に実装できます。下記の例は、DRIVER_INITIALIZE 関数型として DriverEntry メンバ関数を宣言するクラス宣言を示します。

class DriverObject
{
public:
    DRIVER_INITIALIZE DriverEntry;
};

NTSTATUS
DriverObject::DriverEntry(
   __in PDRIVER_OBJECT DriverObject,
   __in PUNICODE_STRING RegistryPath
   )
{ //... }

より完全な例

下記の例は、C++ ドライバ コードで関数の typedef 宣言を使用する、ある可能なソリューションの関連部分をすべて示しています。この例で、DriverEntry は、C++ クラスである MyDriverClass の作成や初期化に必要な作業を少し行い、次に、mf_DriverEntry を C++ メンバ関数として呼び出し、作業を完了します。この例では、実際の作業で DriverEntry ルーチンが実行すると予想される、エラー処理とコードが省略されている点に注意してください。詳細については、Windows Driver Kit (WDK) のドキュメントで「Writing a DriverEntry Routine」を参照してください。

class MyDriverClass
{
    PDRIVER_OBJECT m_DriverObject;
public:

    DRIVER_INITIALIZE mf_DriverEntry;
    MyDriverClass();
};

MyDriverClass *g_DriverObject;

// The C linkage wrapper function
extern "C"
DRIVER_INITIALIZE DriverEntry;

extern "C"
NTSTATUS
DriverEntry(
   __in PDRIVER_OBJECT DriverObject,
   __in PUNICODE_STRING RegistryPath
   )
{
   // Create/Initialize class MyDriverClass with a pointer
   // to it in g_DriverObject.
   g_DriverObject = new MyDriverClass();
   if (g_DriverObject == NULL)
   {
       //...
   }

   NTSTATUS st = g_DriverObject
        ->mf_DriverEntry(DriverObject,RegistryPath);

   //...
}

// Implement the DriverEntry member function
NTSTATUS
MyDriverClass::mf_DriverEntry(
   __in PDRIVER_OBJECT DriverObject,
   __in PUNICODE_STRING RegistryPath
   )
{
   m_DriverObject = DriverObject;
   //...
}

詳細情報

PREƒast for Drivers

C++ for Kernel-Mode Drivers: Pros and Cons

Writing a DriverEntry Routine

『Developing Drivers with the Windows Driver Foundation』



この情報はお役に立ちましたか?