HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2017
0801
774作ってみました、コールバックモジュール8解決


774

リンク

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



この記事に返信する


774

リンク

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



774

リンク

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言語のソース・アルゴリズムがないとできないので。
公開できるのであれば、お願いしたいです。



774

リンク

2017/8/3(Thu) 19:32:27|NO.80812

レス、ありがとうございます。

>callback_init命令のバイナリ部分のCソース・コードなどもしりたいですね。
実は、コードを短くするためにバイナリ部分はすべてアセンブリで書いてあります。
それでも良ければ。



科学太郎

リンク

2017/8/3(Thu) 19:48:39|NO.80813

> 実は、コードを短くするためにバイナリ部分はすべてアセンブリで書いてあります。
へぇ〜。アセンブラか。懐かしいですね。

> それでも良ければ。
アセンブラでもOKですよ。
少なくも私はね。



774

リンク

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 命令を呼び出す必要がないので便利になるね。



774

リンク

2017/8/5(Sat) 08:56:42|NO.80831

うーん、でも効率という面でいえばやはりコールバック関数ごとに初期化ルーチンを呼び出すと
いうのは…(一応内部でWinAPIを呼んでいるわけですし)

私としての考えはモジュールファイルの末尾にでも書いておいて、includeした時点で初期化される
ような感じでいいのかなと。

もっとも、このモジュールには必要最小限のものしか入れてないので、これを疑似クラスでラップ
するなり、機械語だけ取り出してあとは全部書き換えるなり、命令名・コーディングスタイルが
気に入らないからそこだけ書き換えて公開するなり、好きにしてください。



ONION software Copyright 1997-2023(c) All rights reserved.