前回
コマンドラインからのゴースト操作のために、C#のTCPでSSTPを送信するプログラムを書きました。
C#で伺か(SSP)にSSTP通信する - Qiita
C#で伺か(SSP)にSSTP通信してエミリをつついてシルフィアをなでる - Qiita
しかしながら、いちいちソケット通信をはさむ必要からか反応速度が遅く、体験が悪かったです。
そこで、より高速が望めそうなプロトコル間通信としてDirect SSTPで書き直すことを考えました。
C#は諦めた
C#かつDirect SSTPといえば前述した記事にもある
SSTPLib
が最有力です。
しかしながら、そもそもこのコードのリーディングを諦めてTCPに逃げた都合上、メモ帳でソースを読むだけでは何をしているのかさっぱりわかりませんでした。
FMOを取得するあたりまではなんとかわかったのですが、そこからバイト列を操作したりなんなりで、動く実物がないことには理解が限界でした。
高度にオブジェクト化されていることもメモ帳リーディングでの妨げになっていましたし…
HSP版が読みやすい
そこでなんとかDirect SSTPの情報をネットからあさっていると
HSPでDirectSSTPを送信する | すくりや
HSPでDirectSSTPの送信結果を表示する | すくりや
を見つけました。
これのいいところは実物のコードと単体で動作するexeファイルが同梱されていることです。
しかもウィンドウ関連を入れても総行数200未満と大変読みやすそう。
ということで、サンプルをコピペしながら動作を理解することにしました。
HSPの開発環境を作る
ただ、HSPのプログラミング環境を作る必要が私の勝手なラインの
「プログラミング言語をインストールしない」
に抵触するかが謎でした。
C#で言うとVisual StudioはNG。
csc.exeはセーフ。
という謎ライン。
HSPはどうかというと、
インストーラーを起動するか、または任意のディレクトリにhsp34フォルダを解凍する ことで使用できるようになります。(推奨ディレクトリは、「c:\hsp34」です)
...
5. アンインストール
フォルダをまとめて削除することでアンインストールすることができます。
標準のスクリプトエディタは、標準で以下のレジストリに設定を保存します。必要な場合は、こちらの削除も行なってください。
...
HKEY_CURRENT_USER\Software\OnionSoftware\hsed3_3
とかなりポータブルで許容範囲内かなという感じでした。
また、極々簡単なコードの場合は、HSP製のサードパーティのCLI用コンパイラなどを取得すれば、HSP本体は不要でexeの作成までできるので、かなり依存性が少ないと感じました。
(実際にはWin32を扱うための機能だったりが必要だったためHSP本体をダウンロードし、必要なライブラリ(.as
ファイル)のみを作成プログラムの同一ディレクトリにコピーしincludeを通しました。
なのでhsp351\common
を必要としただけで、
他のファイルや標準のエディタなどは一切起動しませんでした)
HSPのコンパイラはいろいろあるようですが、今回は更新日時が近かった
hspc - SoupSeed
を利用しました。
独立ファイル群を用意するだけでメモ帳開発ができるHSPはそこそこ手軽かなという印象です。
サンプルを動かす
まずはコードで明示的にincludeされているモジュールを集めます。
kernel32.as
mod_regexp.as
user32.as
これらはHSP本体に付属しているのでダウンロードしてcommon
ディレクトリからコピーすればOKです。
役割はWindowsのプロセス間通信と正規表現のためのモジュールであることは名前から連想できると思います。
マクロを読み込む
コードでincludeしている3つのモジュールを読み込んだだけではまだエラーがでます。
何の変哲もない以下のコード
for i, 1, length(sharedMemVal), 1
; hogehoge
next
がエラーになります。
HSPにはなんとfor
構文がないようです。
あるのはrepeat
などで、上記を書き換えると
repeat length(sharedMemVal) - 1
i = cnt + 1
loop
このようになります。
ではfor
はなにかというと、
HSP3 プログラミング・マニュアル#QUICK_START
繰り返しの記述は通常、repeat~loop命令で行ないます。これは、C言語のfor、while、doを 簡略化したものと捉えることができます。 この他にも、while~wend、do~until、for~next、switch~caseなどC言語ライクなマクロ命令が用意されています。 詳しくは、「標準マクロ定義ファイル」を参照してください。
ということでマクロとして用意されており、その定義ファイルを読み込まないと動作しないわけです。
HSP3 プログラミング・マニュアル#HSPDEF
から、ファイルはhspdef.as
であり、明示的にinclude
しなくとも直下ディレクトリを探索されることがわかるので、これも設置しましょう。
前述のようにサンプルコードのfor
をrepeat
にすることも検討したのですが、それを解決したところ依存するmod_regexp.as
でもfor
が使われていることが判明したため、素直にマクロを読み込みました。
バッチ化に向けて削る
サンプルが動けばあとはウィンドウ関連のコードなどを削っていけばいいと思います。
ウィンドウを完璧に消すには
#packopt hide 1
を追加してコンパイルでした。
ただプリントデバッグには便利なので最後に付けるといいかもしれません。
ここで、参考コードのgetSakuraFMO
の返り値を見てみます。
ウインドウハンドル取得方法
filemapping object "Sakura" からデータを取得することで確実にウインドウハンドルを取得できます。メモリオブジェクトの文書を参照して下さい。
内容は「メモリオブジェクトの文書」と同一なのですが、
#defcfunc getSakuraFMO
MAP_OBJ_NAME_SAKURA = "Sakura"
sharedMemSize = 1024 * 64
OpenFileMapping FILE_MAP_READ,FALSE,MAP_OBJ_NAME_SAKURA
hMapObjSakura = stat
if (hMapObjSakura==0) {
return ""
}
はまだわかりますが
その後の
MapViewOfFile hMapObjSakura,FILE_MAP_READ,0,0,sharedMemSize
sharedMemPtr = stat
dupptr sharedMemVal, sharedMemPtr, sharedMemSize
sfmo = ""
for i, 1, length(sharedMemVal), 1
l = sharedMemVal(i)
b1 = (l & 0x000000FF) >> 0
b2 = (l & 0x0000FF00) >> 8
b3 = (l & 0x00FF0000) >> 16
b4 = (l & 0xFF000000) >> 24
sfmo += strf("%c%c%c%c", b1, b2, b3, b4)
next
の仕組みがわかっていない状態ですね…
ともかく出力を見ると、私の場合は
こんな感じです。
Direct SSTPは
「ウインドウメッセージを伺かのプロセスにSSTPを送ればいいよ」
と読めますが、実際には
-
Sakura
のファイルマッピングオブジェクトを読み込む? - 読み込んだファイルマッピングオブジェクトをマップビューとして開く?
- マップビューの内容をバイト列操作をしながらstringに書き出す
- 書き出した中に格納されているhwnd(ウィンドウハンドル)の数値を抜き出す
- hwndの数値に対してメッセージを
SendMessage
する
という手順を踏むようです。
そしてそのあたりに伺かとしてのサポートはないので単純なC、というかWin32API周りの知識を身につける必要がありそうです。
遠回りしましたが、そんなこんなでシェイプアップしたsample.hsp
がこちらです(開発中)
;#packopt hide 1
; テスト
#include "dsstphsp_1_2_0/dsstp.hsp"
#define WM_COPYDATA 0x004A
#include "mod_regexp.as"
#module
; "hwnd"とか指定するとhwndの配列が返ってくる
#deffunc getParamsFromSakuraFMO str param, str sfmo, array ret
re = "\\." + param + strf("%c", 1) + "([^\\r\\n]+)"
matches result, sfmo, re, 0, 0, 0
i = 0
repeat stat
split result(cnt), strf("%c", 1), r
ret(i) = r(1)
i += 1
loop
return
#global
oncmd gosub *OnCopyData, WM_COPYDATA
sdim msg, 4096
msg = {"
NOTIFY SSTP/1.1
Charset: Shift_JIS
Sender: HSP
Event: OnDive
Reference0: 道頓堀
Script: \\h\\s[5]どぼ〜ん。\\w9\\w9\\u\\s[11]…\\w5…\\w5…\\w5…\\w5…。\\e
Option: notranslate
HWnd: 0"}
sdim s, 4096
poke s, 0, msg
sfmo = getSakuraFMO()
mes sfmo
getParamsFromSakuraFMO "hwnd", sfmo, hwnds
hwndTarget = int(hwnds(0))
sendDSSTP hwndTarget, s
mes stat
wait 1000
end
*OnCopyData
dupptr cds, lParam, 12
data_id = cds(0)
data_size = cds(1)
dupptr data_ref, cds(2), data_size
if data_id == 9801 {
sdim data, data_size/4
memcpy data, data_ref, data_size
mes data
}
dim cds, 1
dim data_ref, 1
end
ゴーストを複数立てないならhwndlsitではなく伺か本体のhwndでよく、任意のフィールドを引っ張るgetParamsFromSakuraFMO
ではなくhwnd狙い撃ちの抜き出し関数にすればもっとスリムになりそうですね。
あとはこれは引数駆動にしてメッセージを変動させれば、コマンド用DirectSSTPの完成ですね。