レガシー Windows API(概要)について

ここで紹介しているAPIは、Windows95、Windows NT3.5時代のAPI群であり、最新のAPIは含まれていないことに注しして欲しい。なお、当時の解説書は現在よりもより詳細に解説しているため、参考になるはずである。

  • レイアウトを変換する際、崩れているページがあります。
  • 概要およびSampleコードはありますが、APIの解説はありません。

ご要望があれば旧版のWin32 API(HELP形式)をお譲りします。問い合わせフォームより申し込みください。

Windows API Win3とPMの入力処理の問題

Windowsバージョン3.x (以下Win3と略す) はノンプリエンプティブなオペレーティング システムであり、 それに基づいて入力システムが設計されています。ノンプリエンプティブなオペレーティング システムでは、 一度に1つのアプリケーションだけが入力を処理できるようになっています。このため、 入力に関する状態を簡単に同期させることができます。つまり、 ある状態のコピーは1つしか存在せず、 その状態がシステム全体でグローバルに共有されます。また、 (マウスやキーボードからの) 入力の処理手順が単純になります。入力は、 共有の「システム 入力 キュー」から読み取られ、 1つのアプリケーションに割り当てられます。そのようにして割り当てられたものが最新のシステムの状態を示し、 それによって完全な「入力の分類処理」が実現されます。

キューから順番にメッセージを読み取り、 それらを読み取った順番で一度にまとめて処理するために、 Win3システムでは一度に1つのアプリケーションだけがこのシステム キューからメッセージを読み取ることができるようになっています。つまり、 キーボード フォーカスをもつアプリケーション (キー メッセージの場合)、 またはマウス イベントが送られたアプリケーション (マウス メッセージの場合) のどちらかのアプリケーションだけが、 メッセージを読み取ることができます。この方式は単純ですが、 選択されたアプリケーションが何らかの理由 (内部的なハングアップ、 再計算、 ページ再編成、 印刷、塗りつぶし、 ファイルの保存、 ファイルの読み取り、 デバイスのタイムアウトなど) で入力を読み取らない場合には、 入力システムが完全にハングアップしてしまうのが欠点です。このことは、 ユーザーがどのような方法によっても、 アプリケーションを終了したり、 ほかのアプリケーションに制御を移したり、 あるいは使用したりできなくなることを意味します。

ノンプリエンプティブであるWindowsでは、 これでも大丈夫でした。ループに陥ったアプリケーションはYield を呼び出してほかのアプリケーションを実行させる必要がありましたが、 ループ中で入力を確認して処理しているかぎり、 システムは動作し続けます。ところが、 残念なことにこれとまったく同じ入力方式が、 プリエンプティブな環境であるPM (プレゼンテーション マネージャ) にも採用されています。このような環境では、 ユーザーが望んでいる本当のマルチタスク環境を実現することはできません。なぜなら、 入力スレッドが「常に」入力を読み取れる状態になるようにアプリケーションを記述することを開発者に期待して、 このようなシステム上の問題を解決してもらおうという要求は、 開発者にとって受け入れ難いことであることが明らかになったからです。一般のアプリケーションはこのようには記述されていませんし、 Windowsアプリケーションも確かにこのようには記述されていません。

Win32における入力処理の目標

Win32における入力処理は、 前述のような問題を解決します。Win32における入力処理の目標としては、 次のようなことが挙げられます。

1. ユーザーは、 いかなるアプリケーションからの妨げも受けずに、 任意のアプリケーションに直接入力することができる。これには、 次のような意味がある。

アプリケーションは、 入力のプライマリ プロセッサでもある任意のスレッドを使って時間のかかるタスクを実行することができ、 ハング アップすることも許される。

ユーザーはいつでも (システム内でハング アップしているアプリケーションの影響を受けずに) 任意のアプリケーションに直接入力ができる。

2. 原則としてWin16と互換性を保つ。

複数の実行スレッド

Windowsアプリケーションは、 新しい実行スレッドを簡単に作成することができ、 それらの実行スレッドの内部からユーザー インターフェイスを操作することができます。このようなスレッドに対しては、 ハードウェア イベントやウィンドウ メッセージを処理する際に特別な要求を出す必要はありません。

Win3では、 1つのアプリケーションが本質的に1つのスレッドを表します。各スレッドは、 アプリケーションの一連の初期化処理の中で、 そのスレッド専用として自動的に作成される入力キューを持っています。スレッドは、 たとえばウィンドウを作成して、 そのウィンドウに送られるメッセージを処理します。

一方、 Win32では、 1つのアプリケーションが多数のスレッドを表す場合があり、 どのスレッドもウィンドウを作成することが可能です。ウィンドウを作成したスレッドは、 そのウィンドウに送られたりポストされたりするウィンドウ メッセージを処理することになります。

PostMessage (hwnd, ...) は、 hwndを作成したスレッドに関連付けられているキューに、 ウィンドウ メッセージをポストします。つまり、 ウィンドウを作成したスレッドは、 それぞれメッセージ キューを持つことができます。このようなキューは多くの関数を識別するハンドルである、 ということもできます。「メッセージ キュー」はそのスレッド用として作成されるものであり、 「スレッド メッセージ キュー」とも呼ばれます。

スレッド メッセージ キューは、 関数の「要求時」に作成されます。これにより、 既存の関数の意味とパラメータが保持されます。スレッド メッセージ キューは外部オブジェクト型としては存在しません。

GetMessage関数およびPeekMessage関数は、 通常はアプリケーション キューからのメッセージを返しますが、 Win32ではスレッド キューを使ってメッセージを返します。GetMessage関数およびPeekMessage関数を呼び出したスレッドは、 スレッド自身のメッセージ キューからのメッセージを返します。

入力処理の流れ

入力を処理する順序は、 端的にいえば次のようになります。

1. ハードウェア デバイスが入力を検出し、 プロセッサが割り込みをかける。

2. ハードウェア入力がキューに入る。

3. ユーザー モード スレッドが起動され、 次に処理すべき未処理のハードウェア イベントを (発生した時間順に) 取得する。

4. ユーザー モード スレッドは、 どのアプリケーションが入力を取得すべきかをすばやく判断する。

5. 適切なスレッド入力キューに入力が置かれる。必要ならば、 キューを所有しているスレッドが起動される。

6. スレッドは、 メッセージ キューから入力を読み取り、 入力を送るべきウィンドウを判断する。最終的には、 ウィンドウ メッセージを作成する。

7. ウィンドウ メッセージがアプリケーションに返され、 アプリケーションがその入力を処理する。

ここでは詳しい説明は省きますが、 上記の処理がWin32における通常の入力処理手順になります。どのようなアプリケーションも複数のスレッドを持つことができ、 スレッドはそれぞれ自分に関連付けられるメッセージ キューを持つことができます (このことは前述の「複数の実行スレッド」のトピックで説明しているとおりです)。これらのスレッドは、 ほかのアプリケーションやスレッドの実行に影響を与えることなく、 また、 ユーザーがほかのウィンドウに入力を切り替えることを妨げることもなく、 比較的緩やかな速さで自分の入力キューから入力を読み取ることができます。

局所化される入力状態

従来のWin3PMのモデルとこのWindows NTのモデルとの唯一の違いは、 入力の所有権がシステム キューから読み取られるときに割り当てられる (Win3モデル) のではなく、 ユーザーからの入力時、 すなわち入力イベントの発生時に割り当てられる、 という点です。このようなイベントの処理順序の変更により、 システムの機能が著しく影響を受けます。その主な要因は、 複数のアプリケーションが同時に入力を処理している場合 (入力をたくわえておくなど) があるためです。入力が同期化される種々の状態 (キーボード状態、 フォーカス状態、 マウスのキャプチャ状態、 アクティブ状態など) はすべて、 その入力をグローバルに同期化させることがもはやできなくなります。たとえば、 2つのアプリケーションが同時にSetCaptureを呼び出す場合に、 呼び出すアプリケーション自身にとっては意味がありますが、 もう一方にとっては無意味なことになります。ほかにもこのような例はたくさんあります。

以上のような理由で、 スレッドは、 入力が同期化される種々の状態に関する情報をそれぞれ独自に持っています。言い換えれば、 スレッドはそれぞれ、 マウス キャプチャがどこにあるのか、 アクティブなウィンドウはどれか、 また、 どのウィンドウがフォーカスを持っているのか、 というイメージを個々に持っている、 ということになります。

入力システムは、 入力状態を次の2つのグループに分けて処理しています (グループの中にはさらにさまざまな入力状態が存在します)

局所スレッドをベースとした状態 (スレッド同期化状態)。この状態はスレッドの現在の入力同期化状態を反映し、 このスレッドだけが所有しているオブジェクトを参照する。

フォーカス ウィンドウ

アクティブ ウィンドウ

キャプチャ ウィンドウ

キー状態テーブルなど

ユーザー同期化状態。この状態は、 ユーザーが現在対象としているウィンドウの状態を反映する。

いちばん手前のウィンドウはどれか

アクティブなウィンドウはどれか

入力を受け取っているウィンドウはどれか

状態にはさまざまな種類があります。「ユーザー同期化状態」は、 ユーザーが直接入力を送るようシステムに指示したアプリケーションがどれかを単に示すものです。スレッド同期化状態は、 そのスレッドに対してローカルな (すなわち固有の) 状態です。スレッドの状態はスレッドが自分自身への入力を処理するときに変化します。

ユーザーの仕事は画面上の任意のアプリケーションに入力を送ることであり、 システムの仕事はこのユーザーの仕事を確実にすることです。アプリケーション自身にとっては、 通常は局所化された状態だけが問題になります。ユーザーがどのウィンドウに入力を送っているのかということはほとんど問題になりません。このため、 現在関数を呼び出しているルーチンは、 スレッド固有の入力同期化状態「だけ」を設定または取得します。

局所化された入力状態の影響を受ける関数

次に挙げる関数は、 局所化された入力状態の影響を受けます。これらの関数について詳しくは、 関数の説明を参照してください。

SetFocus

GetFocus

SetActiveWindow

GetActiveWindow

GetCapture

SetCapture

ReleaseCapture

一般に、 Get関数 (取得関数) は現在の局所スレッド状態だけを問い合わせ、 Set関数 (設定関数) はウィンドウを作成したスレッドに対してローカルな状態を設定します。現在のスレッドが渡されたウィンドウを作成したスレッドでない場合は、 現在のスレッドに関連する入力状態がNULLに設定され、 あたかもスレッド間で入力関連の状態が転送されたかのように扱われます。

この仕様は、 関数が入力同期化状態を返すというWin3における関数の意味を若干変更するものです。たとえば、 SetFocushwndを設定して呼び出すと正常終了を示すTRUEを返しますが、 その後にGetFocusを呼び出したときにNULLを返すことがあります。さらに本質的にいえば、 Win32においては、 呼び出し側スレッドがフォーカス ウィンドウを持っていない場合にGetFocusNULLを返します。Win3では、 通常はシステム内の何らかのウィンドウがキーボード フォーカスを持っているため、 GetFocusNULLを返すことはほとんどありません。

マウスのキャプチャについても、 このような仕様の拡張の影響を受けます。Win32のサーバー入力スレッドは、 入力スレッドがキャプチャを設定しようとする時期、 または設定を解除しようとする時期をあらかじめ知ることができません。また、 アプリケーションの入力状態にかかわらず、 システムは、 ユーザーに対していつでもほかのアプリケーションに入力を切り替えることができるようにしなければなりません。このため、 マウス キャプチャの意味も従来とは若干異なります。

キャプチャの変更方法とその時期の意味は変わりませんが、 アプリケーションがマウス入力を取得する方法とその時期が変わります。Win32サーバーは、 マウス ボタンを押してから離すまでの操作の間に、 元のマウス押下入力が送られるウィンドウを作成したスレッドのキューに、 すべてのマウス入力を送ります。これにより、 キューから入力が読み取られるときに、 入力スレッドがマウス キャプチャの処理を実行できるようになります。この結果、 マウスをキャプチャしている間マウス ボタンが押されると、 マウスが画面上のどこにあっても、 マウス ボタンが離されるかまたはキャプチャが解除されるまで、 マウスから生成された入力がすべてキャプチャ ウィンドウに送られます。また、 マウス ボタンが離されているときにスレッドがマウスのキャプチャを設定した場合には、 そのスレッドが作成したウィンドウの上にマウスが置かれているかぎり、 マウス キャプチャ ウィンドウにマウス イベントが送られます。

アクティブ化に関するZ順序

Win32は、 アプリケーションがハングした場合でもシステムの性能に影響を与えることがないように設計されており、 内部的には、 複数のアプリケーションが同時に入力を処理できるようになっています。ほとんどの入力同期化状態はスレッド固有にされます。ただし、 ウィンドウのZ順序 (配置順序とも呼ぶ、 ウィンドウの重なり順序を示すもの) については例外です。ウィンドウをいちばん手前にもってくること、 すなわちそのZ順序を変えることは、 ほとんどの場合、 マウスのクリックなどのユーザーが意図した操作に関連付けられ、 それによって入力に関連付けられます。

Z順序はスレッド固有の状態ではありませんが、 ウィンドウ マネージャが内部的に使用するグローバルなウィンドウ ツリーの状態と関係のある状態です。ふつう、 ユーザーは、 ハング アップしたアプリケーションにマウス クリックが送られることを望んでいません。なぜなら、 そのようにしてもアプリケーションが後でクリックを処理してウィンドウをいちばん手前に表示させるだけであり、 ユーザーが現在行っている別の作業ウィンドウの表示を妨げてしまうからです。

つまり、 アクティブ化に関するZ順序はほかのアプリケーションとの相対的なZ順序を変更するものではない、 ということです。たとえば、 アクティブ化に関するZ順序では、 ダイアログをそのアプリケーションの最も手前に表示させることはありますが、 ほかのすべてアプリケーションよりも手前に表示させることはありません。

ほとんどの場合、 アプリケーションのZ順序を変更できるのはユーザーの操作だけであり、 ユーザーが選んだアプリケーションに入力を切り替えるだけで、 Z順序が変更されます。ただ、 通常は起こりませんが、 アプリケーションがあらかじめ決められたウィンドウに入力を切り替えて、 そのウィンドウをいちばん手前に表示させることも実際にはあります。「困難なエラー」などの警告ウィンドウを表示するときや、 アプリケーションの起動時、 アプリケーションのシャットダウン時などにこのようなことが起こります。このときには、 SetForegroundWindowという特殊な関数が作成され、 呼び出されます。

ハング アップしたアプリケーションのウィンドウの枠やアイコンの消去と表示

スレッドまたはプロセスがハング アップすると、 システムはそのアプリケーションにユーザー インターフェイスを提供しなくなります。しかし、 システムは、 ハング アップしたウィンドウの枠をいったん消去して、 「応答しなくなった」ということを示す色を使って枠を描き直します。この枠の色は新しい色であったり、 または単に「使用不能」を示すハーフトーンの色であったりします。このような表示は、 アプリケーションが応答しないことをユーザーに示すための視覚的な補助情報になります。このほか、 応答しないことを示すためにマウス カーソルの形状を変えたり、 応答しない (と判断された) アプリケーションをクリックしていることをユーザーに通知したりするなどの方法が考えられます。ハング アップしたアプリケーションは移動やサイズ変更ができなくなり、 コントロール メニューにもアクセスできなくなります。このようなアプリケーションをクローズするには、 タスク マネージャに移り、 ハングしたアプリケーションを選択して[閉じる]ボタンをクリックします。

▲ページトップに戻る

【本を読んでもよくらからない】 … 個別指導でわかりやすくお教えします
inserted by FC2 system