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追加版に修正済み)
動作原理
- RegisterClassEx用にAsmモジュールで関数ポインターを作成する
- RegisterClassExする
- 登録したクラスを使い、Windowを作成する
- Windowの作成が終わったら、表示する
- UWSCのメッセージ処理用スレッドを起動する
- ウインドウが閉じる(UWSCのスレッド終了)を待つ
- 関数ポインターやイベントを解放する
この順番、超重要。
なお、Asmモジュールで作成する関数ポインターは、WndProc相当で
- 指定されたメッセージが来ると、共有メモリーに内容を書き、メッセージ通知イベントを発行する
- 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はコールバックが処理できない」とは言わせません!
(過去に自分でそう回答した気がしますが、、、)
でも、面倒なのは変わりませんが、、、。