アトムによる文字列の参照

Windows API では、しばしばアプリケーション内で文字列を使ったデータの識別を行います。アプリケーションで管理しているデータを文字列を用いて識別するとき、アトムと呼ばれる仕組みを利用すると便利です。アトムは、文字列と整数を関連付けるテーブルを用意することで、整数から文字列を検索したり参照したりできる機能を提供しています。

ゼロからはじめるWindows API - WinMain 関数 すべての始まり編
C言語の学習および環境の構築は下記を参考にしてください
1.ゼロからはじめるC言語 - 環境構築編
2.ゼロからはじめるC言語 - 関数編
3.ゼロからはじめるC言語 - 型・定数編
4.ゼロからはじめるC言語 - 変数編
5.ゼロからはじめるC言語 - 選択編
6.ゼロからはじめるC言語 - 繰り返し編
7.ゼロからはじめるC言語 - 配列編
8.ゼロからはじめるC言語 - 構造体編
9.ゼロからはじめるC言語 - 自作関数編
10.ゼロからはじめるC言語 - ポインタ編

例えば、アプリケーションに動的に登録されたオブジェクトを文字列による ID で管理している場合、事前にアトムに変換することで比較や参照処理が軽量になります。オブジェクトの識別に用いる文字列は、Windows API によって管理されるアトムテーブルに集約されるため、重複する文字列がメモリ内に分散することを防げます。文字列を参照するアトムは 2 バイトの整数なので、文字列のままでデータを受けたしたり比較するよりも効率的です。

新しく文字列をアトムテーブルに登録するには AddAtom() 関数を使います。

AddAtom() 関数

パラメータ
lpString 最大 255 文字までの、登録する文字列

戻り値

新しく作成されたアトム。失敗すると 0。

すでに登録されている文字列が lpString パラメータに指定された場合、内部の参照カウントを 1 増やします。同じ文字列が重複して登録されることはありません。戻り値の ATOM 型は 2 バイト型を表す WORD 型の別名であり、WORD 型は unsigned short の別名です。

ATOM 型の定義

typedef unsigned short WORD;
typedef WORD ATOM;

AddAtom() 関数から得られた ATOM 型の値を保持することで、登録された文字列を参照できます。すでに登録されている文字列のアトムを取得したい場合は FindAtom() 関数を使います。

FindAtom() 関数

ATOM FindAtom(
  LPCTSTR lpString
);

パラメータ

lpString アトムテーブルから検索する文字列

戻り値

指定した文字列に関連付けられているアトム。失敗すると 0

FindAtom() 関数は、登録済みの文字列に関連付けられているアトムを取得するために用います。アトムテーブル内に保持されている文字列は、AddAtom() 関数に渡した文字列が維持されますが、アトムの検索は大文字と小文字を区別しません。

アトムから、登録されている文字列に復元することも可能です。アトムに対応する文字列を取得するには GetAtomName() 関数を使います。

GetAtomName() 関数

UINT GetAtomName(
  ATOM nAtom,
  LPTSTR lpBuffer,
  int nSize
);

パラメータ

nAtom 取得する文字列を参照するアトム
lpBuffer 文字列を格納するバッファへのポインタ
nSize lpBuffer パラメータに指定したバッファのサイズ(TCHAR 単位)

戻り値

バッファにコピーされた文字列の長さ。失敗すると 0

登録した文字列をアトムテーブルから削除するには DeleteAtom() 関数を使います。この関数は、AddAtom() 関数に対になる形でアトムが不要になった時点で呼び出します。

DeleteAtom() 関数

ATOM DeleteAtom(
  ATOM nAtom
);

パラメータ

nAtom アトムテーブルから削除する文字列を参照するアトム

戻り値

成功すると 0。失敗すると nAtom パラメータに指定した値

DeleteAtom() 関数は nAtom パラメータに指定したアトムに対応する文字列の参照カウントを 1 減らします。参照カウントが 0 になれば、アトムテーブルから対象の文字列が削除されます。通常、DeleteAtom() 関数は AddAtom() 関数に対応する形で呼び出されます。同じ文字列が異なる場所から複数追加された場合でも、適切に DeleteAtom() 関数が呼び出されていれば、参照カウントによって適切に管理できます。

サンプル01

#include <Windows.h>
#define ATOM_TEXT TEXT("Kitty on your lap")

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    ATOM atom;
    TCHAR str[1024];

    atom = AddAtom(ATOM_TEXT);

    wsprintf(str, TEXT("登録した文字列=\"%s\"\nアトム=%X\n"), ATOM_TEXT, atom);
    MessageBox(NULL, str, TEXT("アトムの登録"), MB_OK);

    wsprintf(str, TEXT("アトム=%X\nFindAtom(\"%s\")=%X"), atom, ATOM_TEXT, FindAtom(ATOM_TEXT));
    MessageBox(NULL, str, TEXT("アトムの検索"), MB_OK);

    GetAtomName(atom, str, 1024);
    MessageBox(NULL, str, TEXT("アトムから文字列を取得"), MB_OK);

    DeleteAtom(atom);

    return 0;
}

実行結果

サンプル01の実行結果.1

サンプル01の実行結果.2

上のサンプルは、文字列をアトムテーブルに登録し、結果として得られたアトムをメッセージボックスに表示します。AddAtom() 関数や FindAtom() 関数から得られたアトムを使って、文字列を識別できます。また、GetAtomName() 関数から登録されている文字列を取得することもできます。

整数アトム

文字列を登録して得られたアトムは、常に 0xC000 以上の値となります。これ以下の値は、文字列ではなく整数に対応するアトムに予約されます。前述した文字列を参照するアトムは文字列アトムと呼び、整数を参照するアトムは整数アトムと呼ばれます。整数アトムは 1 ~ 0xBFFF (49151) の範囲の整数に対応するアトムで、アトムテーブル上では # から始まる文字列で登録されます。

例えば、"#1024" という文字列を登録すれば、整数 1024 が整数アトムとして返されます。アプリケーションが管理するデータを識別する手段に整数が用いられている場合、文字列アトムと同様に整数アトムを使って識別できます。整数から整数アトムを表す文字列を作成するには MAKEINTATOM() マクロを使うと便利です。

MAKEINTATOM() マクロ

LPTSTR MAKEINTATOM(
    WORD wInteger
);

パラメータ

wInteger 整数アトムに変換する値

戻り値

整数アトムに変換された文字列型

このマクロが返した結果を AddAtom() 関数に渡すことで、整数アトムを得られます。指定した静数値が定められている範囲を超えている場合は失敗します。

整数アトムの最大値は MAXINTATOM 定数で提供されています。整数をアトムテーブルに登録できるかどうかを調べるには、この定数を利用してください。

サンプル02

#include <Windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    ATOM atom;
    LPTSTR str[1024];

    atom = AddAtom(MAKEINTATOM(1234));

    wsprintf(str, TEXT("登録した整数アトム=%d"), atom);
    MessageBox(NULL, str, TEXT("アトムの登録"), MB_OK);

    GetAtomName(atom, str, 1024);
    MessageBox(NULL, str, TEXT("整数アトムの文字列"), MB_OK);

    DeleteAtom(atom);

    return 0;
}

実行結果

サンプル02の実行結果.1

サンプル02の実行結果.2

このように、整数アトムの場合でも文字列アトムと同じように ATOM 型に変換して管理できます。アプリケーションがデータの識別に文字列と整数の両方を利用している場合でも、同じアトム関数で処理できるためプログラムを分ける必要はありません。

今回紹介したアトム関連関数は、ローカルアトムテーブルと呼ばれるアプリケーション単体で利用するアトムテーブルですが、複数のプロセスで共有できるグローバルアトムテーブルも用意されています。グローバルアトムテーブルを用いることで、アプリケーション間で文字列データを交換できます。アトムを利用することで効率的にデータを受け渡しできるので、文字列を複数の場所に分散されたプログラム間で共有するようなアプリケーションで有効です。