UWSCでCreateWindowする

ついにやったー!!!
できましたよ。CreateWindow。
ま、Pro版のフォームと比べるといろいろ難儀なので、公開します。



サンプルスクリプト

OPTION EXPLICIT

CALL Asm

DIM    code = "VYvsg+wUx0X8EREREcdF9AAAAACLRfwzyYN4CAAPlcGJTfjHRfABAAAAi1X8g3oQ/3QPi0X8"
code = code + "i0gQO00ID4ULAQAAi1X8g3oY/3QPi0X8i0gYO00QD4XzAAAAi1X8g3oc/3QPi0X8i0gcO00U"
code = code + "D4XbAAAAi1X8g3oUAHQ+x0XwAAAAAMdF7AAAAADrCYtF7IPAAYlF7ItN/ItV7DtRFHMai0Xs"
code = code + "i038i1SBQDtVDHUJx0XwAQAAAOsC69KDffAAD4SKAAAAi0X8i00IiUgoi1X8i0UMiUIsi038"
code = code + "i1UQiVEwi0X8i00UiUg0i1X8gzoAdBeLRfyDeAwAdA6LTfyLUQxSi0X8iwj/0YtV/IN6BAB0"
code = code + "PotF/IN4JAB0NYtN/ItRIFKLRfyLSCRRi1X8i0IE/9CFwHUbi038i1E8iVX0i0X8M8mDeDgA"
code = code + "D5TBI034iU34g334AHQbi1UUUotFEFCLTQxRi1UIUotF/ItICP/RiUX0i0X0i+VdwhAA"

DIM addr = Asm.Set(code)
DIM paraSize = 76, para = Asm.Alloc(paraSize), i = 0

IFB addr > 0 AND para > 0 THEN
	DEF_DLL CreateEventA(DWORD, BOOL, BOOL, DWORD): DWORD: kernel32
	DIM hEvent = CreateEventA(0, FALSE, FALSE, 0)
	DIM hEventSync = CreateEventA(0, FALSE, FALSE, 0)
	i = i + Asm.SetDword(para + i, Asm.GetProcAddress(Asm.hK32, "SetEvent"))
	i = i + Asm.SetDword(para + i, Asm.GetProcAddress(Asm.hK32, "WaitForSingleObject"))
	i = i + Asm.SetDword(para + i, Asm.GetProcAddress(Asm.hU32, "DefWindowProcA"))
	i = i + Asm.SetDword(para + i, hEvent)	// メッセージ通知イベント
	i = i + Asm.SetDword(para + i, -1)	// hwnd
	i = i + Asm.SetDword(para + i, 3)	// msgNum
	i = i + Asm.SetDword(para + i, -1)	// wp
	i = i + Asm.SetDword(para + i, -1)	// lp
	i = i + Asm.SetDword(para + i, $FFFFFFFF)	// UWSCスレッドの処理待ち時間
	i = i + Asm.SetDword(para + i, 0)	// リザーブ
	i = i + 16	// 取得メッセージ領域
	i = i + 8	// メッセージ処理後領域
	i = i + Asm.SetDword(para + i, 2)	// msg WM_DESTROY
	i = i + Asm.SetDword(para + i, 5)	// msg WM_SIZE
	i = i + Asm.SetDword(para + i, $111)	// msg WM_COMMAND
	//Asm.Dump(para, paraSize)
	Asm.SetDword(addr + 9, para)

	// クラスを登録する
	DEF_DLL RegisterClassExA({DWORD,DWORD,DWORD,int,int,DWORD,DWORD,DWORD,DWORD,DWORD,var string,DWORD}): DWORD: user32
	DEF_DLL GetModuleHandleA(string): DWORD: kernel32
	DEF_DLL GetStockObject(int): DWORD: gdi32
	DIM className = "JuneUwscClass"
	IFB RegisterClassExA(48,3,addr,0,0,GetModuleHandleA(NULL),0,0,GetStockObject(0),0,className,0) = 0 THEN
		PRINT "RegisterClassEx Error:" + Asm.GetLastError()
		EXITEXIT
	ENDIF

	// ウインドウを作成する
	DEF_DLL CreateWindowExA(DWORD,string,string,DWORD,int,int,int,int,DWORD,DWORD,DWORD,DWORD): DWORD: user32
	DIM hWnd = CreateWindowExA(0,className,className,$CF0000,100,100,200,100,0,0,0,0)
	IFB hWnd = 0 THEN
		PRINT "CreateWindowEx Error:" + Asm.GetLastError()
		EXITEXIT
	ENDIF
	// おまけでボタンをつける
	DIM styleBtn = $40000000 OR $10000000 OR 1	// WS_CHILD OR WS_VISIBLE OR BS_DEFPUSHBUTTON
	DIM hWndBtn = CreateWindowExA(0,"BUTTON","OK",styleBtn,10,10,50,30,hWnd,$123,0,0)	// ID:$123
	// 作り終わったら表示する
	CTRLWIN(HNDTOID(hWnd), SHOW)

	// UWSC側メッセージ処理スレッドを起動する
	DIM hEventFin = CreateEventA(0, FALSE, FALSE, 0)
	THREAD ProcMessage(hEvent, hEventSync, para + 36, hEventFin)

	// メッセージ処理スレッドの終了を待つ
	i = 258
	WHILE i = 258
		i = Asm.WaitForSingleObject(hEventFin, 100)
	WEND
	SLEEP(0.1)

	// 解放
	Asm.Free(addr)
	Asm.Free(para)
	Asm.CloseHandle(hEvent)
	Asm.CloseHandle(hEventSync)
	Asm.CloseHandle(hEventFin)
ENDIF

PROCEDURE ProcMessage(hEvent, hEventSync, p, hEventFin)
	DEF_DLL PostMessageA(DWORD,DWORD,DWORD,DWORD): bool: user32
	DEF_DLL SetEvent(DWORD): bool: kernel32
	DIM hwnd = 0, msg = 0, wp, lp, res = 258
	PRINT "ProcMessage start"
	WHILE res = 258
		res = Asm.WaitForSingleObject(hEvent, 100)
		IFB res = 0 THEN
			hwnd = Asm.GetDword(p + 4)
			msg = Asm.GetDword(p + 8)
			wp = Asm.GetDword(p + 12)
			lp = Asm.GetDword(p + 16)
			PRINT "hwnd:" + FORMAT(hwnd,8,-1) + " msg:" + FORMAT(msg,8,-1) + " wp:" + FORMAT(wp,8,-1) + " lp:" + FORMAT(lp,8,-1)
		ELSEIF res = 258 THEN
			// timeout
			CONTINUE
		ELSE
			msg = 2	// 異常事態なのでWM_DESTROYを模して終わる
			PRINT "ProcMessage Wait fail:" + res
		ENDIF
		res = 258
		// メッセージ処理
		IFB msg = 2 THEN	// WM_DESTROY
			Asm.SetDword(p, 0)
			IF hwnd THEN PostMessageA(hwnd, $12, 0, 0)	// WM_QUIT
			res = 0
			PRINT "ProcMessage WM_DESTROY"
		ELSEIF msg = 5 THEN	// WM_SIZE
			PRINT "ProcMessage WM_SIZE"
		ELSEIF msg = $111 THEN	// WM_COMMAND
			DIM notify = INT(wp / $10000), idBtn = wp MOD $10000
			// lp はボタンのHWND
			IFB notify = 0 THEN	// BN_CLICKED
				PRINT "ProcMessage BTN " + FORMAT(idBtn,4,-1)
				MSGBOX("Pushed!")
			ENDIF
		ENDIF
	WEND
	SetEvent(hEventFin)
	PRINT "ProcMessage End"
FEND

Asmモジュールは、
UWSCにできないことはない? - じゅんじゅんのきまぐれ
のもの。(リンク先をGetDword追加版に修正済み)

動作原理

  1. RegisterClassEx用にAsmモジュールで関数ポインターを作成する
  2. RegisterClassExする
  3. 登録したクラスを使い、Windowを作成する
  4. Windowの作成が終わったら、表示する
  5. UWSCのメッセージ処理用スレッドを起動する
  6. ウインドウが閉じる(UWSCのスレッド終了)を待つ
  7. 関数ポインターやイベントを解放する

この順番、超重要。


なお、Asmモジュールで作成する関数ポインターは、WndProc相当で

  1. 指定されたメッセージが来ると、共有メモリーに内容を書き、メッセージ通知イベントを発行する
  2. DefWindowProcを呼び出す

ということをしているだけ。(メッセージの指定については後述)
UWSCのスレッドは長時間の待ちに入ると、落ちるようです。
WaitForSingleObjectの使い方に注意が必要です。
また、処理するメッセージが大量(短時間でもたくさん)になっても落ちるようです。

メッセージ指定方法

この部分になります。

DIM paraSize = 76, para = Asm.Alloc(paraSize), i = 0
...
	i = i + Asm.SetDword(para + i, -1)	// hwnd
	i = i + Asm.SetDword(para + i, 3)	// msgNum
	i = i + Asm.SetDword(para + i, -1)	// wp
	i = i + Asm.SetDword(para + i, -1)	// lp
...
	i = i + Asm.SetDword(para + i, 2)	// msg WM_DESTROY
	i = i + Asm.SetDword(para + i, 5)	// msg WM_SIZE
	i = i + Asm.SetDword(para + i, $111)	// msg WM_COMMAND

paraSizeは、指定メッセージが3つなら76Byte。1つなら68Byteです。
hwnd/wp/lpの指定は、一つしか利かないのであまり使えない。(-1は全て)
msgNumで何個指定するかを設定し、最後に列挙します。


例えば、WM_PAINTを増やすなら、以下のようにします。

DIM paraSize = 80, para = Asm.Alloc(paraSize), i = 0
...
	i = i + Asm.SetDword(para + i, -1)	// hwnd
	i = i + Asm.SetDword(para + i, 4)	// msgNum
	i = i + Asm.SetDword(para + i, -1)	// wp
	i = i + Asm.SetDword(para + i, -1)	// lp
...
	i = i + Asm.SetDword(para + i, 2)	// msg WM_DESTROY
	i = i + Asm.SetDword(para + i, 5)	// msg WM_SIZE
	i = i + Asm.SetDword(para + i, $111)	// msg WM_COMMAND
	i = i + Asm.SetDword(para + i, 15)	// msg WM_PAINT


メッセージ処理側(ProcMessage)は見ればわかると思います。
WM_DESTROYの処理は一般的なもの。
なくすと正常に終われなくなります。
WM_メッセージの詳細は、msdn等を検索願います。
コントロールの作り方も検索してくださいね〜。


UWSCだけでウインドウが完全に作れるようになるとはっ!!!
任意のDLLが呼べるというのは、恐ろしいほど強力なことなのですね。
もう「UWSCはコールバックが処理できない」とは言わせません!
(過去に自分でそう回答した気がしますが、、、)
でも、面倒なのは変わりませんが、、、。