UWSCのスレッドに割り込む

スレッドとCPUの関連でスレッドの割り込みを検討したので、実証コードを書いてみた。
スレッドとCPUの関連について - じゅんじゅんのきまぐれ



実証スクリプト

UWSCのスレッドは、1秒毎にログに時間を出力するだけ。
ところが、MSGBOXにOKすると、Win32APIのMessageBoxが出力され、その間スレッドが止まる。

OPTION EXPLICIT

CALL Asm

TEXTBLOCK _INTERRUPT
	VYvsgewYAwAAi0UIiUX4x0XM/////4N9+AB0EYtN+IM5AHQJi1X4g3oEAHUIi0XM
	6dECAADHRdRrZXJux0XYZWwzMsdF3AAAAADHReAAAAAAx0XkAAAAAI1F1FCLTfiL
	Ef/SiUXEx0XURnJlZcdF2ExpYnLHRdxhcnkAjUXUUItNxFGLVfiLQgT/0IlFyMdF
	1E9wZW7HRdhUaHJlx0XcYWQAAI1N1FGLVcRSi0X4i0gE/9GJRfTHRdRDbG9zx0XY
	ZUhhbsdF3GRsZQCNVdRSi0XEUItN+ItRBP/SiUX8x0XUU3VzcMdF2GVuZFTHRdxo
	cmVhx0XgZAAAAI1F1FCLTcRRi1X4i0IE/9CJRfDHRdRSZXN1x0XYbWVUaMdF3HJl
	YWTHReAAAAAAjU3UUYtVxFKLRfiLSAT/0YlF6MdF1EdldFTHRdhocmVhx0XcZENv
	bsdF4HRleHTHReQAAAAAjVXUUotFxFCLTfiLUQT/0olF7MdF1FNldFSNRdRQi03E
	UYtV+ItCBP/QiUXQg330AA+EWgEAAIN9/AAPhFABAACDffAAD4RGAQAAg33oAA+E
	PAEAAIN97AAPhDIBAACDfdAAD4QoAQAAi034i1EIUmoAahr/VfSJRcCDfcAAD4QN
	AQAAi0XAUP9V8IlFzIN9zP8PhPIAAADHhej8//8BAAEAjY3o/P//UYtVwFL/VeyF
	wA+EtQAAAIuFrP3//4PoBImFrP3//4uNrP3//4uVkP3//4kRi4Ws/f//g+gEiYWs
	/f//i42s/f//i5WU/f//iRGLhaz9//+D6ASJhaz9//+Ljaz9//+LlZj9//+JEYuF
	rP3//4PoBImFrP3//4uNrP3//4tV+IkRi4Ws/f//g+gEiYWs/f//i42s/f//i5Wg
	/f//iRGLRfiLSAyJjaD9//+Nlej8//9Si0XAUP9V0IXAdQfHRcz9////6wfHRcz+
	////i03AUf9V6IlFvIN9zAB8BotVvIlVzItFwFD/VfyDfcgAdA2DfcQAdAeLTcRR
	/1XIi0XMi+VdwgQA
ENDTEXTBLOCK
TEXTBLOCK _INS_FUNC
	VYvsg+wcx0Xwa2VybsdF9GVsMzLHRfgAAAAAjUXwUItNCIsR/9KJRejHRfBGcmVl
	x0X0TGlicsdF+GFyeQCNRfBQi03oUYtVCItCBP/QiUXsx0XwdXNlcsdF9DMyAACN
	TfBRi1UIiwL/0IlF/MdF8E1lc3PHRfRhZ2VCx0X4b3hBAI1N8FGLVfxSi0UIi0gE
	/9GJReSDfeQAdB3HRfBTdG9wx0X0AAAAAGoAjVXwUo1F8FBqAP9V5IN97AB0DotN
	/FH/VeyLVehS/1Xsi00Qi1UUi0UMi+VdwhAA
ENDTEXTBLOCK

DIM funcIns = Asm.Set(_INS_FUNC)
DIM funcInt = Asm.Set(_INTERRUPT)
DIM paraSize = 16, para = Asm.Alloc(paraSize), i = 0

PUBLIC _threadId = 0
THREAD ProcThread()
// スレッド起動待ち
WHILE _threadId = 0
	SLEEP(0.1)
WEND

IFB funcIns > 0 AND funcInt > 0 AND para > 0 THEN
	i = i + Asm.SetDword(para + i, Asm.GetProcAddress(Asm.hK32, "LoadLibraryA"))
	i = i + Asm.SetDword(para + i, Asm.GetProcAddress(Asm.hK32, "GetProcAddress"))
	i = i + Asm.SetDword(para + i, _threadId)
	i = i + Asm.SetDword(para + i, funcIns)

	WHILE MSGBOX("「OK」するとMessageBox表示", BTN_OK OR BTN_ABORT) = BTN_OK
		Asm.Run(funcInt, para, i)
	WEND

	// スレッド終了待ち
	_threadId = -1
	WHILE _threadId = -1
		SLEEP(0.1)
	WEND
	// 解放
	Asm.Free(para)
	Asm.Free(funcInt)
	Asm.Free(funcIns)
ENDIF


PROCEDURE ProcThread()
	DEF_DLL GetCurrentThreadId(): DWORD: kernel32
	_threadId = GetCurrentThreadId()
	WHILE _threadId > 0
		GETTIME()
		PRINT G_TIME_HH2 + ":" + G_TIME_NN2 + ":" + G_TIME_SS2
		SLEEP(1)
	WEND
	_threadId = 0	// スレッド終了マーク
FEND

Asmモジュールはこちらから。
UWSCにできないことはない? - じゅんじゅんのきまぐれ
ネイティブコードのBase64エンコード文字列に空白・改行を自由に入れる都合上、
AsmモジュールをCrypt API版に修正しています。


UWSCのProcThread関数には、時間を出力するループしかないことがわかると思います。

解説

_INTERRUPTのコードのポイントは、以下の通り。

  • SuspendしてCONTEXT設定してResumeは前書いた通り
  • 引数を利用しstdcallに従うため、引数右からとEipをEsp領域に積む


_INS_FUNCのコードはこんな感じ。(厳密には違う)

DWORD __stdcall Proc(DWORD oeax, DWORD oecx, DWORD oedx)
{
	MessageBox(NULL, "Stop", "Stop", 0);
	_asm {
		mov ecx, oecx;
		mov edx, oedx;
	}
	return oeax;
}

stdcallでは、eax,ecx,edxは自由に使えるため、元の値を引数として渡す。
関数リターン時にeaxは返り値になるため、元のeaxはリターンに。
ecx,edxはその前に戻してやる。


UWSCのMSGBOX関数にOKした時の流れは以下の通り。

  1. ProcThreadを一時停止する
  2. stdcallを模して、引数をProcThreadのスタックに積む。スレッドのeipも積む
  3. スレッドのeipを、_INS_FUNCの先頭ポインターにする
  4. ProcThreadを再開し、メインスレッドは再度MSGBOX表示になる
  5. 再開したProcThreadはeipが_INS_FUNCの先頭ポインターなので、_INS_FUNCを実行
  6. メッセージボックスを出し、OK待ちする
  7. OKが押されたら、ecx,edxを元に戻す
  8. 元のeaxをリターンすることで、eaxも元に戻す
  9. リターン先は元のeipなので、UWSCのProcThread関数が再開される


ProcThread関数はどこで一時停止されるかわかりません。
GETTIME中かもしれないし、PRINT中かもしれない。
はたまた、割り込みしてMessageBoxを表示している最中かもしれない。
割り込んだ場合は、厳密にそこに戻る必要があるのです。


何回割り込めるか、、、?
スタックを消費しきるまでで、割り込み関数は64Byteくらい消費するかな。
UWSCのスレッドスタックサイズがどれくらいか知らないけど、1MBなら16384回くらい呼べる。
いや、UWSCが使っている分があるからもっと少ないか。
でも、限界に挑戦する気にはならないね、、、。
初期ページサイズ4KB=64回はクリアしてくれました(100回ほど割り込んでみた)

備考

「これさー、すごいのはわかったけど、何に使うの?」
その質問は無意味です。
登山家に何故山に登るの?と聞くようなもの。