[[MinGWによるWindows Programming]]

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

一般に Windows で多国語対応の programming を行う場合は
<tchar.h> を include して _Tマクロを使うことが多い。
これによりnaitive code の
実行イメージ用でも Unicode の実行イメージ用でも
* _Tマクロの利用 [#tf9d5a9e]
一般に 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 の日本語問題 [#pa4f1f9e]
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 を使う方法 [#gc99fc0e]
とりあえず、別の 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 でも
うまくいくだろう。

** 漢字プリプロセッサを通す方法 [#n6aa2e99]
上記方法でうまくいくのではあるが、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