UWSCで通り抜けフープもどきを作ってみる
CreateWindow出来るようになったので、調子に乗って通り抜けフープもどきを作ってみた。
通り抜けフープもどきを使うと、そのウインドウの下が見えます。
、、、でも、CreateWindowなくても多分できるな、これ、、、。
ま、CreateWindowした方が比較的きれいに書ける、という事例。
ただ、環境によっては落ちるかも。
多分、メモリー解放のタイミングだなぁ。
操作方法
スクリプトを実行すると、灰色の輪っかが出ます。
それを、下を覗きたいところに移動するだけ。
輪っかが細いので、マウスでつかむのが面倒かもしれません。
その場合、スクリプトの定数RING_WIDTHを変えると良いかもしれません。
輪っかを右クリックで終了。
スクリプト
OPTION EXPLICIT CONST RING_SIZE = 150 CONST RING_WIDTH = 5 DEF_DLL WindowFromPoint(long,long): DWORD: user32 DEF_DLL GetParent(DWORD): DWORD: user32 DEF_DLL CreateEllipticRgn(int,int,int,int): DWORD: gdi32 DEF_DLL CreateRectRgn(int,int,int,int): DWORD: gdi32 DEF_DLL CombineRgn(DWORD,DWORD,DWORD,int): int: gdi32 DEF_DLL DeleteObject(DWORD): BOOL: gdi32 DEF_DLL SetWindowRgn(HWND,DWORD,BOOL): int: user32 DEF_DLL GetWindowRgn(HWND,DWORD): int: user32 PUBLIC _hWndOld = 0, _hRgnOld = 0 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 = 80, 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, 4) // 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, $201) // msg WM_LBUTTONDOWN i = i + Asm.SetDword(para + i, $205) // msg WM_RBUTTONUP i = i + Asm.SetDword(para + i, $232) // msg WM_EXITSIZEMOVE //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" DIM hInst = GetModuleHandleA(NULL) IFB RegisterClassExA(48,3,addr,0,0,hInst,0,0,GetStockObject(2),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,$80000000,10,10,RING_SIZE,RING_SIZE,0,0,0,0) IFB hWnd = 0 THEN PRINT "CreateWindowEx Error:" + Asm.GetLastError() EXITEXIT ENDIF // ウインドウを丸にする DIM hRgn = CreateEllipticRgn(0, 0, 1, 1) DIM hRgnO = CreateEllipticRgn(0, 0, RING_SIZE, RING_SIZE) DIM hRgnI = CreateEllipticRgn(RING_WIDTH, RING_WIDTH, RING_SIZE-RING_WIDTH, RING_SIZE-RING_WIDTH) CombineRgn(hRgn, hRgnO, hRgnI, 4) // RGN_DIFF DeleteObject(hRgnI) DeleteObject(hRgnO) SetWindowRgn(hWnd, hRgn, FALSE) CTRLWIN(HNDTOID(hWnd), TOPMOST) // 作り終わったら表示する CTRLWIN(HNDTOID(hWnd), SHOW) // UWSC側メッセージ処理スレッドを起動する DIM hEventFin = CreateEventA(0, FALSE, FALSE, 0) THREAD ProcMessage(hEvent, hEventSync, para + 36, hEventFin) // メッセージ処理スレッドの終了を待つ DEF_DLL MoveWindow(DWORD,int,int,int,int,BOOL): BOOL: user32 DIM tarHwnd = 0, tarId, tarX, tarY, newX, newY DIM rId = HNDTOID(hWnd) i = 258 WHILE i = 258 i = Asm.WaitForSingleObject(hEventFin, 10) IFB _hWndOld <> 0 THEN IFB _hWndOld = tarHwnd THEN newX = STATUS(tarId, ST_X) - tarX newY = STATUS(tarId, ST_Y) - tarY IFB newX <> 0 OR newY <> 0 THEN MoveWindow(hWnd, STATUS(rId, ST_X) + newX, STATUS(rId, ST_Y) + newY, RING_SIZE, RING_SIZE, TRUE) tarX = tarX + newX tarY = tarY + newY ENDIF ELSE tarHwnd = _hWndOld tarId = HNDTOID(tarHwnd) tarX = STATUS(tarId, ST_X) tarY = STATUS(tarId, ST_Y) ENDIF ENDIF WEND SLEEP(0.1) // 解放 DEF_DLL UnregisterClassA(string,DWORD): BOOL: user32 IF !UnregisterClassA(className, hInst) THEN SLEEP(1) 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 // 同期イベントを設定する //Asm.SetDword(p, hEventSync) 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 ELSEIF msg = $205 THEN // WM_RBUTTONUP // 右クリックされたら終了 PostMessageA(hwnd, $10, 0, 0) // WM_CLOSE ELSEIF msg = $201 THEN // WM_LBUTTONDOWN // 左クリックされたらタイトルバーがクリックされたことにする PostMessageA(hwnd, $A1, 2, 0) // WM_NCLBUTTONDOWN,HTCAPTION ELSEIF msg = $232 THEN // WM_EXITSIZEMOVE // 移動完了したのならその下のウインドウに穴を空ける DIM wid = HNDTOID(hwnd) Through(STATUS(wid,ST_X), STATUS(wid,ST_Y)) ENDIF //SetEvent(hEventSync) WEND IFB _hwndOld <> 0 THEN SetWindowRgn(_hwndOld, _hRgnOld, TRUE) _hwndOld = 0 ENDIF SetEvent(hEventFin) FEND PROCEDURE Through(x, y) // 輪っか中心あたりのウインドウを取得する DIM hwndTar = WindowFromPoint(x + RING_SIZE / 2, y + RING_SIZE / 2) // 子ウインドウの場合、親をたどる DIM hwndPar = GetParent(hwndTar) WHILE hwndPar <> 0 hwndTar = hwndPar hwndPar = GetParent(hwndTar) WEND // 違うウインドウに穴あけしてた場合、元に戻す IFB _hwndOld <> 0 AND _hwndOld <> hwndTar THEN SetWindowRgn(_hwndOld, _hRgnOld, TRUE) _hwndOld = 0 ENDIF IFB hwndTar <> 0 THEN // 前と違うなら、ウインドウの形を取得する IFB hwndTar <> _hwndOld THEN _hRgnOld = CreateEllipticRgn(0, 0, 1, 1) IFB GetWindowRgn(hwndTar, _hRgnOld) = 0 THEN // 取得失敗はNULLにしておく DeleteObject(_hRgnOld) _hRgnOld = 0 ENDIF ENDIF // 穴あき型を作成する DIM wid = HNDTOID(hwndTar), tarX = STATUS(wid, ST_X), tarY = STATUS(wid, ST_Y), hRgnNew = 0 DIM hRgnR = CreateEllipticRgn(x - tarX, y - tarY, RING_SIZE + x - tarX, RING_SIZE + y - tarY) DIM hRgn = CreateRectRgn(0, 0, 1, 1) // 元の形がNULLの場合、一般的な矩形とみなす DIM hRgnOld = _hRgnOld IF hRgnOld = 0 THEN hRgnOld = CreateRectRgn(0, 0, STATUS(wid, ST_WIDTH), STATUS(wid, ST_HEIGHT)) // 元の形と穴を合成(穴開け)する CombineRgn(hRgn, hRgnOld, hRgnR, 4) DeleteObject(hRgnR) IF hRgnOld <> _hRgnOld THEN DeleteObject(hRgnOld) // 対象を穴あき形にする SetWindowRgn(hWndTar, hRgn, TRUE) _hwndOld = hWndTar ENDIF FEND
Asmモジュールは、こちらから。
UWSCにできないことはない? - じゅんじゅんのきまぐれ
解説
ほとんど、UWSCでCreateWindowする - じゅんじゅんのきまぐれと同じ。
違いは、
- 捕まえるメッセージが、WM_DESTROY/WM_LBUTTONDOWN/WM_RBUTTONUP/WM_EXITSIZEMOVEの四つ
- 作成するウインドウは輪っか型
- メッセージ処理の都合上、非同期処理としている(同期にするとデッドロックする)
このため処理したいメッセージが重複すると拾えないことがある、、、 - Through(x, y)関数の追加
Through(x, y)関数について
引数は、輪っかの左上。
輪っかの中心にあるウインドウを穴あけ対象とする。
前回、別ウインドウで穴あけ処理が行われていたら、穴を戻す。
対象のウインドウの元の形を退避(_hRgnOld)する(未設定は未設定の情報を取得する)
穴用の円リージョン(hRgnR)を作成。
対象の元の形が未設定の場合、対象ウインドウ全面が元の形であるとする。
元の形に穴を抜いた形を作成する(hRgn)。
不要になったリージョンを削除する。
対象のウインドウに作成した形(リージョン)を適用する。
なお、CombineRgnすればそれぞれの元の形が不要です。
ウインドウに適用したリージョンはWindows管理になるため、解放は不要です。
これで、いろいろなウインドウの形が変えられたりします。
UWSCは、万能実行環境ですね。