Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2020年07月01日] [更新 2020年07月18日] Delphi 一般・その他
   
Lazarus における Windows API の関数の日本語文字列
動作確認等 Windows 7 U64(SP1) + lazarus-2.0.8
[整理番号 - 1510]







Lazarus における Windows API の関数の日本語文字列


Lazarus で,Windows API の関数を使用する際,日本語の文字列を使用する時の参考記事です.

Lazarus が文字列の処理に使用するコードページは UTF-8 です.
Windows API の関数には,Ansi 用とユニコード UTF-16 用の両方の関数を実装している関数があります.Ansi 用の関数名の末尾は A で,ユニコード UTF-16 用の関数の末尾は W です.Lazarus では,それらの末尾の A または W を省略すると Ansi 用の関数となります.

ASCII 文字 (半角の英数字) だけを扱うのであれば,Ansi 用の Windows API の関数が使用できます.この場合は,本ページで説明している処理は必要ありません.
Unix 系の OS は UTF-8 を使用しています.Linux で Lazarus を使用する場合も,本ページのような処理は必要ないかも知れません.



本ページで使用しているコードページ変換用の関数類は,Widows API の関数専用ではありません.以下のように,文字列 (テキスト) のコードページの変換に一般的に使用される関数類です.
コードページを明示的に指定したい場合は,LConvEncoding ユニットに実装されている CP932ToUTF8 などの関数が利用できます.


//=============================================================================
//  Shift_JIS のテキストをファイルから読み込んで TMemo に表示
//  コードページの変換には LConvEncoding ユニットの CP932ToUTF8 関数も使用可能
//
//  uses に LazUTF8 が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LStrList : TStringList;
begin
  LStrList := TStringList.Create;
  try
    LStrList.LoadFromFile('TEST.txt');
    Memo1.Lines.Text:= WinCPToUTF8(LStrList.Text);
  finally
    FreeAndNil(LStrList);
  end;
end;

//=============================================================================
//  TMemo のテキストを Shift_JIS のテキストとしてファイルに保存
//  コードページの変換には LazUTF8 ユニットの UTF8ToWinCP 関数も使用可能
//  TMemo.SaveToFile で保存すると UTF-8 (BOM 無し) のテキストになる
//
//  uses に LConvEncoding が必要
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LStrList : TStringList;
begin
  LStrList := TStringList.Create;
  try
    LStrList.Text := UTF8ToCP932(Memo1.Lines.text);
    LStrList.SaveToFile('TEST_SAVE.txt');
  finally
    FreeAndNil(LStrList);
  end;
end;


[備考 1]
AnsiString 型の文字と文字列は,日本 ( 日本語版の Windows ) では,コードページが 932 (CP932) の Shift_JIS コード体系の文字列を意味します.


[備考 2]
Lazarus では,Char 型の文字,String 型と PChar 型の文字列もコードページは UtF-8 です.ASCII 文字 (半角の英数字) は,UTF-8,AnsiChar 型,AnsiString 型,PAnsiChar 型共にコードポイント (文字のコード) が同じです.その結果,ASCII 文字は文字化けしないことになります.
全角の A, B 等は ASCII 文字ではありません.

[備考 3]
関数名の末尾に A も W もない場合,末尾に A がある Windows API の関数が使われるのは Lazarus に実装されている関数です.
Lazarus に実装されていない Windows API の関数を使用するには,自分で関数の定義文を作成します.その場合は,明示的に末尾に A または W がある関数を使用するようにします.




01_Windows API の関数から AnsiString 文字列を取得する場合


Windows API の関数の実行結果として AnsiString 型の文字列を取得する例です.この場合,Windows API の関数は,末尾に W がない関数を使用します.以下がテストに使用した関数です.

この関数の戻り値は string 型になっていますが,AnsiString 型にしても結果は同じです.関数内部の文字配列の型も AnsiChar 型にしても同じです.
この関数の戻り値の string 型の変数には AnsiString 型の文字列が格納されます.AnsiString 型の文字列変数を PChar でキャストすると,それは Ansi 文字列の Null 終端文字列になります.



リスト1
AnsiString 型の文字列を返す Windows API の関数の例
//-----------------------------------------------------------------------------
//  ウィンドウのタイトル名を取得する関数
//  戻り値が string 型の例
//  この場合の string 型の変数のコードページは AnsiString 型と同じになる
//
//  GetWindowText 関数の末尾に A も W もないので Ansi 用の関数
//-----------------------------------------------------------------------------
function GetWindowTitelStr(hWindow: HWND): string;
var
  LBuffer : array[0..MAX_PATH-1] of Char;
  LLen    : Integer;
begin
  FillChar(LBuffer, SizeOf(LBuffer), #0);
  LLen := GetWindowText(hWindow, LBuffer, Length(LBuffer));
  if LLen > 0 then begin
    Result := LBuffer
  end else begin
    Result := '';
  end;
end;


上の関数を使用して,自分自身のフォームのウィンドウのタイトルを取得します.
以下は,AnsiString 型の変数に ASCII 文字だけが含まれている場合です.UTF-8 の文字列変数に代入して,その文字列を表示用のコントロールに表示しても文字化けしません.



図1
ASCII 文字ならば文字化けしない

リスト2
//=============================================================================
//  Windows API で string ( AnsiString ASCII ) 文字列を取得
//  コードページの処理なし
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LTitle : string;
begin
  LTitle:= GetWindowTitelStr(Handle);

  Edit1.Text:= LTitle;
  ShowMessage(LTitle);
end;


AnsiString 型の変数に 日本語の文字が含まれている場合です.UTF-8 の文字列変数に代入して,その文字列を表示用のコントロールに表示すると文字化けします.



図2
AnsiString 型の文字列の日本語の文字は Lazarus てば認識できない

リスト3
//=============================================================================
//  Windows API で string ( AnsiString 日本語 ) 文字列を取得
//  コードページの処理なし
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LTitle : string;
begin
  Caption:= '日本語タイトル ABC';

  LTitle:= GetWindowTitelStr(Handle);

  Edit1.Text:= LTitle;
  ShowMessage(LTitle);
end;


AnsiString 型の変数に 日本語の文字が含まれている場合,Lazarus 側で処理するには文字列のコードページをUTF-8 に変換します.変換には LazUTF8 ユニットにある WinCPToUTF8 関数が利用できます.
WinCPToUTF8 の WinCP は,Windows CodePage と解釈しておくといいかも知れません.



図3
デバッガエラー発生
  • 日本語を Lazarus て認識させるためには UTF-8 に変換する

リスト4
//=============================================================================
//  Windows API で string ( AnsiString 日本語 ) 文字列を取得
//  WinCPToUTF8 (AnsiString をUTF-8 に変換) を使用しててコードページを変換

//  uses に LazUTF8 が必要
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LTitle : string;
begin
  Caption:= '日本語タイトル ABC';

  LTitle:= WinCPToUTF8(GetWindowTitelStr(Handle));

  Edit1.Text:= LTitle;
  ShowMessage(LTitle);
end;




02_Windows API の関数から Unicode 文字列を取得する場合


Windows API の関数の実行結果としてユニコードの文字列を取得する例です.この場合,Windows API の関数は,末尾が W の関数を使用します.以下がテストに使用した関数です.
この関数の戻り値を WideString 型にしてもユニコードの文字列を扱えます.その場合は,文字配列の文字の型を WideChar にします.
本ページで扱うユニコード文字列は,コートページが UTF-16 の文字列を意味します.



リスト5
ユニコード (UTF-16) の文字列を返す Windows API の関数の例
//-----------------------------------------------------------------------------
//  ウィンドウのタイトル名を取得する関数
//  戻り値が UnicodeString 型の例
//  この場合の UnicodeString 型の変数の文字のコードページは UTF-16 となる
//
//  ユニコードでは末尾に W がある GetWindowTextW 関数を使用する
//-----------------------------------------------------------------------------
function GetWindowTitelStr(hWindow: HWND): UnicodeString;
var
  LBuffer : array[0..MAX_PATH-1] of UnicodeChar;
  LLen    : Integer;
begin
  FillChar(LBuffer, SizeOf(LBuffer), #0);
  LLen := GetWindowTextW(hWindow, LBuffer, Length(LBuffer));
  if LLen > 0 then begin
    Result := LBuffer
  end else begin
    Result := '';
  end;
end;


上の関数を使用して,自分自身のフォームのウィンドウのタイトルを取得します. 以下は,ユニコードの文字列に ASCII 文字だけが含まれている場合です.UTF-8 の文字列変数に代入して,その文字列を表示用のコントロールに表示しても文字化けしません.



図4
ASCII 文字ならば文字化けしない

リスト6
//=============================================================================
//  Windows API で UnicodeString ( UTF-16 ASCII 文字のみ) 文字列を取得
//  コードページの処理なし
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LTitle : string;
begin
  LTitle:= GetWindowTitelStr(Handle);

  Edit1.Text:= LTitle;
  ShowMessage(LTitle);
end;


ユニコード文字列の変数に 日本語の文字が含まれている場合です.UTF-8 の文字列変数に代入して,その文字列を表示用のコントロールに表示していすが文字化けしていません.UTF-8 もユニコードの一種です.Lazarus 側で処理しているのかも知れません.詳細については未確認です.



図5
UTF-16 の文字列が文字化けしないで表示できている

リスト7
//=============================================================================
//  Windows API で UnicodeString ( UTF-16 の日本語) 文字列を取得
//  コードページの処理なし
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LTitle : string;
begin
  Caption:= '日本語タイトル ABC';

  LTitle:= GetWindowTitelStr(Handle);

  Edit1.Text:= LTitle;
  ShowMessage(LTitle);
end;


UTF-16 の文字列を UTF-8 に変換するには,LazUTF8 ユニットにある UTF16ToUTF8 関数が利用できます.このような関数が用意されているということは,UTF-16 の場合もコードページの変換が必要と思われます.



図6
UTF16ToUTF8 関数で変換した結果

リスト8
//=============================================================================
//  Windows API で UnicodeString ( UTF-16 の日本語) 文字列を取得
//  UTF16ToUTF8 (Unicode を UTF-8 に変換) を使用してコードページを変換
//
//  uses に LazUTF8 が必要
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LTitle : string;
begin
  Caption:= '日本語タイトル ABC';

  LTitle:= UTF16ToUTF8(GetWindowTitelStr(Handle));

  Edit1.Text:= LTitle;
  ShowMessage(LTitle);
end;




03_Windows API の関数に日本語の文字列を渡す場合


Lazarus の UTF-8 の文字列を Windows API の関数に渡す場合のテストてす.ここでは MessageBox 関数を使用しています.この関数はダイアログを表示し,引数で指定した文字列を表示します.

下図は,コードページを変換しないで Windows API の関数に文字列を渡した場合です.Windows API の関数は UTF-8 の文字列を正しく認識できません.そのため文字化けしています.
関数に渡す文字列が ASCII 文字だけであれば文字化けしません.



図7
Windows API の関数は UTF-8 の日本語を認識できない

リスト9
//=============================================================================
//  Windows API の関数の引数に文字列を渡す
//  UTF-8 の文字列を渡しているので文字化けする 
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LText : string;
begin
  LText:= '日本語 ABC';

  MessageBox(Handle, PChar(LText), 'TEST', MB_ICONINFORMATION);
end;


LazUTF8 ユニットの UTF8ToWinCP 関数を使用して ,Windows API の関数が認識できるコードページの文字列である AnsiString 型に変換してから関数に渡した場合です. 文字化けしていません.このコードでは,PChar はポインタとして使用されています.Pointer(LDstText) としても結果は同じです.

このコードの変数 LDstTex には AnsiString 型の文字列が格納されます.変数の宣言で AnsiString 型にしておいた方が無難と言えます.



図8
UTF8ToWinCP 関数を使用して Ansi 用の Windows API の関数に文字列を渡した結果

リスト10
//=============================================================================
//  Windows API の関数の引数に文字列を渡す
//  文字列を UTF8ToWinCP で変換して引数に渡しているので文字化けしない 
//
//  uses に LazUTF8 が必要
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LSrcText : string;
  LDstText : string;
begin
  LSrcText:= '日本語 ABC';

  LDstText:= UTF8ToWinCP(LSrcText);
  MessageBox(Handle, PChar(LDstText), 'TEST', MB_ICONINFORMATION);
end;


ユニコード用の Windows API の関数に日本語を含む文字列を渡す場合は,UTF8ToUTF16 関数を使用してコードページを変換します.
この場合,WideString 型よりも UnicodeString 型にしておくと,ユニコード用の Windows API の関数を使用するということが明確になるかも知れません.Lazarus では WideString 型と UnicodeString 型は互換性があります.



図9
UTF8ToUTF16 関数を使用して ユニコード用の Windows API の関数に文字列を渡した結果

リスト11
//=============================================================================
//  Windows API の関数の引数に文字列を渡す
//  Unicode 用 Windows API の関数の場合は UTF8ToUTF16 でコードページを変換する
//
//  uses に LazUTF8 が必要
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LSrcText : string;
  LDstText : WideString;
begin
  LSrcText:= '日本語 ABC';

  LDstText:= UTF8ToUTF16(LSrcText);
  MessageBoxW(Handle, PWideChar(LDstText), 'TEST', MB_ICONINFORMATION);
end;


コードページ変換用の関数は,Char 型の文字配列でも使用できます.また,PChar 型の文字列でも使用できます.
この例では WideChar 型を使用していますが,UnicodeChar 型を使用すればユニコード用の関数を使用することが明確になります.Lazarus では WideChar と UnicodeChar は同じです.Ansi 用の Windows API の関数を使用する時は AnsiChar 型が使用できます.



図10
コードページ変換用の関数は Char 型の文字配列でも使用可能
  • PChar 型の文字列でも使用可能

リスト12
//=============================================================================
//  Windows API の関数の引数に文字列を渡す
//  静的文字配列の場合
//  PChar の変数も同じように変換可能
//
//  uses に LazUTF8 が必要
//=============================================================================
procedure TForm1.Button4Click(Sender: TObject);
var
  LSrcText : array [0..MAX_PATH] of Char;
  LDstText : array [0..MAX_PATH] of WideChar;
begin
  LSrcText := '日本語テキスト';

  LDstText:= UTF8ToUTF16(LSrcText);
  MessageBoxW(Handle, PWideChar(LDstText), 'TEST', MB_ICONINFORMATION);
end;




04_FindWindow 関数を使用してウィンドウをタイトルで検索


FindWindow 関数の引数に日本語を含む文字列を渡す例です.FindWindow 関数の第 2 引数にウィンドウのタイトルの文字列を指定すると,そのタイトルのウィンドウを検索することができます.
ここまでのサンプルでは,文字化けという現象で文字列が正しく処理できたかを確認することができました.FindWindow のような関数では,そのような確認手段は利用できません.コードページの設定を確実に行う必要があります.



図11
FindWindow 関数を使用して,ウィンドのタイトルでウィンドウを検索
  • コートページかせ正しくないと検索に失敗する
  • ウィンドウのタイトルが ASCII 文字だけなら検索可能
図12
FindWindow 関数を使用して,ウィンドのタイトルでウィンドウを検索
  • コードページを正しく設定すれば日本語のタイトルも検索可能

リスト13
//=============================================================================
//  Windows API の関数の引数に文字列を渡す
//  FindWindow 関数の例
//  引数に UTF-8 の文字列を渡しているので検索に失敗する
//
//  uses に LazUTF8 が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LhWnd : HWND;
  LText : AnsiString;
begin
  Caption:= '日本語タイトル';

  LhWnd:= FindWindow(nil, PAnsiChar(Caption));

  if LhWnd = 0 then begin
    LText:= UTF8ToWinCP('見つかりませんでした');
    MessageBox(Handle, PAnsiChar(LText), 'Result', MB_ICONWARNING
    );
  end else begin
    LText:= UTF8ToWinCP('見つかりました');
    MessageBox(Handle, PAnsiChar(LText), 'Result', MB_ICONINFORMATION);
  end;
end;

//=============================================================================
//  Windows API の関数の引数に文字列を渡す
//  FindWindow 関数の例
//  文字列を UTF8ToWinCP で変換して引数に渡しているので検索に成功する
//
//  uses に LazUTF8 が必要
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LhWnd : HWND;
  LText : AnsiString;
begin
  Caption:= '日本語タイトル';

  LText:= UTF8ToWinCP(Caption);
  LhWnd:= FindWindow(nil, PAnsiChar(LText));

  if LhWnd = 0 then begin
    LText:= UTF8ToWinCP('見つかりませんでした');
    MessageBox(Handle, PAnsiChar(LText), 'Result', MB_ICONWARNING);
  end else begin
    LText:= UTF8ToWinCP('見つかりました');
    MessageBox(Handle, PAnsiChar(LText), 'Result', MB_ICONINFORMATION);
  end;
end;