|
|
|
2017/8/1(Tue) 14:31:57|NO.80801
作ってみました、コールバックモジュール。
hspでコールバック関数を扱うには、
ちょくとさんのhscallbk.dll
http://chokuto.ifdef.jp/download/#hscallbk
tds12さんのmodclbkなどがあります。
http://hsp.tv/play/pforum.php?mode=pastwch&num=62130
(Noapさんも作られていたようですが、リンクが切れています)
http://hsp.tv/play/pforum.php?mode=pastwch&num=73509
このモジュールはtds12さんのmodclbkとほとんど同じですが、やや異なります。
・thiscallには対応してない
・可変長引数に対応
(あと、動的なメモリ確保を省いたので少し軽いかもしれません)
また、modclbkと同じく、浮動小数点型の引数には対応していません
対応している呼び出し規約はcdeclとstdcallの二つです
ご意見ご感想、お待ちしております。
#module hsp_callme_maybe
#uselib "kernel32.dll"
#func VirtualProtect "VirtualProtect" var, int, int, var
#define PAGE_EXECUTE_READWRITE 0x00000040
// callback_init
//-------------------------------------------------------------
// コールバックモジュールを初期化する
// このモジュールのほかの関数より前に一度呼び出す必要がある
#deffunc callback_init
dim code_, 12
dim popargs_, 8
old = 0
VirtualProtect code_, 48, PAGE_EXECUTE_READWRITE, old
VirtualProtect popargs_, 32, PAGE_EXECUTE_READWRITE, old
mref ctx, 68
pctx = varptr(ctx)
code_(0) = $8de58955,$00a30845,$c7000000,$00000005
code_(4) = $00000000,$0000b900,$00680000,$ff000000
code_(8) = $04c483d1,$000000a1,$00c2c900,$00000000
lpoke code_, $07, pctx + 40
lpoke code_, $0d, pctx + 36
lpoke code_, $16, lpeek(ctx, 216)
lpoke code_, $25, pctx + 784
popargs_(0) = $bae58955, $00000000, $018b0a8b, $8904c183
popargs_(4) = $0000ba0a, $0a8b0000, $8901e983, $00c3c90a
lpoke popargs_, $04, pctx + 40
lpoke popargs_, $12, pctx + 36
return
// callback_create func, lab, nargs, abi
// func: コールバック関数が代入される変数
// lab: コールバックラベル
// nargs: 引数の数
// abi: 呼び出し規約
//----------------------------------------------------------------------------------------------
// コールバック関数を作成
// コールバックラベル(lab)を呼び出す関数を動的に作成し、funcに代入する
//
// abiにABI_STDCALLを指定した場合
// stdcall呼び出し規約の関数を作成する
// このとき、nargsは0以上の数値を指定する必要がある
// コールバックラベルでは、callback_pop_arg(), callback_args によって引数を取得できる
//
// abiにABI_CDECLを指定した場合
// cdecl呼び出し規約の関数を作成する
//
// nargsに0以上の数値を指定した場合、引数は固定長となる
// コールバックラベルではcallback_pop_arg(), callback_args によって引数を取得できる
//
// nargsに0未満の数値を指定した場合、引数は可変長、固定長のどちらも可
// ただし、コールバックラベルでは callback_args による引数の取得はできない
#define global ABI_CDECL 0x00
#define global ABI_STDCALL 0x01
#define global callback_create(%1,%2,%3,%4) tmp = %2 : _callback_create %1, tmp, %3, %4
#deffunc _callback_create array func, var lab, int _nargs, int _abi
dim func, 12
plab = 0
memcpy plab, lab, 4
memcpy func, code_, 48
if (_abi == ABI_STDCALL) {
assert _nargs >= 0
wpoke func, $2b, (_nargs * 4) & 0xffffff
}
lpoke func, $11, _nargs
lpoke func, $1b, plab
return
// winapi_callback(pfunc, lab, nargs)
// pfunc: コールバック関数へのポインタが代入される変数
// lab: コールバックラベル
// nargs: 引数の数
//---------------------------------------------------------------------------------------------------
// コールバックラベル(lab)を呼び出す関数を動的に作成し、その関数へのポインタをpfuncに代入する
// コールバック関数の呼び出し規約は stdcall
// nargsは0以上の数値を指定する必要がある
// コールバックラベルでは、callback_pop_arg(), callback_args によって引数を取得できる
#define global winapi_callback(%1,%2,%3) %tclbk_var callback_create %i, %2, %3, ABI_STDCALL : %1 = varptr(%o)
// c_callback(pfunc, lab, nargs)
// pfunc: コールバック関数へのポインタが代入される変数
// lab: コールバックラベル
// nargs: 引数の数
//---------------------------------------------------------------------------------------------------
// コールバックラベル(lab)を呼び出す関数を動的に作成し、その関数へのポインタをpfuncに代入する
// コールバック関数の呼び出し規約は cdecl
//
// nargsに0以上の数値を指定した場合、引数は固定長となる
// コールバックラベルではcallback_pop_arg(), callback_args によって引数を取得できる
//
// nargsを省略、もしくは0未満の数値を指定した場合、引数は可変長、固定長のどちらも可
// コールバックラベルでは callback_args による引数の取得はできない
#define global c_callback(%1,%2,%3=-1) %tclbk_var callback_create %i, %2, %3, ABI_CDECL : %1 = varptr(%o)
// callback_arg_left
//---------------------------------------------------------------------------------------------------
// コールバック作成時に引数の数として0以上の数値を指定していたならば、残りの引数の数を返す
#define global callback_arg_left wparam
// callback_pop_arg()
// callback_next_arg
//-----------------------------------------------------------------------
// 引数を一つ取得
// コールバック作成時に引数の数を指定していなくても取得できる
// なので可変長引数の取得も可能
#define global callback_next_arg callback_pop_arg()
#defcfunc callback_pop_arg
if wparam != 0 {
return callfunc(prm, varptr(popargs_), 0)
}
return
// callback_args args
// args: 引数が代入される配列
//-------------------------------------------------------------------
// 引数をすべて取得
// コールバック作成時に引数の数を指定していなかった場合、-1が戻る
// 成功すると、引数の数が戻る
#deffunc callback_args array args
if wparam > 0 {
dim args, wparam
dupptr args, lparam, wparam * 4
}
return wparam
#global
callback_init //モジュール初期化
// サンプル
#if 1
mes : mes "##sample 1"
#uselib "user32.dll"
#func EnumWindows "EnumWindows" int, int
#func GetWindowText "GetWindowTextA" int, sptr, int
winapi_callback lpEnumProc, *EnumWindowsProc, 2
EnumWindows lpEnumProc, 0
mes : mes "##sample 2"
#uselib "msvcrt.dll"
#func qsort "qsort" int, int, int, int
randomize
repeat 20 : data(cnt) = rnd(100) : loop
s = "ソート前: "
foreach data : s += "" + data(cnt) + "," : loop : s = strtrim(s, 2, ',')
mes s
c_callback sortfunc, *sort, 2
qsort varptr(data), 20, 4, sortfunc
s = "ソート後: "
foreach data : s += "" + data(cnt) + "," : loop : s = strtrim(s, 2, ',')
mes s
stop
;sample 1
*EnumWindowsProc
h = callback_next_arg
lp = callback_next_arg
sdim text, 256
GetWindowText h, varptr(text), 256
if text != "" {
mes text
i++
}
if i >= 10 {
return 0
}
return 1
; sample 2
*sort
callback_args args
dupptr a, args(0), 4
dupptr b, args(1), 4
if a < b {
return -1
} else : if a > b {
return 1
}
return 0
#endif
| |
|
2017/8/1(Tue) 19:59:48|NO.80802
よく見るとdupptr周りが危険だったので修正
#module hsp_callme_maybe
#uselib "kernel32.dll"
#func VirtualProtect "VirtualProtect" var, int, int, var
#define PAGE_EXECUTE_READWRITE 0x00000040
#deffunc callback_init
dim code_, 12
dim popargs_, 8
old = 0
VirtualProtect code_, 48, PAGE_EXECUTE_READWRITE, old
VirtualProtect popargs_, 32, PAGE_EXECUTE_READWRITE, old
mref ctx, 68
pctx = varptr(ctx)
code_(0) = $8de58955,$00a30845,$c7000000,$00000005
code_(4) = $00000000,$0000b900,$00680000,$ff000000
code_(8) = $04c483d1,$000000a1,$00c2c900,$00000000
lpoke code_, $07, pctx + 40
lpoke code_, $0d, pctx + 36
lpoke code_, $16, lpeek(ctx, 216)
lpoke code_, $25, pctx + 784
popargs_(0) = $bae58955, $00000000, $018b0a8b, $8904c183
popargs_(4) = $0000ba0a, $0a8b0000, $8901e983, $00c3c90a
lpoke popargs_, $04, pctx + 40
lpoke popargs_, $12, pctx + 36
return
#define global ABI_CDECL 0x00
#define global ABI_STDCALL 0x01
#define global callback_create(%1,%2,%3,%4) tmp = %2 : _callback_create %1, tmp, %3, %4
#deffunc _callback_create array func, var lab, int _nargs, int _abi
dim func, 12
plab = 0
memcpy plab, lab, 4
memcpy func, code_, 48
if (_abi == ABI_STDCALL) {
assert _nargs >= 0
wpoke func, $2b, (_nargs * 4) & 0xffffff
}
lpoke func, $11, _nargs
lpoke func, $1b, plab
return
#define global winapi_callback(%1,%2,%3) %tclbk_var callback_create %i, %2, %3, ABI_STDCALL : %1 = varptr(%o)
#define global c_callback(%1,%2,%3=-1) %tclbk_var callback_create %i, %2, %3, ABI_CDECL : %1 = varptr(%o)
#define global callback_arg_left wparam
#define global callback_next_arg callback_pop_arg()
#defcfunc callback_pop_arg
if wparam != 0 {
return callfunc(prm, varptr(popargs_), 0)
}
return
#deffunc callback_args array newargs, local args
if wparam > 0 {
dim args, wparam
dim newargs, wparam
dupptr args, lparam, wparam * 4
memcpy newargs, args, wparam * 4
}
return wparam
#global
| |
|
2017/8/1(Tue) 20:57:34|NO.80803
よく考えたら可変長引数のコールバック関数なんて需要ない気がしてきたので、固定長のみの
バージョンも置いておきます
#module hsp_callme_maybe_min
#uselib "kernel32.dll"
#func VirtualProtect "VirtualProtect" var, int, int, var
#define PAGE_EXECUTE_READWRITE 0x00000040
#deffunc callback_init
dim code_, 12
old = 0
VirtualProtect code_, 48, PAGE_EXECUTE_READWRITE, old
mref ctx, 68
pctx = varptr(ctx)
code_(0) = $8de58955,$00a30845,$c7000000,$00000005
code_(4) = $00000000,$0000b900,$00680000,$ff000000
code_(8) = $04c483d1,$000000a1,$00c2c900,$00000000
lpoke code_, $07, pctx + 40
lpoke code_, $0d, pctx + 36
lpoke code_, $16, lpeek(ctx, 216)
lpoke code_, $25, pctx + 784
return
#define global ABI_CDECL 0x00
#define global ABI_STDCALL 0x01
#define global callback_create(%1,%2,%3,%4) tmp = %2 : _callback_create %1, tmp, %3, %4
#deffunc _callback_create array func, var lab, int _nargs, int _abi
assert _nargs >= 0
dim func, 12
plab = 0
memcpy plab, lab, 4
memcpy func, code_, 48
if (_abi == ABI_STDCALL) {
wpoke func, $2b, (_nargs * 4) & 0xffffff
}
lpoke func, $11, _nargs
lpoke func, $1b, plab
return
#define global winapi_callback(%1,%2,%3) %tclbk_var callback_create %i, %2, %3, ABI_STDCALL : %1 = varptr(%o)
#define global c_callback(%1,%2,%3=-1) %tclbk_var callback_create %i, %2, %3, ABI_CDECL : %1 = varptr(%o)
#define global callback_arg_num wparam
#deffunc callback_args array newargs, local args
if wparam > 0 {
dim args, wparam
dim newargs, wparam
dupptr args, lparam, wparam * 4
memcpy newargs, args, wparam * 4
}
return wparam
#defcfunc callback_nth_arg int _n, local arg
dupptr arg, lparam + _n * 4, 4
return arg
#global
// callback_init
//-------------------------------------------------------------
// コールバックモジュールを初期化する
// このモジュールのほかの関数より前に一度呼び出す必要がある
// callback_create func, lab, nargs, abi
// func: コールバック関数が代入される変数
// lab: コールバックラベル
// nargs: 引数の数
// abi: 呼び出し規約
//----------------------------------------------------------------------------------------------
// コールバック関数を作成
// コールバックラベル(lab)を呼び出す関数を動的に作成し、funcに代入する
//
// nargsには0以上の数値を指定する必要がある
// コールバックラベルでは、callback_args, callback_nth_arg() によって引数を取得できる
// winapi_callback(pfunc, lab, nargs)
// pfunc: コールバック関数へのポインタが代入される変数
// lab: コールバックラベル
// nargs: 引数の数
//---------------------------------------------------------------------------------------------------
// コールバックラベル(lab)を呼び出す関数を動的に作成し、その関数へのポインタをpfuncに代入する
// コールバック関数の呼び出し規約は stdcall
//
// nargsには0以上の数値を指定する必要がある
// コールバックラベルでは、callback_args, callback_nth_arg() によって引数を取得できる
// c_callback(pfunc, lab, nargs)
// pfunc: コールバック関数へのポインタが代入される変数
// lab: コールバックラベル
// nargs: 引数の数
//---------------------------------------------------------------------------------------------------
// コールバックラベル(lab)を呼び出す関数を動的に作成し、その関数へのポインタをpfuncに代入する
// コールバック関数の呼び出し規約は cdecl
//
// nargsには0以上の数値を指定する必要がある
// コールバックラベルでは、callback_args, callback_nth_arg() によって引数を取得できる
// callback_arg_num
//---------------------------------------------------------------------------------------------------
// コールバック作成時に指定された引数の数を返す
// callback_args args
// args: 引数が代入される配列
//-------------------------------------------------------------------
// 引数をすべて取得
// 戻り値は、引数の数
// callback_nth_arg(n)
// n: 引数の序数
//-----------------------------------------------------------------------
// n番目の引数を取得
//サンプル
#if 0
callback_init //モジュール初期化
#include "user32.as"
winapi_callback lpEnumProc, *EnumWindowsProc, 2
EnumWindows lpEnumProc, 128
stop
*EnumWindowsProc
h = callback_nth_arg(0)
lp = callback_nth_arg(1)
sdim text, 256
GetWindowText h, varptr(text), 256
if text != "" {
mes text
i++
}
if i >= 20 {
return 0
}
return 1
#endif
| |
|
2017/8/3(Thu) 16:37:42|NO.80810
> よく考えたら可変長引数のコールバック関数なんて需要ない気がしてきたので、固定長のみの
> バージョンも置いておきます
ここの掲示板で、時々「コールバック・モジュール」が公開されますね。
いろいろと勉強になるのですが、それぞれ微妙に違います。
使うときに「何がどう違うの?」と迷いそうです。
今回のモジュールでは、固定長のみのバージョン(No.80803)を使ってみたいですね。
理由は、単純にソースコードが短いので。
こちらが「コールバック・モジュール」に関心があるのは、
ボタンなどのコントロールのサブクラス化や、プロシージャ関数などが
HSP で使えるなら使ってみたいという感じです。
または、サンプルにあるような EnumWindows 関数で
ちょっとしたツールを作るときに便利ですから。
これ以外でコールバックは必要ないでしょう。
> ご意見ご感想、お待ちしております。
callback_init命令のバイナリ部分のCソース・コードなどもしりたいですね。
改良するなり、参考にするなり、C言語のソース・アルゴリズムがないとできないので。
公開できるのであれば、お願いしたいです。
|
|
2017/8/3(Thu) 19:32:27|NO.80812
レス、ありがとうございます。
>callback_init命令のバイナリ部分のCソース・コードなどもしりたいですね。
実は、コードを短くするためにバイナリ部分はすべてアセンブリで書いてあります。
それでも良ければ。
|
|
2017/8/3(Thu) 19:48:39|NO.80813
> 実は、コードを短くするためにバイナリ部分はすべてアセンブリで書いてあります。
へぇ〜。アセンブラか。懐かしいですね。
> それでも良ければ。
アセンブラでもOKですよ。
少なくも私はね。
|
|
2017/8/3(Thu) 20:10:22|NO.80814
はやっ
push ebp
mov ebp, esp
lea eax, DWORD PTR [ebp + 8]
mov DWORD PTR [0x11111111], eax ;plparam
mov DWORD PTR [0x11111111], 0x22222222 ;pwparam, nargs
mov ecx, 0x11111111 ;code_call
push 0x11111111 ;label
call ecx
add esp, 4
mov eax, DWORD PTR [0x11111111] ;pstat
leave
ret
ちょっと解説すると、これが呼ばれた時点では引数はebp + 8 + 4nにあるはずなので、ebp + 8を
lparamに代入、引数の個数をwparamに代入して、code_callを呼ぶ、そしてstatの値をeaxに入れて
返している感じです。
書いていませんでしたが、これを作るにあたり、tds12さんのmodclbkがとても参考になりました。
tds12さん、ありがとうございます。
|
|
2017/8/4(Fri) 23:51:05|NO.80830
アセンブラのソース公開、ありがとうございます。
> 書いていませんでしたが、これを作るにあたり、tds12さんのmodclbkがとても参考になりました。
> tds12さん、ありがとうございます。
同感。
コールバック・モジュールをモジュール型変数に対応したら便利になるかもね。
モジュール型変数ならば、インスタンスが生成された瞬間に自動的に初期化処理を実行します。
つまり、callback_init 命令を呼び出す必要がないので便利になるね。
|
|
2017/8/5(Sat) 08:56:42|NO.80831
うーん、でも効率という面でいえばやはりコールバック関数ごとに初期化ルーチンを呼び出すと
いうのは…(一応内部でWinAPIを呼んでいるわけですし)
私としての考えはモジュールファイルの末尾にでも書いておいて、includeした時点で初期化される
ような感じでいいのかなと。
もっとも、このモジュールには必要最小限のものしか入れてないので、これを疑似クラスでラップ
するなり、機械語だけ取り出してあとは全部書き換えるなり、命令名・コーディングスタイルが
気に入らないからそこだけ書き換えて公開するなり、好きにしてください。
|
|