MinGWによるWindows Programming

ここではMingGWを使って日本語Windows Programmingを行う際の 注意点を述べる。

_Tマクロの利用

一般に Windows で多国語対応の programming を行う場合は<tchar.h> を include して文字列に対し _Tマクロを使う。 これによりlocal code の 実行イメージ用でも UNICODE の実行イメージ用でも 共通のコードを書くことができるようになる。 例えばMinGWによるWindows Programmingに示した winmain.c に 対応するものは

#define STRICT
#include <windows.h>
#include <tchar.h>

#define MY_CLASS_NAME   _T("MyClass")
#define MY_WINDOW_TITLE _T("MyTitle")

/* Prototype */
LRESULT CALLBACK
  WndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
  LPSTR lpCmdLine, int nCmdShow )
{
  MSG msg;
  WNDCLASSEX wcex;
  HWND hwnd;

  /* Register the window class for the main window. */
  if( hPrevInstance == NULL )
  {

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = WndProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName = _T("MyMenu");
    wcex.lpszClassName = MY_CLASS_NAME;
    wcex.hIconSm = 0;

    if ( RegisterClassEx( &wcex ) == 0 )
      return 0;
  }
	
  /* Create our window. */
  hwnd = CreateWindow( MY_CLASS_NAME, MY_WINDOW_TITLE, WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
    (HWND)NULL, (HMENU)NULL, hInstance, (LPVOID)NULL );
  /*
   * If the window cannot be created, terminate
   * the application.
   */
  if( hwnd == NULL )
    return 0;

  /* Show the window and paint its contents. */
  ShowWindow( hwnd, nCmdShow );
  UpdateWindow( hwnd );

  /* Start the message loop. */
  while( GetMessage( &msg, (HWND)NULL, 0, 0 ) )
  {
    TranslateMessage( &msg );
    DispatchMessage( &msg );
  }

  /* Return the exit code to the system. */
  return msg.wParam;
}

という具合である。

UNICODE の実行イメージを生成するには、compile 時に UNICODE と _UNICODE を define する。

gcc -mwindows -DUNICODE -D_UNICODE winmain.c wndproc.c myapp.o

windres の日本語問題

windres は一般に2バイトコードの片方に 0x5C となるコードが含まれている場合、 正しく扱えない。 SHIFT-JIS コードでは、例えばメニューによくある「表示」の 「表」の字が問題を引き起こす。 一方出力は、res file 中の文字列は UCS-2LE であり、 ソースをざっと眺めた感じでは、 UNICODEへの変換には MutiByteToWideChar?() が呼ばれていて、ソース文字列の コードは CP_ACP に固定されているので、上の 0x5C 問題さえなければ local code のソースで正しく変換される(逆に言えば、local code にしないと いけない)ようである。

日本語版 windres なるものが存在するが、実装の仕方が悪い(日本語を 特別扱いする)ので正式版に入れてもらえるかどうか疑問である。

元の(日本語版でない)ソースコードを見る限り、UTF-8 対応にすることは 容易にできそうである。 UTF-8 では 0x00-0x7F は ASCII 文字(制御コードを含む)だし、 そうでないコードは 0x00-0x7F は含まないので、特に新たな論理を作らなくても 問題を引き起こさないと思われる。また日本語だけでなく他の言語に対しても 有効である。ざっと眺めた感じでは UTF-8 の string を UCS-2LE に変換する関数を用意し(これは単なる encoding の変更なので自明)string を UNICODE で出力している部分で その関数を呼ぶようにすればよいだけと思われる。試してみたいところだが どうやら再build には yacc なども設定しないといけないみたいで、 開発環境を整えるのに時間がかかりそうなので後回し。

別の resource complier を使う方法

とりあえず、別の resource compiler を使って .res file を作り、 その .res file を windres で .o file にする(.o file にしないと ld が受け付けない)。で、 resource compiler としては Microsoft が無償提供している Windows Platform SDK に付属の resource compiler である rc を使ってみた。

rc myapp.rc
windres myapp.res myapp.o
gcc -mwindows ... myapp.o

この方法はうまくいく。多分 Borland の resource compiler でも うまくいくだろう。

漢字プリプロセッサを通す方法

上記方法でうまくいくのではあるが、MinGW (GNU)以外のツールを使う ということで少し悔しい。別の方法を考えてみよう。

もう一つの方法は、昔(1986年頃--20年前!) よくやられたいわゆる漢字プリプロセッサをかませる方法で ある。漢字プリプロセッサは例えばソース中にある

表示

\225\134\216\246

に置き換える。こうすれば windres で正しく扱えることは確認した。 一応漢字プリプロセッサの論理をまとめておくと

  • 1バイト(第1バイト目)を読む。
  • 0x00-0x7f であれば、そのまま出力して最初に戻る。 そうでなければ octal 表示で出力する。
  • 第1バイト目が0x81-0x9f または 0xe0-0xfc の範囲にあれば次の1バイト(第2バイト目)を読む。 そうでなければ最初に戻る。
  • 第2バイト目が 0x40-0x7e または 0x80-0xfc の範囲にあれば octal 表示で出力し最初に戻る。そうでなければ第2バイト目が 0x00-0x7f で あれば、そのまま出力、そうでなけれ octal 表示で出力して最初に戻る。

でよい(はず)。

で、昔作ったプログラムを見つけようとしたが3分で見つからなかったので 書いたほうが早いということで書きなぐってみた。 名づけて sjisto7.c。とりあえず標準入力、標準出力だが、 ファイル指定の改造もしやすいように fxxx シリーズの関数を使っておく。

/* sjisto7 */

#include <stdio.h>

void octprint(int c, FILE* fp)
{
  fprintf( fp, "\\%03o", c );
}

int main()
{
  FILE* src;
  FILE* dst;
  int c1, c2;

  src = stdin;
  dst = stdout;

  while((c1 = fgetc( src )) != EOF)
  {
    if((c1 >= 0x00) && (c1 <= 0x7f))
      fputc( c1, dst );
    else
    {
      octprint( c1, dst );
      if(((c1 >= 0x81) && (c1 <= 0x9f)) ||
         ((c1 >= 0xe0) && (c1 <= 0xfc)))
      {
        c2 = fgetc( src );
        if(c2 == EOF)
          return 1;
        if(((c2 >= 0x40) && (c2 <= 0x7e)) ||
           ((c2 >= 0x80) && (c2 <= 0xfc)))
          octprint( c2, dst );
        else if((c2 >= 0x00) && (c2 <= 0x7f))
          fputc( c2, dst );
        else
          octprint( c2, dst );
      }
    }
  }
  return 0;
}

これを使って

sjisto7 < myapp.rc | windres -o myapp.o

とすれば MinGW だけで無事できあがり(もちろん sjisto7.exe も MinGW で作る)。 ただし、この方法でも DIALOG の TEXT などに日本語が含まれる場合、 新しい windres(GNU windres 2.16.91 20050827) でないと、正しく生成されなかった(GNU windres 2.15.91 20040904ではダメ)。


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2007-02-15 (木) 12:48:35