或曰

日曜日, 12月 23, 2007

Good-bye ATL

ATL/WTL に依存していた部分を素の C++ で書き直し。これで VC++ Express Edition でも動くかな。

Windows プログラミングを C++ で行う際に面倒なのは、ウィンドウプロシージャに this ポインタを渡すこと。SetWindowLongPtr(), GetWindowLongPtr() を使ってウィンドウハンドルに this ポインタを関連付けるにしても、CreateWindowEx() した時点で WM_MINMAXINFO などいくつかのメッセージが飛んでくるため、それをメンバ関数で処理しようと思うと、結局は一度 this ポインタをグローバルな領域に書いておく必要がある。

ATL では 一度グローバル変数 _Module からたどれるデータ構造にスレッドIDをキーにして this ポインタを書き込んでおいて、後でウィンドウプロシージャを thunk に差し替える という方法を採用している。

あそこまで作りこむのは面倒なので、多少パフォーマンスが落ちるがお手軽なところで手を打っておく。次のような内容の thunk を作成し、これを最初からウィンドウプロシージャとして登録。
  1. 第一引数に渡ってくるウィンドウハンドル (hWnd) をメンバ変数に保存
  2. hWnd を this ポインタに差し替え
二回目以降は最初の処理が無駄になる。第一引数の hWnd は、関数が呼ばれた時点では %esp + 4 の位置にあるので、コードはこんな感じになる。
push %eax
movl 8(%esp), %eax ; %eaxをスタックに退避した分だけ +4 ずれてる。
movl %eax, pThis->m_hWnd
pop %eax
movl pThis, 4(%esp)
jmp WndProc
IA32 のコードをハンドアセンブルする気にはなれなかったので、それっぽいコードを書いて GNU as でアセンブルし、出力を逆アセンブルして thunk の雛形に。
IA32 だと scratch register どれか忘れたので、とりあえずスタックに保存して %eax 使用。%eax, %ecx あたりは呼び出された側で値壊して OK だったような気がするけど、資料どこにいったかなぁ…。

2007/12/25 追記

ABI の資料を HDD から発見。eax, ecx, edx は callee 側で破壊して OK ということで、私が書いた thunk は push, pop 命令を消して、その間に挟まれている 8(%esp) を 4(%esp) にすれば良いね。

ABI は今だと SCO のサイトからダウンロードできる。Intel386™ Architecture Processor Supplement Fourth Edition の p.37-38 あたり。

ラベル:

0 件のコメント:

コメントを投稿



この投稿へのリンク:

リンクを作成

<< ホーム