ネイティブ アプリケーション内

Mark Russinovich。発行日: 2006 年 11 月 1 日

はじめに

NT のアーキテクチャにある程度精通している方は、Win32 アプリケーションで使用される API が "実際の" NT API ではないことをおそらく認識しているでしょう。 POSIX、OS/2、Win32 を含む NT の運用環境は、独自の API を介してクライアント アプリケーションと通信しますが、NT とは NT "ネイティブ" API を使用して通信します。 ネイティブ API はほとんど文書化されておらず、250 個の関数のうち約 25 個のみが Windows NT Device Driver Kit に記載されています。

ただし、ほとんどの人が、どのオペレーティング環境のクライアントでもない "ネイティブ" アプリケーションが NT 上に存在するということを知りません。 これらのプログラムはネイティブ NT API と通信し、Win32 などのオペレーティング環境 API を使用できません。 なぜこのようなプログラムが必要とされるのでしょうか。Win32 サブシステムを開始する前 (ログオン ボックスが表示される頃) に実行する必要があるプログラムは、ネイティブ アプリケーションである必要があります。 ネイティブ アプリケーションの最も明白な例は、初期化ブルー スクリーン中に chkdsk を実行する "autochk" プログラム (画面に "." を出力するプログラム) です。 当然ながら、Win32 オペレーティング環境サーバー、CSRSS.EXE (クライアント/サーバー ランタイム サブシステム) もネイティブ アプリケーションである必要があります。

この記事では、ネイティブ アプリケーションの構築方法と仕組みについて説明します。

Autochk の実行方法

Autochk は、NT のブートおよびシステム起動の各ドライバーが読み込まれるときと、ページングが有効になるときの間に実行されます。 この時点では、ブート シーケンス内でセッション マネージャー (smss.exe) が NT のユーザー モード環境を開始させており、他のプログラムはアクティブになっていません。 HKLM\System\CurrentControlSet\Control\Session Manager\BootExecute 値、MULTI_SZ には、セッション マネージャーによって実行されるプログラムの名前と引数が含まれており、ここに Autochk が指定されています。 この値を見ると、通常は次のようになります。ここで、"Autochk" は引数として "*" が渡されます。

Autocheck Autochk *

"セッション マネージャー" は、<winnt>\system32 ディレクトリを調べて、この値にリストされている実行可能ファイルを探します。 Autochk が実行されるときに、ファイルは全く開かれていないため、Autochk はブート ドライブを含む任意のボリュームを未加工モードで開いて、ディスク上のデータ構造を操作できます。 これは、後のどの時点でも実行できません。

ネイティブ アプリケーションのビルド

Microsoft は文書化していませんが、ネイティブ アプリケーションの作成方法を NT DDK Build ユーティリティは認識しています (また、これはおそらく Autochk のコンパイルに使用されます)。 デバイス ドライバーの場合と同じように、アプリケーションを定義する SOURCES ファイルに情報を指定します。 ただし、ドライバーが必要であることを Build に示す代わりに、SOURCES ファイル内にネイティブ アプリケーションが必要であることを次のように指示します。

TARGETTYPE=PROGRAM

Build ユーティリティは、それをガイドするために標準のメイクファイル \ddk\inc\makefile.def を使用します。これは、ネイティブ アプリケーションのコンパイル時に nt.lib という名前のランタイム ライブラリを探します。 残念ながら、Microsoft はこのファイルを DDK (Server 2003 DDK に含まれています) と共に発送しませんが、そのバージョンとリンクしている場合、ネイティブ アプリケーションは XP または Windows 2000 で実行されないと思われます。 ただし、Visual C++ のランタイム ライブラリ、msvcrt.lib を指定して nt.lib の選択をオーバーライドする行を makefile.def に含めると、この問題を回避できます

DDK の "Checked Build" 環境で Build を実行すると、%BASEDIR%\lib%CPU%\Checked の下に全デバッグ情報と共にネイティブ アプリケーションが生成され (例: c:\ddk\lib\i386\checked\native.exe)、"Free Build" 環境でこれを呼び出す場合、プログラムのリリース バージョンは %BASEDIR%\lib%CPU%\Free になります。 これらは、デバイス ドライバーイメージが Build によって配置されるのと同じ場所です。

ネイティブ アプリケーションには ".exe" ファイル拡張子がありますが、Win32 .exe のように実行できません。 試してみると、次のメッセージが表示されます。

アプリケーションは Windows NT モードで実行できません。

ネイティブ アプリケーション内

ネイティブ アプリケーションのエントリ ポイントは、winmain または main ではなく、NtProcessStartup です。 また、他の Win32 エントリ ポイントとは異なり、ネイティブ アプリケーションは、コマンド ライン引数を見つけるために、その唯一のパラメーターとして渡されるデータ構造に到達する必要があります。

ネイティブ アプリケーションのランタイム環境の大部分は、NT のネイティブ API エクスポート ライブラリである NTDLL.DLL によって提供されます。 ネイティブ アプリケーションは、独自のヒープを作成し、そこから NTDLL 関数である RtlCreateHeap を使用してストレージを割り当てる必要があります。 メモリは、RtlAllocateHeap を使用してヒープから割り当てられ、RtlFreeHeap を使用して解放されます。 ネイティブ アプリケーションが画面に何かを表示する場合は、関数 NtDisplayString を使用する必要があります。これは、初期化ブルー スクリーンに出力します。

ネイティブ アプリケーションは、戻るためのランタイム コードがないため、Win32 プログラムのようなスタートアップ関数から単に戻るわけではありません。 代わりに、NtProcessTerminate を呼び出して終了する必要があります。

NTDLL ランタイムは、ネイティブ アプリケーションがファイル I/O を実行し、デバイス ドライバーを操作し、プロセス間通信を実行できるようにする数百の関数で構成されています。 残念ながら、前に述べたように、これらの関数の大部分は文書化されていません。