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

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

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

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

Windows API 文字列操作とUnicodeの概要

世界中に広がるMicrosoft(R) Windows(TM) 用アプリケーションのマーケットは複雑であり、 大規模なプログラミング プロジェクトの管理は困難であるため、 文字列をより洗練された方法で扱うことが必要になってきています。ここでは、 Windowsでの文字列操作について説明します。はじめに、 Windowsプログラミングでの国別言語サポートの実現について詳しく説明し、 その次に新しいグローバル文字エンコード システムであるUnicodeについて説明します。

次に示すトピックでは、 Windows NTの文字列操作とUnicodeについて説明しています。

文字セット

Windowsでの文字列操作

WindowsUnicode

文字列とUnicodeの使用

ウィンドウ クラス

サブクラス化と自動メッセージ変換

特殊文字

Unicodeを利用できない関数

文字列操作関数

文字セット

文字セットとは、 文字とその文字を識別する数値のマッピングです。コンピュータで一般に使われている文字セットのほとんどはシングル バイト文字セットで、 各文字は1バイトの値で識別されます。しかし、 アジア系言語には文字が大量にあるため、 マルチバイト文字セット、 特に2バイト文字セット (DBCS) が開発されました。Microsoft(R) Windows NT(TM) では、 文字エンコードの新しいグローバルな標準として、 Unicodeを導入しています。ここでは、 各文字セットについて説明します。

シングル バイト文字セット

シングル バイト文字セットは、 256個の文字と各文字を識別する数値のマッピングです。文字コードが0x20から0x7Eまでの文字は標準化された表示可能文字を表しますが、 そのほかのコードが表す文字は文字セットによって異なります。ASCII文字セットでは、 0x00から0x7Fまでを定義しています。

Windowsでは、 ウィンドウ マネージャやグラフィック デバイス インターフェイス (GDI) ANSI文字セットを使いますが、 MS-DOSファイル アロケーション テーブル (FAT) ファイル システムは、 相手先ブランド供給 (OEM) 文字セットと呼ばれる文字セットを使います。「コード ページ」と呼ばれる文字セットのバリエーションには、 さまざまな特殊文字 (通常は、 特定の言語用にカスタマイズされた文字) が含まれています。通常、 アメリカ合衆国で使われるOEMコード ページは437です。

Win32アプリケーション プログラミング インターフェイス (API) を使うアプリケーションは、 Unicodeを使うことによって、 一貫していないさまざまなコード ページを使わずに、 ローカライズしたアプリケーションを簡単に開発できます。

システムのANSIコード ページ識別子を取得するには、 GetACP関数を使います。また、 OEMコード ページ識別子を取得するには、 GetOEMCP関数を使います。

OEMコード ページの文字や文字列をANSIコード ページやUnicodeに変換するには、 OemToChar関数やOemToCharBuff関数を使います。逆方向に変換するには、 CharToOem関数やCharToOemBuff関数を使います。さらに、 アプリケーションは、 シングル バイト文字セット (SBCS) 文字列をUnicodeにマップしたり、 Unicode文字列をSBCSにマップするには、 MultiByteToWideChar関数やWideCharToMultiByte関数を使います。

GetCPInfo関数は、 コード ページの最大文字のバイト数や、 コード ページに対応するエントリがない文字コードが入力されたときに使われるデフォルト文字などの情報をCPINFO構造体に設定します。

2バイト文字セット

2バイト文字セット (DBCS; double-byte character set) は、 1バイトを最小単位とするため、 拡張8ビット文字セットとも呼ばれます。この文字セットは、 一部のアジア バージョンWindows (特に日本語版Windows) 用のANSI文字セットともいえます。日本語版WindowsWin32関数は、 ANSIバージョンの関数でもDBCS文字列を受け付けます。しかし、 Unicodeとは異なり、 DBCS文字を処理するには、 アプリケーションのソース コードの文字処理アルゴリズムをすべて変更しなければなりません。

特定の文字が2バイト文字の第1バイトかどうか調べるには、 IsDBCSLeadByte関数を使います。これによって、 2バイト文字セットかどうかを判断できます。また、 DBCS文字列をUnicodeにマップしたり、 Unicode文字列をDBCSにマップするには、 MultiByteToWideChar関数やWideCharToMultiByte関数を使います。

Unicode

Unicodeは、 文字エンコードの世界的な標準です。Windowsは、 システム レベルの文字や文字列の操作ではUnicodeだけを使います。Unicodeによって、 ソフトウェアのローカライズが簡単になり、 多言語テキストの処理が改善されます。アプリケーションでUnicodeを利用すれば、 すべての文字コードに対して1つのバイナリ ファイルを用意するだけで、 世界的なマーケットに対応した普遍的なデータ交換機能を実現できます。

Unicodeでは、 各文字の意味、 標準のスクリプト動作、 両方向テキスト用標準アルゴリズム、 ほかの文字コード規格へのクロス マッピングを定義しています。Unicodeがサポートしているスクリプトには、 Latin Greek Han Hiragana Katakanaなどがあります。また、 サポートされている言語には、 German French English Greek Chinese Japaneseなどがあります。

Unicodeは、 科学技術用記号や出版用特殊文字など、 世界の文字をすべて表現できます。Unicodeの各文字は16ビットであるため、 65,536個までの個別の値を表現できます。Unicode対応関数は、 「ワイド文字」関数とも呼ばれます。

Win32関数は、 Unicodeを使うアプリケーションとANSI文字セットを使うアプリケーションをサポートしています。また、 1つのアプリケーションでこの2つを混在して使うこともできます。Unicodeサポートの追加は簡単で、 1つのソース セットからUnicodeをサポートするアプリケーションやWindows ANSI文字セットをサポートするアプリケーションを生成できます。

Win32関数は、 Unicode文字列用のデータ型を割り当てて、 その新しいデータ型をサポートするエントリ ポイントやメッセージを別に用意することによって、 Unicodeをサポートしています。マクロや命名規約のセットによって、 Unicodeへの移植は意識せずに済みます。また、 再コンパイルが必要でも、 1つのソース セットからそのまま非UnicodeバージョンとUnicodeバージョンの両方を生成できます。

また、 Unicodeは新しいデータ型として実現されているため、 コンパイラの型チェックによって、 Unicode文字列を仮定している関数ではUnicodeパラメータだけが使われるようにできます。

Windowsでの文字列操作

Windows関数には、 標準C言語ライブラリの文字列操作関数を真似たものや、 拡張したものが多くあります。Windowsの関数はさまざまに改良されているため、 Unicodeや拡張文字セットでも使えます。たとえば、 次の表のWindows関数は標準C言語関数と同等ですが、 Windows関数は、 Unicode用に拡張されています。

Windows関数 標準C言語関数

lstrcatstrcat

lstrcmpstrcmp

lstrcmpistrcmpi

lstrcpystrcpy

lstrlenstrlen

たとえば、 標準C言語関数のstrlenは常に文字列のバイト数を返しますが、 lstrlen関数は、 文字が1バイトか2バイトかに関わらず、 文字列の文字数を返します。

次に示すWindows関数は、 tolowertoupperなどの標準C言語関数とは異なり、 文字セット中のすべての文字を扱うことができます。たとえば、 Windows用アプリケーションは、 CharLower関数を使って、 ウムラウト付きの大文字のUを小文字に変換できます。文字セットについて詳しくは、 シングル バイト文字セットを参照してください。

関数 説明

CharLower 文字や文字列を小文字に変換します。

CharLowerBuff 文字列を小文字に変換します。

CharNext 文字列の次の文字に移動します。

CharPrev 文字列の前の文字に移動します。

CharUpper 文字や文字列を大文字に変換します。

CharUpperBuff 文字列を大文字に変換します。

次に示すWindows関数は、 ユーザーが選択している言語の定義に基づいて、 文字に関する情報を調べます。これらの関数ではUnicodeが使えます。

関数 説明

IsCharAlpha 文字がアルファベットかどうか調べます。

IsCharAlphaNumeric 文字がアルファベットまたは数字かどうか調べます。

IsCharLower 文字が小文字かどうか調べます。

IsCharUpper 文字が大文字かどうか調べます。

wsprintf関数とwvsprintf関数は、 標準C言語関数のsprintfvsprintfの拡張版です。Windowsバージョンは、 Unicode用の書式指定をサポートしています。

文字列リソース

文字列をリソースで管理しているアプリケーションは、 簡単に新しい言語に変換できます。ソース モジュールの文字列を検索しなくても、 リソース ファイルの文字列を変換してアプリケーションを再リンクするだけです。さらに、 文字列リソースを使うことによって、 1つのソース ファイルからUnicodeバージョンと非Unicodeバージョンの両方を簡単に生成できるようになります。

LoadString関数は、 アプリケーションの実行可能ファイルから文字列リソースをロードします。FormatMessage関数は、 文字列リソースをロードして、 文字列に埋め込まれている書式オプションを解釈します。

Windowsのバイナリ形式32ビット リソースは、 Unicode形式で格納されます。リソースをロードするときは、 Unicodeバージョンのリソース関数 (LoadStringWなど) を使えば、 リソースをUnicodeデータとして取得できます。

ロケールと言語識別子

アプリケーションをより洗練されたものにするには、 アプリケーションがインストールされているシステムの言語とロケールに関する情報を取得してください。特に、 言語固有の文字列やロケール固有の文字列を処理する場合はこの情報が重要です。

ロケール識別子とは、 言語識別子と予約されているワードをダブルワードにまとめた値です。定義済みのロケール識別子は2つあります。LOCALE_SYSTEM_DEFAULTはシステムのデフォルト ロケールを識別し、 LOCALE_USER_DEFAULTは現在のユーザーのロケールを識別します。現在のロケール識別子を取得するには、 GetSystemDefaultLCID関数やGetUserDefaultLangID関数を呼び出します。また、 現在の言語識別子を取得するには、 GetSystemDefaultLangID関数やGetUserDefaultLCID関数をLAST_VOLUMEで呼び出します。

CompareStringW関数は、 指定されたロケール識別子に基づいて、 2つのUnicode文字列を比較します。

GetLocaleInfoW関数は、 ロケールに関するさまざまな情報を取得します。

GetStringTypeW関数は、 Unicode文字列に関する情報を文字単位で取得します。

WindowsUnicode

Win32 APIの文字を使う部分には、 一般に次の3つの形式があります。

ANSIでもUnicodeでもコンパイル可能な汎用バージョン

ANSIバージョン

Unicodeバージョン

ここでは、 Unicodeのデータ型と、 関数やメッセージ、 リソース、 ファイル名、 コマンド ライン引数でUnicodeを使う方法について説明します。また、 さまざまな種類の文字列を変換する方法も説明します。

データ型

Unicodeの文字列操作のほとんどはWindows ANSI文字セットと同じ方法で実現できますが、 操作の基本単位は8ビットではなく16ビットになります。ヘッダー ファイルにはさまざまな型が定義されており、 UnicodeANSI文字セットの両方でコンパイル可能なソースを簡単に作成できるようになっています。

次の例は、 Win32のヘッダー ファイルでデータ型を3セット定義するのに使われている方法を示しています。1つはANSIUnicodeの両方でコンパイル可能な汎用型定義で、 あとの2つはそれぞれの型定義です。1つ目の型定義は既存のWindows (ANSI) 文字セット用で、 もう1つはUnicode文字用です。

/* Generic types */

#ifdef UNICODE

typedef wchar_t TCHAR;

#else

typedef unsigned char TCHAR;

#endif

typedef TCHAR * LPTSTR, *LPTCH;

/* 8-bit character specific */

typedef unsigned char CHAR;

typedef CHAR *LPSTR, *LPCH;

/* Unicode specific (wide characters) */

typedef unsigned wchar_t WCHAR;

typedef WCHAR *LPWSTR, *LPWCH;

型名の先頭のTは、 ANSIUnicodeの両方でコンパイル可能な汎用型を示しています。型名の先頭のWは、 ワイド文字 (Unicode) 型を示しています。この型定義の実際の実現方法については、 WINNT.Hヘッダー ファイルを参照してください。

汎用データ型を利用するアプリケーションは、 ヘッダー ファイルの#include文の前にUNICODEを定義するだけで、 Unicodeでコンパイルできるようになります。ANSI用にコンパイルするには、 UNICODEを定義しないでください。

通常は汎用データ型を使うべきですが、 型を混合して使わなければならないアプリケーションのため、 個別のデータ型も用意されています。

関数プロトタイプ

関数プロトタイプも、 次に示すように、 汎用、 ANSI Unicode3種類が用意されています。汎用関数プロトタイプは、 マクロとして実現されている標準関数名で構成されます。プリプロセッサは、 UNICODEが指定されているかどうかに応じて、 マクロをどちらかの関数プロトタイプに展開します。各関数プロトタイプの関数名の末尾には、 A(ANSI) W(wide) が付加されます。この例では、 汎用プロトタイプではテキスト パラメータに汎用型のLPTSTRを使っていますが、 AプロトタイプとWプロトタイプでは、 それぞれ、 LPCSTR型とLPCWSTR型を使っています。

SetWindowText(HWND hwnd, LPTSTR lpText);

SetWindowTextA(HWND hwnd, LPCSTR lpText);

SetWindowTextW(HWND hwnd, LPCWSTR lpText);

アプリケーションは、 汎用関数を使って、 コードをコンパイルするときにUNICODEオプションを指定するかどうかによってUnicode仕様にするかどうかを決定するか、 AWで終わる関数名を使って2つの型を混在させることができます。

この3つのプロトタイプによる方法は、 テキスト引数を持つ関数すべてに適用されます。汎用関数プロトタイプは、 必ず汎用文字列型や汎用文字型で使ってください。大文字のWで終わる関数は、 ワイド文字引数を取ります。

一部の関数はワイド文字バージョンしかないため、 適切なデータ型で使ってください。

Unicode対応関数のヘルプには、 地球のビットマップが付いています。関数がマクロとして実現されおり、 プリプロセッサがANSI形式かUnicode形式に展開する場合は、 マクロ形式だけが示されています。

:文字列の長さを示すパラメータが関数にある場合、 長さは文字数 (TCHAR単位) で指定してください。しかし、 GlobalAlloc関数など、 型なしメモリを指すポインタをパラメータや戻り値とする関数は除きます。

メッセージ

通常、 ウィンドウ クラスは複数のアプリケーションで共用されますが、 さまざまなクラスのウィンドウ間のメッセージは、 システムによって自動的に変換されます。

ウィンドウ関数はUnicode形式やANSI形式のメッセージを受け取るようになっていますが、 ウィンドウ プロシージャではどちらか一方の種類のメッセージを送ったり関数を呼び出すことができます。

次に示すメッセージにはテキスト引数があり、 自動テキスト変換の対象になります (自動変換について詳しくは、 サブクラス化と自動メッセージ変換を参照してください)

CB_ADDSTRINGWM_ASKCBFORMATNAME

CB_DIRWM_CHAR

CB_FINDSTRINGWM_CHARTOITEM

CB_GETLBTEXTWM_CREATE

CB_INSERTSTRINGWM_DEADCHAR

CB_SELECTSTRINGWM_DEVMODECHANGE

WM_GETTEXT

EM_GETLINE WM_MDICREATE

EM_REPLACESELWM_MENUCHAR

EM_SETPASSWORDCHARWM_NCCREATE

WM_SETTEXT

LB_ADDFILE WM_SYSCHAR

LB_ADDSTRINGWM_SYSDEADCHAR

LB_DIR WM_WININICHANGE

LB_FINDSTRING

LB_GETTEXT

LB_INSERTSTRING

LB_SELECTSTRING

文字列関数

このトピックで示す文字列関数にはすべてANSIバージョンとUnicodeバージョンがあり、 それぞれANSIの引数とUnicodeの引数をサポートしています。しかし、 微妙な違いもあります。

次に示す文字列関数にはそのような違いはなく、 ANSIバージョンとUnicodeバージョンは同様に動作します。

CharNext

CharPrev

lstrcat

lstrcpy

lstrlen

ANSI形式かUnicode形式かに関わらず、 lstrlen関数が返す値は常に文字数です。

次に示す文字列関数は、 現在のスレッドの言語 (ユーザーがコントロール パネルで選択した言語) によって動作が異なります。lstrcmp関数とlstrcmpi関数は、 ANSI C言語の関数と同様、 バイト単位の比較は行いません。これらの関数は、 選択されている言語の規則に従って文字列を比較します。

CharLower

CharLowerBuff

CharUpper

CharUpperBuff

lstrcmp

lstrcmpi

次に示す関数は、 OEM文字セットとANSIまたはUnicodeとの間の変換を行います。

CharToOem

CharToOemBuff

OemToChar

OemToCharBuff

wsprintf関数はUnicodeをサポートしており、 書式指定に次に示す新しいデータ型と変更されたデータ型を用意しています。

書式指定ANSIデータ型Unicodeデータ型

cCHARWCHAR

CWCHARCHAR

hchCCHARCHAR

hshSLPSTRLPSTR

lclCWCHARWCHAR

lslSLPWSTRLPWSTR

sLPSTRLPWSTR

SLPWSTRLPSTR

出力テキストのデータ型は関数のバージョンによります。入力されたパラメータの型と出力の型が一致しない場合は、 wsprintfは必要に応じてUnicodeからANSIへ変換します。また、 その逆の場合もあります。

wsprintfUnicodeバージョンでは、 出力テキスト同様、 書式文字列もUnicodeです。

標準C言語関数

標準C言語ライブラリには、 strで始まるANSI文字列関数のワイド文字バージョンがあります。ワイド文字バージョンの関数の名前は、 wcs (一部は_wcs) で始まります。Unicodeデータ型は、 ANSI C言語のワイド文字データ型であるwchar_tと互換性があります。これによって、 ワイド文字の文字列関数にアクセスできます。

すべての標準C言語文字列関数について、 対応する汎用関数があります。これらは_tcsで始まり、 TCHAR.Hヘッダー ファイルにリストされています。これらの関数は、 汎用データ形TCHARおよびTCHAR *を使用しています。

アプリケーションで汎用関数を使うには、 次の行をプログラムに追加し、 Unicodeとしてコンパイルしてください。

#difine _UNICODE

#include <tchar.h>
#include <wchar.h>

TCHAR.HおよびWCHAR.Hの両方が必要なこと、 _UNICODE変数の先頭にアンダスコアが必要なことに注意してください。

wcstombs関数とmbstowcs関数は、 標準C言語ライブラリがサポートしている文字セットとUnicodeを制限付きで相互変換できます。Unicodeとの間の文字列の変換について詳しくは、 各種類間での文字列の変換を参照してください。

TCHAR.Hで定義されているprintf関数は、 wsprintfと同じ書式指定をサポートしています。詳しくは、 文字列操作関数を参照してください。同様に、 TCHAR.Hwprintf関数は、 書式文字列自体もUnicode文字列です。

ファイル名

Windowsでは、 ウィンドウ マネージャとGDIANSI文字セットを使用し、 MS-DOS FATファイル システムはOEM文字セットを使用します。MS-DOSファイルを作成するWindowsアプリケーションは、 CharToOem関数やOemToChar関数を使って文字セット間で変換しなければなりませんでした。しかし、 NTFSファイル システムには、 ファイル名をUnicodeにする機能があるため、 変換は不要です。

ファイル システム関数がUnicodeをサポートしているため、 ANSI文字セットやOEM文字セットとの間で変換を行う必要はありません。Unicode用にコンパイルするときはCharToOemOemToCharなどの関数を呼び出さないようにするマクロを用意することによって、 1つのソース ファイルから非UnicodeバージョンとUnicodeバージョンを生成できます。

次に示すMS-DOSの特殊なファイル名文字は、 Unicodeファイル名でもそのまま使えます。

\ / . ? *

上記の特殊文字はASCIIの範囲 ((0x00から0x7F) にあり、 Unicodeの対応するコードも、 2バイトで同じ値 (0x0000から0x007F) になっています。

各種類間での文字列の変換

次に示すWin32関数は、 ある種類の文字列を別の種類に変換します。

関数 説明

FoldStringW ワイド文字文字列を別の文字列に変換します。

LCMapStringW ロケールによって文字列をマップします。

ToUnicode 仮想キー コードをUnicode文字に変換します。

MultiByteToWideChar マルチバイト文字列をワイド文字文字列にマップします。

WideCharToMultiByte ワイド文字文字列をマルチバイト文字列にマップします。

複数の種類の文字列をサポートする場合、 WideCharToMultiByte関数とMultiByteToWideChar関数が特に役立ちます。また、 ANSI Cでは変換関数のwcstombsmbstowcsを定義していますが、 これらの関数は、 標準C言語ライブラリがサポートしている文字セット間でしか変換できません。

コマンド ライン引数

Unicodeのコマンド ライン引数を取得するには、 GetCommandLine関数をUnicode関数として呼び出します。

文字列とUnicodeの使用

ここでは、 次に示す作業について説明します。

汎用データ型によるコードの変換。これによって、 1つのソース ファイルでANSI形式とUnicode形式の両方にコンパイルできます。

ANSIアプリケーションとUnicodeアプリケーションのウィンドウ クラスの登録

サブクラス化

自動メッセージ変換の使用

Unicodeで特別な意味を持つ文字の使用

対応するUnicodeバージョンがない関数の使用

汎用データ型の使用

ソース コードで汎用データ型を使うことによって、 ヘッダー ファイルのインクルード文の前にUNICODEを定義するだけでUnicode用にコンパイルできます。ANSI用にコンパイルするには、 UNICODEを定義しないでください。

文字列処理コードをANSI用とUnicode用の両方にコンパイルできるようにするには、 次に示す手順に従ってください。

1. テキストに使われている文字型や文字列型をすべてTCHARLPTSTRLPTCHに変更します。

2. テキスト以外のデータ バッファやバイナリのバイト配列を指すポインタは、 LPTSTR型やLPTCH型でなく、 必ずLPBYTE型にしてください。型を決定できないポインタは、 LPVOIDを使ってvoidポインタとして宣言してください。

3. ポインタ演算が型に依存しないようにします。TCHARサイズを単位にすれば、 UNICODEオプションが定義されていなければバイト単位になり、 UNICODEが定義されていればワード単位になります。次に示す式は、 UNICODEの定義の有無に関係なく常に要素数を返します。

cCount = lpEnd - lpStart;

次の式により、 使われているバイト数が決まります。

cByteCount = (lpEnd - lpStart) * sizeof(TCHAR);

ポインタをインクリメントすると次の文字要素を指すようになるため、 次のような文を変更する必要はありません。

chNext = *++lpText;

4. リテラル文字列や明示的な文字定数はマクロで置き換えます。次に示すような式は変更してください。

while(*lpFileName++ != '\\') {

.

.

.

}

上記のような場合、 次のようにTEXTマクロを使ってください。

while(*lpFileName++ != TEXT('\\')) {

.

.

.

}

Unicode形式の関数のリテラル文字列の前にはLを付けてください。TEXTマクロは、 UNICODEが定義されているときは文字列がL"string"に、 UNICODEが定義されていないときは"string"に評価されるようにします。次に示すlstrlen関数のUnicodeバージョンの呼び出しは、 L識別子の使い方を示しています。

cch == lstrlenW(L"hello world");

管理を簡単にするため、 ASCII範囲 (0x00から0x7Fまで) 以外の文字を含む場合は特に、 リテラル文字列はリソースに置いてください。

5. 標準Cライブラリの文字列関数は、 標準C言語関数にリストされているUnicodeバージョンだけを呼び出してください。

6. 文字の最大値が255であると仮定しているコードを修正してください。

上記のとおりに変更したコードをコンパイルすると、 コンパイラは、 変更前と同じバイナリ ファイルを作成します。しかし、 UNICODEオプションを使ってコンパイルすると、 Unicodeアプリケーションとしてコンパイルされます。

ウィンドウ クラスの登録

ウィンドウ クラスは、 ウィンドウ プロシージャでサポートされます。ウィンドウ クラスを登録するには、 RegisterWindowClassA関数かRegisterWindowClassW関数を使います。RegisterWindowClassAを使ってウィンドウ クラスを登録すると、 登録 (作成) されたクラスのウィンドウは、 メッセージのテキスト パラメータをANSI文字セットで受け取ることになります。また、 RegisterWindowClassWを使ってウィンドウ クラスを登録すると、 メッセージのテキスト パラメータをUnicodeで受け取ることになります。IsWindowUnicode関数によって、 ウィンドウがUnicode用か調べることができます。

次の例は、 ANSIウィンドウ クラスとUnicodeウィンドウ クラスの登録方法とそのウィンドウ プロシージャを示しています。この例では、 説明のため、 関数や構造体はすべてデータ型のAWを明示しています。汎用データ型の使用で説明した方法を使ってこの例を汎用データ型で書き換えることによって、 UNICODEの定義に応じてANSIUnicodeのコードにコンパイルできます。

/* Register an ANSI window class. */

WNDCLASSA AnsiWndCls;

AnsiWndCls.style= CS_DBLCLKS | CS_PARENTDC;
AnsiWndCls.lpfnWndProc= (WNDPROC)AnsiWndProc;
AnsiWndCls.cbClsExtra= 0;
AnsiWndCls.cbWndExtra= 0;
AnsiWndCls.hInstance= hmodUser;
AnsiWndCls.hIcon= NULL;
AnsiWndCls.hCursor= LoadCursor(NULL, (LPTSTR)IDC_IBEAM);
AnsiWndCls.hbrBackground = NULL;
AnsiWndCls.lpszMenuName= NULL;
AnsiWndCls.lpszClassName = "TestAnsi";

RegisterClassA(&AnsiWndCls);

/* Register a Unicode window class. */

WNDCLASSW UnicodeWndCls;

UnicodeWndCls.style= CS_DBLCLKS | CS_PARENTDC;
UnicodeWndCls.lpfnWndProc= (WNDPROC)UniWndProc;
UnicodeWndCls.cbClsExtra= 0;
UnicodeWndCls.cbWndExtra= 0;
UnicodeWndCls.hInstance= hmodUser;
UnicodeWndCls.hIcon= NULL;
UnicodeWndCls.hCursor= LoadCursor(NULL,(LPTSTR)IDC_IBEAM);
UnicodeWndCls.hbrBackground = NULL;
UnicodeWndCls.lpszMenuName= NULL;
UnicodeWndCls.lpszClassName = L"TestUnicode";

RegisterClassW(&UnicodeWndCls);

次の例は、 ANSIウィンドウ プロシージャとUnicodeウィンドウ プロシージャでのWM_CHARメッセージの処理方法の違いを示しています。

/* ANSI Window Procedure */

LONG AnsiWndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{

/* Dispatch the messages that can be received. */

switch (message) {

case WM_CHAR:

/*
* wParam - the value of the key
* lParam - (not used in this example)
*/

if (lstrcmpA("Q", (LPCSTR) wParam)) {
.
.
.
}
else {
.
.
.
}
break;
.
. /* process other messages */
.
}
}

/* Unicode Window Procedure */

LONG UniWndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{

/* Dispatch the messages that can be received. */

switch (message) {

case WM_CHAR:

/*
* wParam - the value of the key
* lParam - (not used in this example)
*/

if (lstrcmpW(L"Q", (LPCWSTR) wParam)) {
.
.
.
}
else {
.
.
.
}
break;
.
. /* process other messages */
.
}
}

AnsiWndProcが受け取るメッセージのテキストはすべてANSI文字で構成され、 UniWndProcが受け取るメッセージのテキストはすべてUnicode文字で構成されています。

サブクラス化と自動メッセージ変換

サブクラス化とは、 特定のウィンドウに送られたりポストされたメッセージを、 そのウィンドウが処理する前にアプリケーションが解釈して処理する方法です。システムは、 ウィンドウのサブクラス化に使われた関数に応じて、 メッセージを自動的にANSI形式かUnicode形式に変換します。

次に示すSetWindowLongA関数は、 hwndが識別するウィンドウに現在関連付けられているウィンドウ プロシージャをサブクラス化します。新しいウィンドウ プロシージャのNewWndProcは、 テキストをANSI形式で受け取ります。

OldWndProc = (WNDPROC) SetWindowLongA(hwnd,
GWL_WNDPROC, (LONG)NewWndProc);

NewWndProcは、 メッセージを処理し終わったら、 次に示すように、 CallWindowProc関数を使ってメッセージをOldWndProcに渡します。

CallWindowProc(OldWndProc, hwnd, uMessage, wParam, lParam);

OldWndProcUNICODEクラス スタイルで作成されていれば、 NewWndProcANSI形式で受け取ったメッセージはUnicodeに変換されます。

同様に、 SetWindowLongW関数は、 現在のウィンドウ プロシージャを、 Unicodeテキストのメッセージを仮定するウィンドウ プロシージャでサブクラス化します。CallWindowProc関数は、 必要ならばメッセージを変換します。

サブクラス化について詳しくは、 ウィンドウ プロシージャの概要を参照してください。

Unicodeの特殊文字の使用

Unicodeには、 通常とは異なる意味を持つ文字や特殊文字があります。このような文字による問題が発生しないようにするため、 ここで示す規則に従ってください。

NULLで終わる文字列

NULLで終わる文字列を使うときは、 必ず0TCHARにキャストしてください。コード0x0000は、 NULLで終わる文字列のUnicode文字列終端記号です。Unicodeには上位バイトか下位バイトがNULLである文字列が多く含まれているため、 単一バイトのNULLでは終端記号には不十分です。たとえば、 Aの文字コードは0x0041です。

バイト順序マーク

Unicodeの純粋なテキスト ファイルの先頭には、 必ず「バイト順序マーク」を付けてください。Unicodeの純粋なテキストは16ビット コードの並びであるため、 テキストを作成したときのバイト順序は重要です。

バイト順序マークは、 テキストのバイト順序を選択する制御文字ではありません。このマークは、 ファイルがバイト順序規則に従っていることをアプリケーションに知らせるだけです。

理想的には、 Unicodeテキストはすべて1つのバイト順序規則に従っていなければなりません。しかし、 最下位バイトの位置がマイクロプロセッサによって異なるため、 それは不可能です。Intel(R) MIPS(R) のプロセッサでは最下位バイトが先頭にくるのに対し、 Motorola(R) のプロセッサ (および「バイト順序反転」Unicodeファイル) では、 最下位バイトは最後にきます。どちらか一方のバイト順序規則に固定してしまうと、 そうでない方のマイクロプロセッサのユーザーは、 別のマイクロプロセッサのシステムにファイルを転送することがなくても、 テキスト ファイルを読み書きするたびにバイト順序を反転しなければならなくなります。

バイト順序を指定するのはファイル ヘッダーが適切ですが、 テキスト ファイルにはヘッダーはありません。このため、 Unicodeでは、 バイト順序マークとして文字 (0xFEFF) と非文字 (0xFFFE) を定義しています。この2つの値は、 互いにバイト順序を反転した関係になっています。

通常の非Unicodeテキスト ファイルでは0xFEFFというコードはほとんどないため、 この値は、 ファイルをUnicodeファイルとして識別するための暗黙のマーカーとして使えます。Unicodeテキスト ファイルと非Unicodeテキスト ファイルの両方を読み取るアプリケーションは、 この値があれば、 ファイルがUnicodeファイルであると仮定してください (類似の方法に、 MS-DOSEOFマーカーによるテキスト ファイルの終端の識別があります)

通常、 アプリケーションは、 テキスト ファイルの先頭で0xFEFFを見つけたらそのファイルをUnicodeファイルとして処理しますが、 本当にUnicodeかどうかさらにチェックすることもあります。たとえば、 下位バイトの値の種類が上位バイトの値の種類よりも多いかどうかを調べるだけでもチェックになります。たとえば、 ASCIIテキストをUnicodeテキストに変換すると、 各文字の2バイト目は0になります。また、 改行文字と復帰文字 (0x000A0x000D) のチェックや、 ファイル サイズが偶数か奇数かのチェックでも、 ファイルの特性を知ることができます。

テキスト ファイルの先頭に0xFFFEがあると、 アプリケーションは、 ファイルをバイト順序反転Unicodeファイルとして解釈します。バイト順序を反転して処理を続けるか、 エラーが発生したことをユーザーに警告してください。

Unicodeのバイト順序マーク文字はどのコード ページにもないため、 データをANSIに変換するとバイト順序マークはなくなります。バイト順序マークは、 ほかのUnicode文字と異なり、 変換時にデフォルト文字で置き換えられません。バイト順序マークがファイル先頭以外にあれば、 Unicode文字としては解釈されず、 テキスト出力に影響はありません。

Unicode0xFFFFは、 純粋なテキスト ファイルでは無効であり、 Windows関数で扱うことはできません。0xFFFFは、 アプリケーションの内部使用のために予約されています。

エスケープ シーケンスと制御文字

エスケープ シーケンスは、 文字単位でUnicodeに変換してください。ASCIIの純粋なテキスト ファイルをUnicodeに変換する場合、 後でASCIIにもう一度変換し直す可能性があります。エスケープ シーケンスを2バイト文字としてではなく文字単位でUnicodeに変換することによって、 エスケープ シーケンスを特別に認識して解析しなくても逆変換を実行できます。たとえば、 ESC+Aは、 0x411Bではなく、 0x001B (ESC) 0x0041 (A) にしてください。

Unicodeの最初の32個の16ビット文字は、 32個の制御文字に使われます。これによって、 既存の書式用制御文字もサポートされています。Unicodeアプリケーションは、 このような制御文字をASCIIと同様に扱うことができます。

行区切り記号と段落区切り記号

純粋なテキストを分割するには、 行区切り記号文字 (0x2028) や段落区切り記号文字 (0x2029) を使ってください。行区切り記号の次に新しい行が始まります。また、 段落区切り記号の後に新しい段落が始まります。

これらの文字は区切り記号コードであるため、 ファイルの最初の行や段落をこれらのコードで開始したり、 最後の行や段落をこれらのコードで終える必要はありません (区切り記号だけを指定すれば、 そこに空行や空段落があることを示すことができます)

行区切り記号は、 行の終端記号として使用できます。しかし、 行区切り記号は、 復帰文字や改行文字およびその組み合わせには対応していません。行区切り記号は、 復帰文字や改行文字とは別に処理しなければなりません。

テキストの段落の間には、 段落区切り記号を挿入してください。これによって、 行の幅が異なるさまざまなシステムで書式化できるテキスト ファイルを作成できます。受け取り側のシステムは、 復帰を無視して、 段落区切り記号のところでだけ段落を分割してください。

ASCII制御コードの0x000D0x000A

Unicode規格では、 0x000D (復帰) 0x000A (改行) には特別な意味を規定していません。これらのコードをペアで使う必要はなく、 1つずつ独立して使った場合、 1つのコードでそれ自身か両方のコードを合わせたものを表す場合があります。これらのコードが表すものは、 アプリケーションによって異なります (アプリケーションは、 ASCIIのこれらの制御コードを必ず解釈しなければなりません。Unicodeでは、 これらのコードの使い方を変更していません)

送りのない文字と発音記号

行を分割したりテキストを分離するときは、 送りのない文字と基本文字とが分かれないようにしてください。多くのスクリプトには、 出力時にほかの文字と組み合わせて表示される文字 (発音記号など) があります。Unicodeでは、 送りのない文字は基本文字の後にきます。

Unicodeを利用できない関数

通常、 Unicodeバージョンが用意されていないWindows関数は、 Unicodeをサポートするより強力な関数や拡張された関数で置き換えられています。たとえば、 OpenFile関数を呼び出しているコードを移植するときは、 代わりにCreateFile関数かCreateFileEx関数を使うことによってUnicodeをサポートできます。

関数にUnicodeバージョンがないときは、 関数の呼び出しの前後で、 文字を8ビット文字セットとの間でマップしてください。たとえば、 数値書式化関数のatoiitoaは、 0から9までの数字しか使いません。通常、 Unicode文字を8ビット文字にマップするとデータが失われますが、 コードが型に依存しないようにして、 式が条件に応じて評価されるようにすることによって、 データの損失を避けることができます。たとえば、 次に示す文は型に依存しているため、 Unicodeをサポートするには変更しなければなりません。

char str[4];
num = atoi(str);

上記の文は、 次のように書き直すことによって、 型に依存しないようにできます。

TCHAR tstr[4];
CHAR strTmp[SIZE];

#ifdef UNICODE

wcstombs(strTmp, (const wchar_t *) tstr, sizeof(strTmp));
num = atoi(strTmp);

#else

num = atoi(tstr);

#endif

上記の例のwcstombs関数は、 UnicodeASCIIに変換する標準C言語関数です。ここでは、 数字以外のテキストを変換できなくても、 0から9までの数字は必ずUnicodeからASCIIに変換できると仮定しています。atoi関数は、 数字以外の文字があると処理を中止します。Unicodeのスクリプトのいずれかに固有な数字を含むテキストを処理するには、 LCMapStringW関数を使ってください。

文字列操作関数

文字列操作に関する関数を次に示します。

CharLower

CharLowerBuff

CharNext

CharPrev

CharToOem

CharToOemBuff

CharUpper

CharUpperBuff

CompareStringW

ConvertDefaultLocale

EnumCalendarInfo

EnumCalendarInfoProc

EnumCodePagesProc

EnumDateFormats

EnumDateFormatsProc

EnumLocalesProc

EnumSystemCodePages

EnumSystemLocales

EnumTimeFormats

EnumTimeFormatsProc

FoldStringW

FormatMessage

GetACP

GetCPInfo

GetCurrencyFormat

GetDateFormat

GetNumberFormat

GetLocaleInfoW

GetOEMCP

GetStringTypeA

GetStringTypeEx

GetStringTypeW

GetSystemDefaultLangID

GetSystemDefaultLCID

GetThreadLocale

GetTimeFormat

GetUserDefaultLangID

GetUserDefaultLCID

IsCharAlpha

IsCharAlphaNumeric

IsCharLower

IsCharUpper

IsDBCSLeadByte

IsTextUnicode

IsValidCodePage

IsValidLocale

LCMapStringW

LoadString

lstrcat

lstrcmp

lstrcmpi

lstrcpy

lstrcpyn

lstrlen

MultiByteToWideChar

OemToChar

OemToCharBuff

SetLocaleInfo

SetThreadLocale

WideCharToMultiByte

wsprintf

wvsprintf

AnsiLower (使われなくなりました)

AnsiLowerBuff (使われなくなりました)

AnsiNext (使われなくなりました)

AnsiPrev (使われなくなりました)

AnsiToOem (使われなくなりました)

AnsiToOemBuff (使われなくなりました)

AnsiUpper (使われなくなりました)

AnsiUpperBuff (使われなくなりました)

OemToAnsi (使われなくなりました)

OemToAnsiBuff (使われなくなりました)

▲ページトップに戻る

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