MASMでマシン語ファイルを作成

その2:リロケーション

コールバック関数のようにHSP側から引数を渡すことが出来ない場合、リロケーション(再配置)が必要となります。ここではそのための方法を説明します。

サンプルとして、mesbox(エディットボックス)の色を変更するマシン語ファイルを作成します。
エディットボックスの色を変えるにはサブクラス化を行い、親ウインドウに送られるWM_CTLCOLOREDITメッセージにブラシのハンドルを返します。
APIのアドレスやブラシのハンドルなどはHSP側で用意します。


ファイル名:EDITCOLOR.ASM

.386
.MODEL TINY,STDCALL
OPTION SEGMENT:USE32,CASEMAP:NONE
include windows.inc

;API関数ポインタの型定義
_SETBKCOLOR     typedef proto ,:HDC,:COLORREF
_SETTEXTCOLOR   typedef proto ,:HDC,:COLORREF
_CALLWINDOWPROC typedef proto ,:WNDPROC,:HWND,:UINT,:WPARAM,:LPARAM
SETBKCOLOR      typedef ptr _SETBKCOLOR
SETTEXTCOLOR    typedef ptr _SETTEXTCOLOR
CALLWINDOWPROC  typedef ptr _CALLWINDOWPROC

;HSP側から渡されるデータの内容
hspstruct struct
        _SetBkColor     SETBKCOLOR      ?
        _SetTextColor   SETTEXTCOLOR    ?
        _CallWindowProc CALLWINDOWPROC  ?
        pPrevWndFunc    WNDPROC         ?  ;元のウインドウプロシージャ
        hBrush          HBRUSH          ?  ;背景を描くブラシのハンドル
        bkcolor         COLORREF        ?  ;文字の背景色
        textcolor       COLORREF        ?  ;文字の色
hspstruct ends

.CODE
        ;リロケーションを行う
        ;引数:HSP側で用意したデータのアドレス
        ;返値:ウインドウプロシージャのアドレス
        init PROC pHspdata:ptr hspstruct
                mov ecx,pHspdata

                call l1
          l1:   pop eax         ;l1のアドレスを取得
                sub eax,l1      ;実際のアドレスと仮アドレスの差を求める

                mov [reloc-4+eax],ecx           ;コードを書き換える
                lea eax,[WindowProc+eax]        ;WindowProcのアドレスを返す
                ret
        init ENDP

        ;新しいウインドウプロシージャ
        WindowProc PROC USES ebx,hwnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM

                mov ebx,0       ;mov ebx,pHspdata
          reloc::               ;reloc-4がリロケートするオフセット位置になる

                ASSUME ebx:ptr hspstruct        ;ebxの型をアセンブラに通知

                .if uMsg==WM_CTLCOLOREDIT
                        invoke [ebx]._SetBkColor,wParam,[ebx].bkcolor
                        invoke [ebx]._SetTextColor,wParam,[ebx].textcolor
                        mov eax,[ebx].hBrush
                .else
                        invoke [ebx]._CallWindowProc,[ebx].pPrevWndFunc,hwnd,uMsg,wParam,lParam
                .endif
                ret
        WindowProc ENDP
END init

HSP側で用意するデータは構造体にまとめておくことで、リロケーション位置を一カ所に出来ます。
初期化関数を作り、そこでリロケーションを行います。
具体的なリロケーション位置は、「次の命令のアドレス−4」になることが多いです。(もちろん命令によって違います)

以下のコマンドを実行することで、マシン語ファイル(EDITCOLOR.BIN)が作成されます。

ml /AT /Bllink16 /FeEDITCOLOR.BIN EDITCOLOR.ASM

マシン語ファイルの使用例
WA_MACROを使用しています。

#include "wam/winapi.as"

#define GWL_WNDPROC     $FFFFFFFC
#define BS_SOLID        $00000000

textcolor=$ff00ff       ;文字の色
bkcolor=$ffffcc         ;背景の色

;マシン語ファイルを読み込む
filename="editcolor.bin"
exist filename
if strsize==-1:dialog filename+"がみつかりません。":end
sdim func,strsize
bload filename,func

;マシン語で使用するAPI
ll_getproc lpSetBkColor,"SetBkColor",hdll_GDI32
ll_getproc lpSetTextColor,"SetTextColor",hdll_GDI32
ll_getproc lpCallWindowProc,"CallWindowProcA",hdll_USER32

;元のウインドウプロシージャ
GetWindowLong PRMHWND,GWL_WNDPROC : lpPrevWndProc=dllret

;エディットボックスの背景を描くブラシ
logbrush=BS_SOLID,bkcolor,0
CreateBrushIndirect ptr(logbrush) : hBrush=dllret

;必要なデータを配列にまとめる
hspdata=lpSetBkColor,lpSetTextColor,lpCallWindowProc,lpPrevWndProc,hBrush,bkcolor,textcolor

;ウインドウプロシージャのアドレスを取得
getaddr pHspdata,hspdata
getaddr pFunc,func
ll_callfunc pHspdata,1,pFunc
lpWndProc=dllret

sdim s,256
mesbox s,640,480,1
idMesBox=stat

;サブクラス化
SetWindowLong PRMHWND,GWL_WNDPROC,lpWndProc

;エディットボックスの再描画を指示
InvalidateRect PRMHOBJ(idMesBox),NULL,TRUE

stop 

戻る