ここでは、Pilot Desktop に限らず、非日本語対応 Windows アプリケーショ ンで日本語を表示させるパッチを作成するための一般的手法について解説します。 同様の手法で中文やハングルなども表示できると思います。
注
本ページの内容は筆者が独自に調査したものです。
調査はなるべく誤りのないように留意したつもりですが、調査は完璧というも
のからは程遠いものです。それ故、このページの内容に基づきパッチを当てる
ことは、ディスク上のデータの破壊などの危険性を伴ないます。十分注意して
下さい。
また、本ページの内容に基づいて行われてた全ての行為及びその結果について、 筆者は一切の責任を負わないものとします。特に、多くの商用アプリケーション にはライセンス中にリバースエンジニアリング禁止規定があることに留意して 下さい。
では次に以下に CreateFont の宣言を参照しより具体的に見ていきます。
(BC5 附属のヘルプ Win32 Programes's Reference より引用。以下特に断りが
ない限り、同 Reference からの引用です。)
HFONT CreateFont(
int nHeight, // logical height of font
int nWidth, // logical average character width
int nEscapement, // angle of escapement
int nOrientation, // base-line orientation angle
int fnWeight, // font weight
DWORD fdwItalic, // italic attribute flag
DWORD fdwUnderline, // underline attribute flag
DWORD fdwStrikeOut, // strikeout attribute flag
DWORD fdwCharSet, // character set identifier
DWORD fdwOutputPrecision, // output precision
DWORD fdwClipPrecision, // clipping precision
DWORD fdwQuality, // output quality
DWORD fdwPitchAndFamily, // pitch and family
LPCTSTR lpszFace // address of typeface name string
);
上から判るように、論理フォントの作成には多くのパラメータがありますが、
日本語の表示のためだけでしたら、fdwCharSet のみを書き変えれば OK です。
タイプフェイス名すら書き変える必要はありません。
Windows はパラメータの値を参照して、パラメータの値に近いフォントを選択
してくれます。
fdwCharSet のパラメータは以下のようなものが wingdi.h で定義されていま
す。
#define ANSI_CHARSET 0
#define DEFAULT_CHARSET 1
#define SYMBOL_CHARSET 2
#define SHIFTJIS_CHARSET 128
#define HANGEUL_CHARSET 129
#define GB2312_CHARSET 134
#define CHINESEBIG5_CHARSET 136
#define OEM_CHARSET 255
(以下略)
ということで、日本語フォントを選択するためにはfdwCharSet を 128(0x80)
にすれば良いことが判ります。
次に CreateFontIndirect について見てみます。
HFONT CreateFontIndirect( CONST LOGFONT *lplf // address of logical font structure );ここで LOGFONT 構造体は
typedef struct tagLOGFONT { // lf LONG lfHeight; LONG lfWidth; LONG lfEscapement; LONG lfOrientation; LONG lfWeight; BYTE lfItalic; BYTE lfUnderline; BYTE lfStrikeOut; BYTE lfCharSet; BYTE lfOutPrecision; BYTE lfClipPrecision; BYTE lfQuality; BYTE lfPitchAndFamily; TCHAR lfFaceName[LF_FACESIZE]; } LOGFONT;と定義されています。見てわかるように、LOGFONT 構造体の要素は CreateFont のパラメータと同一です。 つまり、CreateFont で沢山のパラメータを並べる代りに、LOGFONT 構造体に 予め値を代入し、そのポインタを渡すことで間接的(Indirect)にパラメータを 渡すのが、この CreateFontIndirect になります。
LRESULT SendMessage( HWND hwnd, // handle of destination window UINT uMsg, // message to send WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); WM_SETFONT wParam = (WPARAM) hfont; // handle of font lParam = MAKELPARAM(fRedraw, 0); // redraw flagダイアログの仕様上、フォントさえ日本語フォントを選択すれば、多くの場合 問題なく日本語の入力/表示が可能となるようです。さすがに禁則処理などは してくれませんが。
68xxxxxxxx push xxxxxxxx // Pointer of Typeface Name 6a04 push +00 // fdwPitchAndFamily DEFAULT_PITCH 6a00 push +00 // fdwQuality DEFAULT_QUALITY 6a20 push +20 // fdwClipPrecision CLIP_TT_ALWAYS 6a04 push +04 // fdwOutputPrecison OUT_TT_PRECIS 6a00 push +00 // fdwCharSet ANSI_CHARSET 6a00 push +00 // fdwStrikeOut FALSE 6a00 push +00 // fdwUnderLine FALSE 6a00 push +00 // fdwItalic FALSE 68bc020000 push 000002bc // fnWeit FW_BOLD 6a00 push +00 // nOrientation 6a00 push +00 // nEscapement 6a00 push +00 // nWidth 6a0e push +0e // nHeight ff15185a0410 call dword ptr [GDI32.CreateFontA]これは典型的な CreateFont API 呼び出しのパターンです。(CreateFont では なく CreateFontA を呼びだしていますが、ここでは特に気にする必要はあり ません)。 このように呼び出しパラメータは、C で記載した場合の左から順にスタックに 積まれていき、API をコールすることで実行されます。戻り値は eax レジス タに格納されるようです(きちんと確認したわけではありませんが)。 上の例では、
6a00 push +00 // fdwCharSet ANSI_CHARSETを
6a80 push -80 // fdwCharSet SHIFTJIS_CHARSETと書き変えてやれば日本語フォントが生成されることになります。
8d8d6cffffff lea ecx,dword ptr [ebp+ffffff6c] // 6c = -94 51 push ecx ff15xxxxxxxx call dword ptr [GDI32.CreateFontIndirectA]というコードがあった場合、
b080 mov al,80 884583 mov [ebp-7d],al // -94 + 17 = -7dといったようなコードを API 呼び出しより前の適当な場所に挿入できれば日 本語のフォントが作成されるわけです(挿入できるかどうかは、なかなか難し い問題なのですが...)。
API 呼び出しのパラメータを変更する場合、該当する値を直接書き変えられれ ば良いのですが、そうもいかない場合があります(レジスタに格納された値を パラメータとしてプッシュする場合等)。その場合は、プログラムコー ド自体を書き変える必要があるのですが、この時コード全体の大きさを増減させる ことはできません。また、ジャンプやコール命令の飛び先が狂うようなコード の変更も出来ません(該当するジャンプやコール命令の飛び先を書きかえてや ればこの問題は回避できますが、かなり大変な作業となります)。
この制限は特に CreateFontIndirect 呼び出しによるフォント呼び出しの際に 問題となります。LOGFONT 型のデータに直接値を代入しているようなコードで したら問題は少ないのですが、通常予め LOGFONT 型のデータが格納されてい るところに、変更が必要なパラメータのみ値を変更するというような使われ方 をされる場合が多いためです(例えば同一ウィンドウ中で強調部分のみボール ド体を使う場合、現在のデバイスコンテキストからフォントの情報を読み 出し、fwWeit パラメータのみ書き変えて新たに論理フォントを生成する、と いった使われ方が一般的なようです)。
このような場合以下のような対応策が考えられます。
同一の働きをする命令長の短いコードに置き換えを行い、空いた部分にコー
ドを埋め込む。
これは、コンパイラの最適化が不十分であることに期待するものです。
例えば API 呼び出しはパラメータをスタックにプッシュするので
6820000000 push 00000020 6a20 push +20は等価であることが利用できます。
6a00 push +00 6a00 push +00 6a00 push +00というコードがあった場合
33c0 xor eax,eax 50 push eax 50 push eax 50 push eaxと書き換えることができます。この場合1バイト稼いだだけですが、 lfOutPrecision, lfClipPrecision, lfQuality, lfPitchAndFamily などのパ ラメータはデフォルトの 0 でもそれほど支障がない場合も多いので、細かい 設定を諦め、これらの値も 0 とすることにより多くの空きを作ることができ ます(もっとも CreateFont での日本語表示のためには 6a80 (push -80) とい う2バイトのデータを埋めこめればいいので、ここまでする必要はほとんどあ りません。)
CreateFontIndirect 呼び出しが行われている場合はレジスタの値を直接 LOGFONT 型の要素にコピーしたり、別の変数の値をコピーしていることがあり ます(その場合、CreateFontIndirect の前に GetTextMetrics などで現在のフォ ントのパラメータを読み出している場合が多い)。 そして、上のデフォルト値を使うテクニックを用いても書き換えのためのコー ドに余裕がない場合があります。
このような場合は、lfItalic, lfUnderLine, lfStrikeOut といったパラメー
タは、TRUE, FALSE の2値を取ることを考慮して、これらのパラメータに値
を代入しているコードを削除し、そこに lfCharSet パラメータへの代入のコー
ドを書き込むという手法があります。この場合、これらのパラメータは不定と
なりますが、最悪でも本来イタリックであるべきフォントがイタリックでなく
なるといった程度で、プログラムによっては十分許容できる程度の問題ですみ
ます。
Resource Workshop を用いずにダイアログのデフォルトフォントを書き換
える手法
注
この手法はやってみたらたまたま上手く行ったというレベルであり、十分な確
認作業を行っていません。
ダイアログのデフォルト設定のフォントは、デフォルト設定のフォント以下の 長さを持つフォントであれば、書き換えることができます。 例えば MS Sans Serif -> FixedSys は可。Arial -> FixedSys は不可、とい う具合になります。 ダイアログのデフォルトフォントはバイナリ中に以下のような形で格納されて いるようです。
xx xx xx xx xx xx xx xx xx xx xx xx xx xx 4e 00 ..............N. 6f 00 74 00 65 00 20 00 65 00 64 00 69 00 74 00 o.t.e. .e.d.i.t. 6f 00 72 00 00 00 08 00 4d 00 53 00 20 00 53 00 o.r.....M.S. .S. 61 00 63 00 73 00 20 00 53 00 65 00 72 00 69 00 a.n.s. .S.e.r.i. 66 00 00 00 xx xx xx xx xx xx xx xx xx xx xx xx f...............この例では、"Note Editor" という名前のダイアログに 8 ポイントの "MS Sans Serif" が設定されています。各データ/文字は2バイトを1単位とし て格納されているようです。 これを FixedSys に書き換えるには、
xx xx xx xx xx xx xx xx xx xx xx xx xx xx 4e 00 ..............N. 6f 00 74 00 65 00 20 00 45 00 64 00 69 00 74 00 o.t.e. .e.d.i.t. 6f 00 72 00 00 00 08 00 46 00 69 00 78 00 65 00 o.r.....F.i.x.e. 64 00 53 00 79 00 73 00 20 00 20 00 20 00 20 00 d.S.y.s. . . . . 20 00 00 00 xx xx xx xx xx xx xx xx xx xx xx xx ...............としてやれば良いようです(フォント名の後にスペース(0x0020)を挿入し、 文字列長を変更しないようにするのがポイントです)。