UWSCでバックグラウンドウインドウの表示を取得する

追記 20120528

uwsc4.8にて、chkimg,peekcolorがバックグラウンドに対応したため本記事はもはやゴミです(^^;;;




UWSC」「バックグラウンド」で検索する人が多いようなので、ヒント第二弾。
第一弾は、
アプリケーションをバックグラウンド制御する - じゅんじゅんのきまぐれ
ですかね。
今度は、バックグラウンドウインドウの表示を取得する。
(限界はあるけど)



主旨

CHKIMGを行うのが主眼ではない。
(CHKIMG相当のことをスクリプトでやるのはパフォーマンス的にきつい)
SAVEIMGでも、バックグラウンドオプションがあるが、それだとファイル経由。
それをメモリー上で行うことを目標とした。

スクリプト

WindowImage.uws

OPTION EXPLICIT

IFB GET_UWSC_NAME = "WindowImage.uws" THEN
	DIM path = "test.bmp"
	MSGBOX("Desktop: " + path + " " + WindowImage.Save(path))
	MSGBOX("Window: " + path + " " + WindowImage.Save(path, IDTOHND(GETID(GET_FROMPOINT_WIN))))
	MSGBOX("Window(client): " + path + " " + WindowImage.Save(path, IDTOHND(GETID(GET_FROMPOINT_WIN)),0,0,-1,-1,1))
ENDIF

MODULE WindowImage
	DEF_DLL GetDC(hwnd): uint: user32
	DEF_DLL CreateCompatibleBitmap(uint, int, int): uint: gdi32
	DEF_DLL CreateCompatibleDC(uint): uint: gdi32
	DEF_DLL SelectObject(uint, uint): uint: gdi32
	DEF_DLL GetDeviceCaps(uint, int): int: gdi32
	DEF_DLL ReleaseDC(hwnd, uint): int: user32
	DEF_DLL BitBlt(uint, int, int, int, int, uint, int, int, uint): bool: gdi32
	DEF_DLL GetDIBits(uint, uint, uint, uint, var byte[], var byte[], uint): int: gdi32
	DEF_DLL PrintWindow(hwnd, uint, uint): bool: user32
	DEF_DLL DeleteObject(uint): bool: gdi32

	DEF_DLL CreateFileW(wstring, dword, dword, dword, dword, dword, dword): dword: kernel32
	DEF_DLL WriteFile(dword, byte[], dword, var dword, dword): bool: kernel32
	DEF_DLL CloseHandle(dword): bool: kernel32


	PROCEDURE Log(msg)
		PRINT msg
	FEND

	FUNCTION GetBitCount(hDc)
		RESULT = GetDeviceCaps(hDc, 12)
		// 32/16bitは24bit扱いにする
		IF RESULT = 32 OR RESULT = 16 THEN RESULT = 24
		// 8bitは未対応
		IF RESULT = 8 THEN Log("Unsupported bit count.")
	FEND

	FUNCTION CalcLength(width, bitCount)
		RESULT = width * (bitCount / 8)
		IF RESULT Mod 4 <> 0 THEN RESULT = RESULT + 4 - (RESULT Mod 4)
	FEND

	PROCEDURE SetInt(var buf[], offset, data)
		DIM i
		FOR i = 0 TO 3
			buf[offset + i] = data Mod 256
			data = INT(data / 256)
		NEXT
	FEND

	FUNCTION PrivateGet(hwnd, x, y, w, h, var bitCount, var lineLen, var info[], var data[], cl)
		DIM hDc = GetDC(hwnd), i
		bitCount = GetBitCount(hDc)
		lineLen = CalcLength(w, bitCount)
		RESULT = (RESIZE(info, 39) = 39)
		IF RESULT THEN RESULT = (RESIZE(data, lineLen * h - 1) = lineLen * h - 1)

		// BITMAPINFOHEADER
		SetInt(info, 0, LENGTH(info))	// biSize
		SetInt(info, 4, w)				// width
		SetInt(info, 8, h)				// height
		SetInt(info, 12, 1)				// biPlanes = 1固定
		SetInt(info, 14, bitCount)
		FOR i = 16 TO 36 STEP 4
			SetInt(info, i, 0)
		NEXT

		// ビットマップ取得
		DIM hBitShot, hBitBuf, hShotDc = 0, hShotBit = 0, hBufDc = hDc, hBufBit = 0
		IF RESULT AND (hDc <> 0) THEN hShotBit = CreateCompatibleBitmap(hDc, w, h)
		IF hShotBit <> 0 THEN hShotDc = CreateCompatibleDC(hDc)
		DIM x1 = 0, y1 = 0, x2 = x, y2 = y
		IFB x < 0 THEN
			x1 = -x
			x2 = 0
		ENDIF
		IFB y < 0 THEN
			y1 = -y
			y2 = 0
		ENDIF
		IFB hwnd <> 0 THEN
			IF hShotDc <> 0 THEN hBufBit = CreateCompatibleBitmap(hDc, x2 + w, y2 + h)
			IF hBufBit <> 0 THEN hBufDc = CreateCompatibleDC(hDc)
			IFB hBufDc <> 0 THEN
				hBitBuf = SelectObject(hBufDc, hBufBit)
				RESULT = PrintWindow(hwnd, hBufDc, cl)
			ENDIF
		ENDIF
		IFB hShotDc <> 0 THEN
			hBitShot = SelectObject(hShotDc, hShotBit)
			RESULT = BitBlt(hShotDc, x1, y1, w, h, hBufDc, x2, y2, $CC0020)
		ENDIF
		IF RESULT THEN RESULT = (GetDIBits(hShotDc, hShotBit, 0, h, data, info, 0) = h)

		IFB hBufBit <> 0 THEN
			SelectObject(hBufDc, hBitBuf)
			DeleteObject(hBufBit)
		ENDIF
		IF hBufDc <> hDc THEN DeleteObject(hBufDc)
		IFB hShotBit <> 0 THEN
			SelectObject(hShotDc, hBitShot)
			DeleteObject(hShotBit)
		ENDIF
		IF hShotDc <> 0 THEN DeleteObject(hShotDc)
		IF hDc <> 0 THEN ReleaseDC(hwnd, hDc)
	FEND

	FUNCTION Get(var bitCount, var lineLen, var info[], var data[], hwnd=0, x=0, y=0, w=-1, h=-1, cl=0)
		DIM id = HNDTOID(hwnd)
		IFB id = -1 THEN
			IF w = -1 THEN w = G_SCREEN_W
			IF h = -1 THEN h = G_SCREEN_H
		ELSEIF cl = 0 THEN
			IF w = -1 THEN w = STATUS(id, ST_WIDTH)
			IF h = -1 THEN h = STATUS(id, ST_HEIGHT)
		ELSE
			IF w = -1 THEN w = STATUS(id, ST_CLWIDTH)
			IF h = -1 THEN h = STATUS(id, ST_CLHEIGHT)
		ENDIF
		RESULT = PrivateGet(hwnd, x, y, w, h, bitCount, lineLen, info, data, cl)
	FEND

	FUNCTION Write(path, bitCount, lineLen, info[], data[], h=-1)
		DIM head[13]
		DIM headSize = LENGTH(head) + LENGTH(info)
		IF h = -1 THEN h = LENGTH(data) / lineLen
		DIM size = headSize + lineLen * h, written
		// BITMAPFILEHEADER
		SetInt(head, 0, 19778)		// 'BM'
		SetInt(head, 2, size)		// size
		SetInt(head, 6, 0)			// reserve
		SetInt(head, 10, headSize)	// bfOffBits
		// WriteFile
		DIM hFile = CreateFileW(path, $40000000, 0, 0, 2, $80, 0)
		RESULT = hFile > 0
		IFB RESULT THEN
			RESULT = WriteFile(hFile, head, LENGTH(head), written, 0)
			RESULT = WriteFile(hFile, info, LENGTH(info), written, 0)
			RESULT = WriteFile(hFile, data, LENGTH(data), written, 0)
		ENDIF
		IF hFile > 0 THEN CloseHandle(hFile)
	FEND

	FUNCTION Save(path, hwnd=0, x=0, y=0, w=-1, h=-1, cl=0)
		DIM bitCount, lineLen, info[0], data[0]
		RESULT = Get(bitCount, lineLen, info, data, hwnd, x, y, w, h, cl)
		IF RESULT THEN RESULT = Write(path, bitCount, lineLen, info, data)
	FEND

ENDMODULE

ま、興味の範囲なので、いろいろ適当です。
SAVEIMGと同じく、クライアントオプションをつけてますけど、XPとWin7で動作が違ったので、面倒になって適当です。
一応、clを1にすると、クライアントウインドウが対象になる、、、のです。
同様にバグが随所にある気がする、、、けど、あきた。

注意

「月を背に立つ。地面には影。この時、はたして月は存在するのか?」
普通は、存在する、ですよね。
でも、量子力学(の拡大解釈)では、存在しないかもしれないのです。


同じように、後ろに隠れたアプリケーションの画面は、存在しないかもしれません。
Windowsは、後ろに隠れて描画が無駄と判断すると、さぼるのです)
ということで、動作しないこともあるのです。


SAVEIMGの一文が、これほど大規模になってしまう。
いや、これほどのコードをSAVEIMGで簡潔に書ける。
UWSCの良いところです。