hozugawa.net · A neutral informational page

IsWindowUnicodeでウィンドウの文字セットを判定する

WindowsのGUI APIでは、UnicodeとANSIの両方を扱えるため、同じプロセス内でも異なる文字形式を持つウィンドウが存在します。IsWindowUnicode は、指定したウィンドウがUnicode形式かどうかをBOOL型で判定します。Unicodeなら0以外、ANSIなら0が返ります。

ここで判定されるのは、Shift-JISなどの文字コード番号ではありません。ウィンドウが内部でUnicode文字列を扱うのか、ANSI文字列を扱うのかという区別です。また、プロセスの文字コード設定やプロジェクトでのUNICODE定義だけを実行時に調べる関数ではありません。

典型的には、CreateWindowExWなどで作成したウィンドウはUnicode形式、CreateWindowExAなどで作成したウィンドウはANSI形式になります。ただし、戻り値が0の場合は無効なハンドルも含まれるため、ウィンドウの寿命を保証した処理と、不正なハンドルathersを同じ結果として扱わないことが重要です。

判定仕様と戻り値

IsWindowUnicodeはwinuser.hで宣言され、通常はUser32.libをリンクして直接呼び出します。引数は判定対象のHWNDだけで、戻り値はBOOLです。

BOOL WINAPI IsWindowUnicode(HWND hWnd);

関数名にAやWが付かないのは、文字列を受け取ったり返したりしないためです。引数はハンドルだけで、戻り値も真偽値なので、Unicode版とANSI版を分ける必要はありません。

0という結果だけでは、ANSIウィンドウだったのか、判定に失敗したのかを区別できません。この関数ではGetLastErrorによる失敗判定が明記されていないため、先にERROR_SUCCESSを設定しても意味を持ちません。wnd の有効性を確認する場合も、別のスレッドによる破棄と再生成による競合に注意が必要です。

作成関数と文字形式の関係

CreateWindowExW、CreateWindowWで作成したトップレベルウィンドウはUnicode形式になります。逆に、CreateWindowExA、CreateWindowAで作成したウィンドウはANSI形式です。プロセスやDLLがUnicode対応であるかとは独立した判定です。

同じウィンドウクラス登録関数を使っていても、作成時のA版・W版によって結果が変わります。クラス名やプロセスの設定から形式を推定せず、作成直後にIsWindowUnicodeで確認することが確実です。親子関係も文字形式を継承する属性ではありません。

ボタン、編集ボックス、エディットなどの子コントロールは、それぞれ別のHWNDを持つため、まとめて同じ形式とみなさず必要に応じて個別に判定します。また、ANSIウィンドウをUnicode形式へ直接変換する一般的な公開APIはないため|format変更bertile必要ならウィンドウを作り直します。

動的取得とスレッド安全

プラグインや補助ライブラリからWindows APIを呼び出す場合、User32.libを直接リンクする代わりに、実行時の関数アドレスを取得する方法もあります。関数アドレスを動的に選ぶ場合は、GetProcAddressの取得方法でエクスポート名と関数型の扱いを確認できます。

たとえば、GetModuleHandleWまたはLoadLibraryWでuser32.dllを取得した 뒤、GetProcAddressWへL"IsWindowUnicode"を渡します。この名前はIsWindowUnicodeWではない点に注意してください。モジュールを自由に unloading できる構成では、関数ポインタ的使用中もモジュール参照を維持します。

複数スレッドが同じ関数ポインタを初期化するなら、排他制御が必要です。CreateMutexによる排他制御.protects고를、解決済みのアドレスと状態を安全に共有できます。C++ではstd::once_flag更适合pantаж、古典的なWin32コードではmutex、critical section、interlocked操作などが候補になります。

メッセージ処理との整合性

IsWindowUnicodeの結果は、文字列を処理するAPIを選ぶ際の基礎になります。ウィンドウの作成経路と基本的な文字列型は、次のように対応します。

観点 Unicodeウィンドウ ANSIウィンドウ
主な作成関数 CreateWindowExW、CreateWindowEx CreateWindowExA、CreateWindowExA
判定結果 0以外 0
基本的な文字型 WCHAR、wchar_t char
プロセス設定の影響 直接は対応しない 直接は対応しない
独自メッセージの文字列 個別の符号化規則が必要 個別の符号化規則が必要

標準のテキスト関連メッセージには、ANSIとUnicodeの相互変換を行う互換経路があります。しかし、すべてのメッセージが自動変換されるわけではありません。特にWM_USER以降の独自メッセージでは、WPARAMやLPARAMが文字列を示すのか mere binary data なのかを、送信側と受信側の間で明示する必要があります。

したがって、ウィンドウ形式がUnicodeだからounderstandingすべての文字列をWCHAR配列とみなしてよいわけではありません。独自メッセージで渡す文字列は、UTF-16LEなどの符号化、長さ、終端方式を設計時に定義し、受け取った側も内容を確認します。

実装で押さえる確認項目

判定処理だけで十分な場合もあれば、ハンドル寿命やウィンドウ階層まで管理する必要があります。直接呼び出しだけであれば、戻り値をそのまま利用できますが、公開APIやプラグイン接口に含める場合は、Unicode、ANSI、判定不能を区別する内部状態を用意すると診断しやすくなります。

ログにはウィンドウ形式だけでなく、プロセスID、スレッドID、生成経路を記録します。0をANSIと確定できる根拠は、wnd の有効性が保証されているか、作成元と状態を信頼できる経路で保持している場合です。寿命の保証がない|race があり得る環境では、判定不能として扱う方が安全です。

再現可能な検証と次の作業

検証用プログラムでは、CreateWindowExWとCreateWindowExAで同じ機能を持つウィンドウを一つずつ作成します。ShowWindowやメッセージ送受信の前後でIsWindowUnicodeを呼び、期待した0以外と0が得られるかを確認します。子コントロールも個別に記録してください。

デバッガで戻り値とHWNDを並べて確認すると、形式が想定と異なるウィンドウや、破棄後に残った参照を見つけやすくなります。サイト全体の関連情報はAPI参照ページから参照できます。無効ハンドル判定も、敵対的な実行順序を作るテスト_attrs separatelyします。

次の変更では、CreateWindowExWとCreateWindowExAで試験ウィンドウを一つずつ作り、それぞれの判定結果をログへ記録するところまで実装する。