Atom テーブルについて

Atom テーブル は、文字列と対応する識別子を格納するシステム定義テーブルです。 アプリケーションは、atom テーブルに文字列を配置し、その文字列にアクセスするために使用できる 16 ビット整数 (atom と呼ばれます) を受け取ります。 Atom テーブルに配置された文字列は、 Atom名と呼ばれます。

システムは、多数の atom テーブルを提供します。 各 atom テーブルは異なる目的を果たします。 たとえば、Dynamic Data Exchange (DDE) アプリケーションでは、 グローバル Atom テーブル を使用して、項目名とトピック名の文字列を他のアプリケーションと共有します。 DDE アプリケーションは、実際の文字列を渡すのではなく、グローバルAtomをパートナー アプリケーションに渡します。 パートナーは、Atomを使用して Atom テーブルから文字列を取得します。

アプリケーションでは、ローカル Atom テーブルを使用して、独自の項目名の関連付けを格納できます。

システムは、アプリケーションから直接アクセスできない atom テーブルを使用します。 ただし、アプリケーションでは、さまざまな関数を呼び出すときにこれらのAtomを使用します。 たとえば、登録済みのクリップボード形式は、システムで使用される内部Atom テーブルに格納されます。 アプリケーションでは、 RegisterClipboardFormat 関数を使用して、この atom テーブルにAtomを追加します。 また、登録されたクラスは、システムによって使用される内部Atom テーブルに格納されます。 アプリケーションは、 RegisterClass または RegisterClassEx 関数を使用して、この atom テーブルにAtomを追加します。

このセクションでは、次のトピックについて説明します。

グローバルAtomテーブル

グローバル Atom テーブルは、すべてのアプリケーションで使用できます。 アプリケーションがグローバルAtomテーブルに文字列を配置すると、システムはシステム全体で一意のAtomを生成します。 Atomを持つアプリケーションは、グローバル Atom テーブルに対してクエリを実行することで、識別する文字列を取得できます。

他のアプリケーションとデータを共有するためのプライベート DDE データ形式を定義するアプリケーションでは、フォーマット名をグローバル Atom テーブルに配置する必要があります。 この手法は、システムまたは他のアプリケーションによって定義された形式の名前との競合を防ぎ、メッセージまたは形式の識別子 (Atom) を他のアプリケーションで使用できるようにします。

User Atom テーブル

グローバルAtomテーブルに加えて、ユーザーAtomテーブルは、すべてのプロセス間で共有される別のシステムAtomテーブルです。 ユーザーAtomテーブルは、win32k内部の少数のシナリオに使用されます。たとえば、Windows モジュール名、win32k の既知の文字列、OLE 形式などです。アプリケーションはユーザー Atom テーブルと直接対話しませんが、 RegisterClassRegisterWindowMessageRegisterClipboardFormatなど、ユーザーAtom テーブルにエントリを追加するいくつかの API を呼び出します。 RegisterClass によって追加 されたエントリを UnregisterClassで削除できます。 ただし、 RegisterWindowMessageRegisterClipboardFormat で追加されたエントリはセッションが終了するまで削除されません。 ユーザーAtomテーブルに空き領域がなく、渡される文字列がまだテーブルにない場合、呼び出しは失敗します。

Atom テーブルのサイズ

CreateWindowを含む多くの重要な API は、ユーザーAtomに依存しています。 したがって、ユーザーAtomテーブルのスペース枯渇は重大な問題になります。たとえば、すべてのアプリケーションの起動に失敗する場合があります。 アプリケーションで Atom テーブルを効率的に利用し、アプリケーションとシステムの信頼性とパフォーマンスを維持するための推奨事項を次に示します。

  1. アプリのユーザー Atom テーブルの使用を制限する必要があります。 RegisterClassRegisterWindowMessage、 または RegisterClipboardFormat などの API を使用して一意の文字列を格納したり、ユーザーAtom テーブルにスペースを取ったりします。これは、文字列を使用してウィンドウ クラスを登録するために他のアプリによってグローバルに使用されます。 可能であれば、 AddAtom/DeleteAtom を使用して文字列をローカルAtomテーブルに格納するか、Atomがクロスプロセスで必要な場合は、 GlobalAddAtom/GlobalDeleteAtom を使用する必要があります。

  2. アプリケーションがユーザー atom テーブルの問題を引き起こしている懸念がある場合は、カーネル デバッガーを接続し、 UserAddAtomEx (bae1 win32kbase!UserAddAtomEx /p <eprocess> "kc10;g") の呼び出しでプロセスを中断することで、根本原因を調査できます。 呼び出し履歴の user32! を探して、呼び出されている API を確認します。 この手法は、「グローバルAtom テーブル リークの特定」で説明されているグローバルAtom テーブルの問題検出に似ています。 ユーザーAtomテーブルの内容をダンプするもう 1 つの方法は、0xC000から0xFFFFまでの可能なAtomの範囲に対して GetClipboardFormatName を呼び出すことです。 アプリケーションの実行中にAtom数の合計が着実に増加する場合、またはアプリが閉じられたときにベースラインに戻らない場合は、問題があります。

Local Atom テーブル

アプリケーションでは、ローカル Atom テーブルを使用して、アプリケーション内でのみ使用される多数の文字列を効率的に管理できます。 これらの文字列と関連するAtomは、テーブルを作成したアプリケーションでのみ使用できます。

複数の構造体で同じ文字列を必要とするアプリケーションでは、ローカル Atom テーブルを使用してメモリ使用量を削減できます。 アプリケーションでは、文字列を各構造体にコピーするのではなく、atom テーブルに文字列を配置し、結果のAtomを構造体に含めることができます。 この方法では、文字列はメモリ内で 1 回だけ表示されますが、アプリケーションで何度も使用できます。

アプリケーションでは、ローカルの Atom テーブルを使用して、特定の文字列を検索するときの時間を節約することもできます。 検索を実行するには、アプリケーションで必要なのは、atom テーブルに検索文字列を配置し、結果のAtomと関連する構造体のAtomを比較することだけです。 通常、Atomの比較は文字列を比較するよりも高速です。

Atom テーブルはハッシュ テーブルとして実装されます。 既定では、ローカル Atom テーブルではハッシュ テーブルに 37 個のバケットが使用されます。 ただし、 InitAtomTable 関数を呼び出すことによって使用されるバケットの数を変更できます。 ただし、アプリケーションが InitAtomTable を呼び出す場合は、他の atom-management 関数を呼び出す前にこれを行う必要があります。

Atomの種類

アプリケーションでは、文字列原子と整数原子の 2 種類のAtomを作成できます。 整数値と文字列原子の値は重複しないため、両方の種類のAtomを同じコード ブロックで使用できます。

いくつかの関数は、パラメーターとして文字列または原子を受け取ります。 これらの関数にAtomを渡す場合、アプリケーションは MAKEINTATOM マクロを使用して、関数で使用できる形式にAtomを変換できます。

以下のセクションでは、Atomの型について説明します。

String Atoms

アプリケーションは、Null で終わる文字列を GlobalAddAtomAddAtomGlobalFindAtomFindAtom 関数に渡すと、 文字列Atom (16 ビット整数) を受け取ります。 文字列 Atom には、次の特徴があります。

  • 文字列Atomの値は、0xC000 (MAXINTATOM) ~ 0xFFFFの範囲内です。
  • Atom テーブル内のAtom名を検索する場合、大文字と小文字は区別されません。 また、検索操作では文字列全体が一致する必要があります。部分文字列の一致は実行されません。
  • 文字列Atomに関連付けられている文字列のサイズは 255 バイト以下です。 この制限は、すべての Atom 関数に適用されます。
  • 参照カウント は、各Atom名に関連付けられます。 このカウントは、Atom名がテーブルに追加されるたびにインクリメントされ、Atom名が削除されるたびにデクリメントされます。 これにより、同じ文字列Atomの異なるユーザーが互いのAtom名を破棄できなくなります。 Atom名の参照カウントが 0 の場合、システムはテーブルから atom と atom 名を削除します。

Integer Atoms

整数の原子は、次の点で文字列の原子とは異なります。

  • 整数値は、0xBFFF (MAXINTATOM – 1) 0x0001範囲内にあります。
  • Integer Atom の文字列形式は #ddddで、 dddd で表される値は 10 進数です。 先行 0 は無視されます。
  • Integer Atomに関連付けられた参照カウントまたはストレージ オーバーヘッドはありません。

Atom の作成と使用数

アプリケーションは、 AddAtom 関数を呼び出してローカル Atomを作成し、 GlobalAddAtom 関数を呼び出してグローバルAtomを作成します。 どちらの関数も文字列へのポインターを必要とします。 システムは、適切な atom テーブルで文字列を検索し、対応する atom をアプリケーションに返します。 文字列Atomの場合、文字列が既に atom テーブルに存在する場合、システムはこのプロセス中に文字列の参照カウントをインクリメントします。

同じAtom名を追加する呼び出しを繰り返し実行すると、同じAtomが返されます。 AddAtom の呼び出し時にAtom名がテーブルに存在しない場合は、Atom名がテーブルに追加され、新しいAtomが返されます。 文字列Atomの場合、その参照カウントも 1 に設定されます。

アプリケーションは、ローカルAtomを使用する必要がなくなったときに DeleteAtom 関数を呼び出す必要があります。グローバルAtomが不要になったら GlobalDeleteAtom 関数を呼び出す必要があります。 文字列原子の場合、これらの関数のいずれかが対応する原子の参照数を 1 ずつ減らします。 参照カウントが 0 に達すると、システムはテーブルからAtom名を削除します。

グローバル Atom テーブル内の文字列AtomのAtom名はメインテーブルに配置されたアプリケーションが終了した後でも、その参照カウントが 0 より大きい限り、再び指定します。 テーブル内のAtomの参照カウントに関係なく、関連付けられているアプリケーションが終了すると、ローカル Atom テーブルが破棄されます。

Atom-Table クエリ

アプリケーションは、FindAtom 関数または GlobalFindAtom 関数を使用して、特定の文字列が既に Atom テーブル内にあるかどうかを判断できます。 これらの関数は、指定された文字列の atom テーブルを検索し、文字列がある場合は対応するAtomを返します。

アプリケーションは、 GetAtomName または GlobalGetAtomName 関数を使用して、atom テーブルから atom-name 文字列を取得できます。これは、アプリケーションが検索する文字列に対応するAtomを持っている場合です。 どちらの関数も、指定したAtomの atom-name 文字列をバッファーにコピーし、コピーされた文字列の長さを返します。 GetAtomName はローカル Atom テーブルから atom-name 文字列を取得し、 GlobalGetAtomName はグローバル Atom テーブルから atom-name 文字列を取得します。

Atom文字列形式

AddAtomGlobalAddAtomFindAtom、および GlobalFindAtom 関数は、null で終わる文字列へのポインターを受け取ります。 アプリケーションでは、次のいずれかの方法でこのポインターを指定できます。

文字列の形式 説明
#dddd 10 進文字列として指定された整数。 整数の原子を作成または検索するために使用されます。
文字列Atom名 文字列 Atom 名です。 Atom テーブルに文字列Atom名を追加し、その代わりに atom を受け取るために使用します。