ウインドウプロシージャのように、HSP側から必要な情報を渡すことが出来ない関数があります。そのような関数で静的データやAPIを使用したい場合、アドレスの書き換え(リロケーション=再配置)を行う必要があります。ここではリロケーションの手間を最小限に押さえる方法を説明します。
サンプルとして、mesbox(エディットボックス)の色を変更するマシン語ファイルを作成します。
mesboxの色を変えるにはサブクラス化を行い、親ウインドウに送られるWM_CTLCOLOREDITメッセージにブラシのハンドルを返します。
APIのアドレスやブラシのハンドルなどはHSP側で用意します。
ファイル名:editcolor.c
#include <windows.h> //API関数ポインタの型定義 typedef COLORREF (WINAPI *SETBKCOLOR)(HDC,COLORREF); typedef COLORREF (WINAPI *SETTEXTCOLOR)(HDC,COLORREF); typedef LRESULT (WINAPI *CALLWINDOWPROC)(WNDPROC,HWND,UINT,WPARAM,LPARAM); //HSP側で用意するデータ typedef struct{ SETBKCOLOR pSetBkColor; //API関数ポインタ SETTEXTCOLOR pSetTextColor; // 〃 CALLWINDOWPROC pCallWindowProc; // 〃 WNDPROC pPrevWndProc; //元のウインドウプロシージャ HBRUSH hBrush; //背景を描くブラシのハンドル COLORREF bkcolor; //文字の背景色 COLORREF textcolor; //文字の色 } hspstruct; LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) { hspstruct * volatile pHspdata=(hspstruct*)0xeeeeeeee; //この部分のコードを書き換える hspstruct *pData=pHspdata; switch(uMsg){ case WM_CTLCOLOREDIT: pData->pSetBkColor((HDC)wParam,pData->bkcolor); pData->pSetTextColor((HDC)wParam,pData->textcolor); return (LRESULT)(pData->hBrush); default: return pData->pCallWindowProc(pData->pPrevWndProc,hwnd,uMsg,wParam,lParam); } }
必要なデータは構造体にまとめておき、一度だけ構造体へのポインタを受け取るようにします。こうすることで書き換える位置を一カ所だけにすることが出来ます。
(hspstruct*)0xeeeeeeee は構造体の仮のアドレスで、後で書き換える場所の目印となります。
構造体のアドレスはvolatile変数に格納します。
こうしないと、コンパイラが仮のアドレスを使った最適化を行うため、意図しない影響を受けてしまいます。
その後volatile変数をそのまま使うわけにはいかないので、通常の変数にコピーして使うようにします。
最適化対策にはvolatile変数ではなく外部変数を使う方法もあります。
マシン語ファイルの作成方法は前回と同じです。
作成されたEDITCOLOR.BINをバイナリエディタなどでダンプ表示すると以下のようになります。
+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
00000000 51 8B 44 24 0C C7 04 24-EE EE EE EE 56 8B 74 24 |Q汽$.ヌ.$V逆$
00000010 04 3D 33 01 00 00 74 1C-8B 4C 24 18 8B 54 24 14 |.=3...t.記$.亀$.
00000020 51 8B 4E 0C 52 50 8B 44-24 18 50 51 FF 56 08 5E |Q起.RP汽$.PQ.V.^
00000030 59 C2 10 00 8B 56 14 57-8B 7C 24 18 52 57 FF 16 |Yツ..儀.W弓$.RW..
00000040 8B 46 18 50 57 FF 56 04-8B 46 10 5F 5E 59 C2 10 |祈.PW.V.祈._^Yツ.
00000050 00 - |.
ソースファイルで定義した0xEEEEEEEEが見えていますので、リロケートする位置は0x00000008ということが分かります。
この部分をHSP側で書き換えます。
もし該当箇所が複数ある時はソースファイルの値を適当な値に変えてからもう一度マシン語ファイルを作成してください。
マシン語ファイルの使用例
WA_MACROを使用しています。
#include "wam/winapi.as" #define GWL_WNDPROC $FFFFFFFC #define BS_SOLID $00000000 textcolor=$ff00ff ;文字の色 bkcolor=$ffffcc ;背景の色 relocpos=8 ;リロケーション位置 ;マシン語ファイルを読み込む filename="editcolor.bin" exist filename if strsize==-1:dialog filename+"がみつかりません。":end sdim func,strsize bload filename,func ;マシン語で使用するAPI ll_getproc pSetBkColor,"SetBkColor",hdll_GDI32 ll_getproc pSetTextColor,"SetTextColor",hdll_GDI32 ll_getproc pCallWindowProc,"CallWindowProcA",hdll_USER32 ;元のウインドウプロシージャ GetWindowLong PRMHWND,GWL_WNDPROC pPrevWndProc=dllret ;エディットボックスの背景を描くブラシ logbrush=BS_SOLID,bkcolor,0 CreateBrushIndirect ptr(logbrush) hBrush=dllret ;必要なデータを配列にまとめる hspdata=pSetBkColor,pSetTextColor,pCallWindowProc,pPrevWndProc,hBrush,bkcolor,textcolor ;リロケーション getaddr pHspdata,hspdata memcpy func,pHspdata,4,relocpos sdim s,256 mesbox s,640,480,1 idMesBox=stat ;サブクラス化 SetWindowLong PRMHWND,GWL_WNDPROC,ptr(func) ;エディットボックスの再描画を指示 InvalidateRect PRMHOBJ(idMesBox),NULL,TRUE stop