UWSCでCUI(コンソールウインドウ)を扱う(その3)

UWSCでCUI(コンソールウインドウ)を扱う(その2) - じゅんじゅんのきまぐれ
この、その2から、クリップボード依存を排除しました!


までも、お勧めなのは、stuncloudさんのモジュールですが。
UWSCのコマンドラインインタプリタ | たっぷす庵
優位なのは、補完ができること。もっさりしてるけど。



スクリプト

Asmモジュールに依存しています。
http://d.hatena.ne.jp/junjun777/20120616/uwsc_function_pointer
CUI.uws

OPTION EXPLICIT

CALL Asm

IFB GET_UWSC_NAME = "CUI.uws" THEN
	STOPFORM(FALSE)
	CUI.Create()
	THREAD CuiCompSample()

	DIM loop = TRUE, cmd
	WHILE loop
		CUI.Output("> ", FALSE)
		cmd = CUI.Input()
		loop = (cmd <> "exit") AND (cmd <> "quit")
	WEND

	CUI.Destroy()
	STOPFORM(TRUE)
ENDIF

PROCEDURE CuiCompSample()
	DIM buf, id = CUI.ID()
	WHILE TRUE
		IFB GETKEYSTATE(VK_TAB) AND GETID(GET_ACTIVE_WIN) = id THEN
			buf = CUI.GetBuffer(2)	//> 」分飛ばす
			IFB buf = COPY("exit", 1, LENGTH(buf)) THEN
				CUI.PushSckey(COPY("eXit", LENGTH(buf) + 1), 1)
			ELSE
				CUI.PushSckey("", 1)
			ENDIF
		ELSE
			SLEEP(0.01)
		ENDIF
	WEND
FEND


module CUI
	public hThInput = NULL

	dim hInput, hOutput, hError
	dim orgTextAttr = 0
	dim FOREGROUND_ORIGINAL, BACKGROUND_ORIGINAL
	dim id = -1, _addr = 0, _para = 0
	hashtbl _keyConv

	function Create(title = GET_UWSC_NAME, BufferSize = 1024)
		result = AllocConsole()
		if result then SetConsoleTitleA(title)

		hInput  = GetStdHandle(STD_INPUT_HANDLE)
		hOutput = GetStdHandle(STD_OUTPUT_HANDLE)
		hError  = GetStdHandle(STD_ERROR_HANDLE)
		orgTextAttr = getOriginalTextAttribute()
		BACKGROUND_ORIGINAL = orgTextAttr and $F0
		FOREGROUND_ORIGINAL = orgTextAttr and $F
		id = getid(title, "ConsoleWindowClass")

		_addr = Asm.Set("VYvsUYtFCIlF/GoAi038g8EMUYtV/ItCCFCLTfyLUQRSi0X8iwhRi1X8i0IQ/9CL5V3CBADM")
		DIM paraSize = 20, i = 0
		_para = Asm.Alloc(paraSize)
		IFB _addr > 0 AND _para > 0 THEN
			i = i + Asm.SetDword(_para + i, hInput)
			i = i + Asm.SetDword(_para + i, Asm.Alloc(BufferSize))
			i = i + Asm.SetDword(_para + i, BufferSize)
			i = i + Asm.SetDword(_para + i, 0)
			i = i + Asm.SetDword(_para + i, Asm.GetProcAddress(Asm.hK32, "ReadConsoleA"))
		ENDIF

		_keyConv = HASH_REMOVEALL
		_keyConv[" "] = " VK_SPACE"
		_keyConv["<#TAB>"] = " VK_TAB"
		_keyConv["-"] = " 189"
		_keyConv["^"] = " 222"
		_keyConv["\"] = " 220"
		_keyConv["@"] = " 192"
		_keyConv["["] = " 219"
		_keyConv[";"] = " 187"
		_keyConv[":"] = " 186"
		_keyConv["]"] = " 221"
		_keyConv[","] = " 188"
		_keyConv["."] = " 190"
		_keyConv["/"] = " 191"
		_keyConv["\"] = " 226"
		_keyConv["="] = "+189"
		_keyConv["~"] = "+222"
		_keyConv["|"] = "+220"
		_keyConv["`"] = "+192"
		_keyConv["{"] = "+219"
		_keyConv["+"] = "+187"
		_keyConv["*"] = "+186"
		_keyConv["}"] = "+221"
		_keyConv["<"] = "+188"
		_keyConv[">"] = "+190"
		_keyConv["?"] = "+191"
		_keyConv["_"] = "+226"
		_keyConv["!"] = "+VK_1"
		_keyConv["<#DBL>"] = "+VK_2"
		_keyConv["#"] = "+VK_3"
		_keyConv["$"] = "+VK_4"
		_keyConv["%"] = "+VK_5"
		_keyConv["&"] = "+VK_6"
		_keyConv["'"] = "+VK_7"
		_keyConv["("] = "+VK_8"
		_keyConv[")"] = "+VK_9"
	fend

	function ID()
		result = id
	fend

	function GetColor(fgcolor = "", bgcolor = "")
		DIM f = _color(fgcolor)
		if f < 0 then f = FOREGROUND_ORIGINAL
		DIM b = _color(bgcolor)
		if b < 0 then b = BACKGROUND_ORIGINAL else b = b * $10
		result = f or b
	fend

	function _color(color = "", def = -1)
		select color
			case "blue", "b"
				result = FOREGROUND_BLUE or FOREGROUND_INTENSITY
			case "green", "g"
				result = FOREGROUND_GREEN or FOREGROUND_INTENSITY
			case "red", "r"
				result = FOREGROUND_RED or FOREGROUND_INTENSITY
			case "yellow", "y"
				result = FOREGROUND_GREEN or FOREGROUND_RED or FOREGROUND_INTENSITY
			case "yellow-", "y-"
				result = FOREGROUND_GREEN or FOREGROUND_RED
			case "pink", "p"
				result = FOREGROUND_BLUE or FOREGROUND_RED or FOREGROUND_INTENSITY
			case "lightblue", "lb"
				result = FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_INTENSITY
			case "white", "w"
				result = FOREGROUND_BLUE or FOREGROUND_GREEN or FOREGROUND_RED or FOREGROUND_INTENSITY
			case "black", "bl", "bk"
				result = 0
			case "gray", "gy"
				result = FOREGROUND_INTENSITY
			default
				result = def
		selend
	fend

	function Destroy()
		IFB _para THEN
			Asm.Free(Asm.GetDword(_para + 4))
			Asm.Free(_para)
		ENDIF
		IF _addr THEN Asm.Free(_addr)
		result = FreeConsole()
	fend

	procedure Clear(row = -1, col = -1, len = -1)
		DIM cbi = getConsoleScreenBufferInfoArray()
		if row = -1 then row = cbi[3]
		if col = -1 then col = cbi[2]
		if len = -1 then len = cbi[0] - col
		SetConsoleCursorPosition(hOutput, col + row * $10000)
		Output(FORMAT(" ", len), FALSE)
		SetConsoleCursorPosition(hOutput, cbi[2] + cbi[3] * $10000)
	fend

	function Output(Text = "", Cr = TRUE, color = -1, hOut = -1)
		if hOut = -1 THEN hOut = hOutput
		if Cr then Text = Text + "<#CR>"
		color = _color(color, color)
		if length(Text) then
			dim size
			if color > -1 then SetConsoleTextAttribute(hOutput, color)
			result = WriteConsoleA(hOutput, Text, lengthb(Text), size, 0)
			if color > -1 then SetConsoleTextAttribute(hOutput, orgTextAttr)
		endif
	fend

	function Input()
		DIM i = 1, timeout = $7fffffff
		IF _addr AND _para THEN hThInput = Asm.Run(_addr, _para, i, 0)
		WHILE i = 258 AND timeout > 0
			i = Asm.WaitForSingleObject(hThInput, 100)
			timeout = timeout - 100
		WEND
		Asm.CloseHandle(hThInput)
		hThInput = NULL
		ifb i = 0 then
			i = Asm.GetDword(_para + 12)
			result = FORMAT(CHR(0), i)
			RtlMoveMemory(result, Asm.GetDword(_para + 4), i)
			result = replace(result, "<#cr>", "")
		endif
	fend

	function GetBuffer(col = 0, row = -1)
		dim cbi = getConsoleScreenBufferInfoArray(), l = 1
		if row = -1 then row = cbi[3]
		if row < 0 then
			row = 0
			l = cbi[1]
		endif
		dim buf = format(chr(0), cbi[0] * l - col), num = 0
		result = empty
		if ReadConsoleOutputCharacterW(hOutput, buf, cbi[0] * l - col, col + row * $10000, num) then result = trim(buf)
	fend
	function PushSckey(cmd, backNum=0, ignoreCase=FALSE)
		result = ""
		dim i, n, nn, b = GetKeyState(TGL_CAPSLOCK), nb = false
		for i = 1 to backNum
			result = result + ",VK_BACK"
		next
		for i = 1 to length(cmd)
			n = copy(cmd, i, 1)
			if _keyConv[n, HASH_EXISTS] then
				n = _keyConv[n]
				nn = (copy(n, 1, 1) = "+")
				if nn xor nb then
					nb = !nn
				else
					nn = nb
				endif
				n = copy(n, 2)
			else
				nn = asc(n)
				nn = ((nn >= 65 and nn <= 90) xor b) and !ignoreCase
			endif
			if nn xor nb then
				if length(result) then eval("sckey(id" + result + ")")
				result = ""
				if nn then result = ",vk_shift"
				nb = nn
			endif
			result = result + "," + n
		next
		if length(result) then eval("sckey(id" + result + ")")
	fend

	function Error(Text, Cr = TRUE, color = -2)
		if color = -2 then color = GetColor("r")
		result = Output(Text, Cr, color, hError)
	fend

	function getConsoleScreenBufferInfoArray()
		result = safearray(0, 10)
		GetConsoleScreenBufferInfo(hOutput, result)
	fend

	function getOriginalTextAttribute()
		DIM CONSOLE_SCREEN_BUFFER_INFO = getConsoleScreenBufferInfoArray()
		result = CONSOLE_SCREEN_BUFFER_INFO[4]
	fend

	function GetConsoleWidth()
		DIM CONSOLE_SCREEN_BUFFER_INFO = getConsoleScreenBufferInfoArray()
		result = CONSOLE_SCREEN_BUFFER_INFO[0]
	fend

	def_dll AllocConsole():bool:kernel32
	def_dll FreeConsole():bool:kernel32
	def_dll GetStdHandle(dword):dword:kernel32
	def_dll ReadConsoleA(dword, var string, dword, var dword, dword):bool:kernel32
	def_dll WriteConsoleA(dword, string, dword, var dword, dword):bool:kernel32
	def_dll SetConsoleTitleA(string):bool:kernel32
	def_dll SetConsoleTextAttribute(dword, int):bool:kernel32
	def_dll GetConsoleScreenBufferInfo(hwnd, word[]):bool:kernel32
	def_dll SetConsoleCursorPosition(dword, dword): bool: kernel32
	def_dll RtlMoveMemory(var string, dword, dword): kernel32
	def_dll ReadConsoleOutputCharacterW(dword, var wstring, dword, dword, var dword): bool: kernel32

	const STD_INPUT_HANDLE  = -10   //標準入力ハンドルを取得
	const STD_OUTPUT_HANDLE = -11   //標準出力ハンドルを取得
	const STD_ERROR_HANDLE  = -12   //標準エラーハンドルを取得

	const FOREGROUND_BLUE		= $1  //文字色に青を加える
	const FOREGROUND_GREEN		= $2  //文字色に緑を加える
	const FOREGROUND_RED		= $4  //文字色に赤を加える
	const FOREGROUND_INTENSITY	= $8  //文字色を高輝度にする
	const BACKGROUND_BLUE		= $10 //背景色に青を加える
	const BACKGROUND_GREEN		= $20 //背景色に緑を加える
	const BACKGROUND_RED		= $40 //背景色に赤を加える
	const BACKGROUND_INTENSITY	= $80 //背景色を高輝度にする

endmodule

修正ポイント

取得側は、ReadConsoleOutputCharacter Win32APIで可能なことがわかったので、これで取得することにしました。
設定側は、UWSCのSCKEYを使いました。
記号は小細工して送れるようにしていますが、日本語は送れません。
UWSC4.9a以降なら、KBDが使えるかな、、、?
いずれにしても、少々もっさりしてしまうのは否めない、、、。


AttachConsole Win32APIを使えば、http://d.hatena.ne.jp/junjun777/20110529/uwsc_cmdクリップボード非依存にできますね。


ついでにインタープリターも更新しました。
上のCUIモジュールに依存しています。

OPTION EXPLICIT

CALL cui


STOPFORM(FALSE)
Interpreter.Run()
STOPFORM(TRUE)


module Interpreter
	// 予約済み変数
	hashtbl hash, hash2

	hashtbl __threads

	CONST VAR_HASH = VAR_ARRAY * 2	// 勝手定義
	CONST SEC_SETTINGS = "settings"

	procedure Run()
		thread Proc()
		sleep(0.1)
		while length(__threads)
			sleep(0.01)
		wend
	fend
	procedure Proc(__cmd=EMPTY)
		hashtbl __cmdN
		dim __ret="", __i, __id, __cmds, __exit=false, __if=0, __while=0, __cui=(__cmd=EMPTY)
		dim __display=TRUE, __key, __val, __mode, __offset, __c, __ck, __cp
		dim __r1=NULL, __r2=NULL, __r3=NULL, __threadId=GetCurrentThreadId()
		__threads[__threadId] = __cmd
		if __cui then
			CUI.Create()
			THREAD Complete()
			__cmd = trim(readini(SEC_SETTINGS, "startup"))
		endif
		if length(__cmd) = 0 then __cmd = ""
		while __cmd <> EMPTY
			__cmds = split(__cmd, " ", TRUE)
			if length(__cmds) = 0 then __cmds = safearray(0, 0)
			select __cmds[0]
			case "exit", "quit"
				__cmd = EMPTY
				__exit = length(__cmds) > 1 or !__cui
				if !__exit then
					CUI.Output("終了しますか? (y/n): ", FALSE)
					__exit = (copy(trim(CUI.Input()), 1, 1) = "y")
				endif
				if __exit and __cui then __cmd = trim(readini(SEC_SETTINGS, "exit"))
				if length(__cmd) then continue
			case "window"
				__cmd = EMPTY
				if length(__cmds) > 1 then
					__ret = val(__cmds[1], 1)
				else
					__ret = FALSE
				endif
				CUI.Output("ID	Title / Class", TRUE)
				CUI.Output(format("-", CUI.GetConsoleWidth() - 1), TRUE)
				for __i = 0 to getallwin() - 1
					__id = ALL_WIN_ID[__i]
					if !__ret and ! status(__id, ST_VISIBLE) then continue
					CUI.Output(format(__id, 4), FALSE)
					CUI.Output(format(" ", 2) + status(__id, ST_TITLE), TRUE)
					CUI.Output(format(" ", 6) + status(__id, ST_CLASS), TRUE)
				next
			case "enum"
				if length(__cmds) > 1 then
					__cmdN[length(__cmdN)] = __cmd
					try
						if __mode = 0 then
							__display = false
							try
								__id = eval("VarTypeEx(" + __cmds[1] + ")")
							except
								__id = eval("vartype(" + __cmds[1] + ")")
							endtry
							__mode = eval("length(" + __cmds[1] + ")")
							__i = 0
							if __id <> VAR_HASH AND __id >= VAR_ARRAY then
								try
									__offset = eval(__cmds[1])
									__offset = resize(__offset) - __mode + 1
								except
									__offset = eval("resize(" + __cmds[1] + ")") - __mode + 1
								endtry
							endif
						else
							if VarTypeEx(__val) >= VAR_ARRAY then
								CUI.Output(__cmds[1] + "[" + __key + "] = var_array:" + length(__val) + " " + __ret)
							else
								CUI.Output(__cmds[1] + "[" + __key + "] = " + __val + " " + __ret)
							endif
						endif
						if __i < __mode and __id >= VAR_ARRAY then
							if __id = VAR_HASH then
								__key = eval(__cmds[1] + "[" + __i + ",HASH_KEY]")
								__val = eval(__cmds[1] + "[" + __i + ",HASH_VAL]")
							else
								__key = __i + __offset
								__val = eval(__cmds[1] + "[" + __key + "]")
							endif
							if length(__cmds) > 2 then __cmd = trim(join(slice(__cmds, 2))) else __cmd = ""
							__ret = ""
							__i = __i + 1
						else
							__mode = 0
						endif
					except
						CUI.Error("  " + TRY_ERRMSG)
						__mode = 0
					endtry
					if __mode = 0 then
						__display = true
						__cmd = EMPTY
						if length(__cmdN) then __i = __cmdN[length(__cmdN) - 1, HASH_REMOVE]
					endif
				endif
			case "thread"
				if length(__cmds) > 1 then thread Proc(trim(copy(__cmd, 7)))
				__cmd = EMPTY
			case "help", "?"
				__cmd = EMPTY
				if length(__cmds) > 1 then
					doscmd(GET_UWSC_DIR + "\uwsc.chm", TRUE)
				else
					Help()
				endif
			default
				if pos("#", __cmds[0]) = 1 then
					__ck = trim(readini(SEC_SETTINGS, __cmds[0]))
					if length(__ck) then
						__cmd = __ck
						continue
					endif
				endif
			selend
			if length(__cmd) then
				__c = Conv(__r1, __r2, __r3, __cmd, __ck, __cp)
				try
					if __ck = "if" and length(__cp) > 0 then
						if length(__cp[0]) then
							__if = __if + 1
							if length(__cmd) then __cmdN[length(__cmdN)] = __cmd
							if length(__cp) < 3 then resize(__cp, 2)
							__cmdN[length(__cmdN)] = __cp[2]
							__cmdN[length(__cmdN)] = __cp[1]
							__cmd = __cp[0]
						elseif length(__cp) > 2 then
							for __ck = length(__cp) - 1 to 2 step - 1
								__cmdN[length(__cmdN)] = __cp[__ck]
							next
							__cmd = __cp[1]
						endif
						__ret = ""
					elseif __ck = "while" and length(__cp) > 0 then
						__while = (__while * 4) or 1
						if length(__cmd) then __cmdN[length(__cmdN)] = __cmd
						if length(__cp) < 2 then resize(__cp, 1)
						__cmdN[length(__cmdN)] = __cp[0]
						__cmdN[length(__cmdN)] = __cp[1]
						__cmd = __cp[0]
						__ret = ""
					else
						if length(__c) then __ret = eval(__c) else __ret = ""
						if __while and 2 then
							if length(__cmd) = 0 then __while = (__while - 2) or 1
						elseif __while and 1 then
							if length(__cmd) = 0 then
								try
									__ret = vartype(__ret, var_boolean)
								except
									__ret = false
								endtry
								__while = __while - 1
								if __ret then
									__while = __while or 2
									__cmd = __cmdN[length(__cmdN) - 1]
									__cmdN[length(__cmdN)] = __cmdN[length(__cmdN) - 2]
								else
									__ck = __cmdN[length(__cmdN) - 1, hash_remove]
									__ck = __cmdN[length(__cmdN) - 1, hash_remove]
									__while = int(__while / 4)
								endif
								__ret = ""
							endif
						elseif __if > 0 and length(__cmd) = 0 then
							__if = __if - 1
							try
								__ret = vartype(__ret, var_boolean)
							except
								__ret = false
							endtry
							if __ret then
								__cmd = __cmdN[length(__cmdN)-1]
							else
								__cmd = __cmdN[length(__cmdN)-2]
							endif
							__ret = __cmdN[length(__cmdN)-1, hash_remove]
							__ret = __cmdN[length(__cmdN)-1, hash_remove]
							__ret = ""
						endif
					endif
					if __display then
						select __ret
						case EMPTY
							CUI.Output("  EMPTY", TRUE, "lb")
						case NULL
							CUI.Output("  NULL", TRUE, "lb")
						case NOTHING
							CUI.Output("  NOTHING", TRUE, "lb")
						case ""
							// do nothing
						default
							CUI.Output( "  " + __ret, TRUE, "w")
						selend
					endif
				except
					CUI.Error("  " + TRY_ERRMSG)
				endtry
			endif
			if length(__cmd) = 0 and length(__cmdN) > 0 then
				__cmd = __cmdN[length(__cmdN) - 1]
				__ck = __cmdN[length(__cmdN) - 1, hash_remove]
			endif
			if length(__cmd) = 0 then
				if __exit then
					__cmd = EMPTY
				elseif __cui then
					ctrlwin(CUI.ID(), ACTIVATE)
					CUI.Output("> ", FALSE)
					__cmd = trim(CUI.Input())
				endif
			endif
		wend
		if __cui then CUI.Destroy()
		__threadId = __threads[__threadId, HASH_REMOVE]
	fend
	def_dll GetCurrentThreadId(): dword: kernel32

	function Conv(var __r1, var __r2, var __r3, var cmd, var kind, var p)
		ifb __r1 = NULL then
			__r1 = CreateOleObj("VBScript.RegExp")
			__r1.Pattern = "^((?:[^=()<#DBL>]|<#DBL>[^<#DBL>]*<#DBL>|\([^)]*\))+)=(?!=)"
		endif
		ifb __r2 = NULL then
			__r2 = CreateOleObj("VBScript.RegExp")
			__r2.Pattern = "^((?:[^=()<#DBL>]|<#DBL>[^<#DBL>]*<#DBL>|\([^)]*\))+)=(?==)"
		endif
		ifb __r3 = NULL then
			__r3 = CreateOleObj("VBScript.RegExp")
			__r3.Pattern = "((?:[^,();<#DBL>]|<#DBL>[^<#DBL>]*<#DBL>)*)([,();])?"
			__r3.Global = TRUE
		endif

		// 関数解析
		dim ms = __r3.Execute(cmd), i, step=0, s=0, wk="", res=EMPTY
		kind = EMPTY
		result = EMPTY
		p = safearray(0, -1)
		ifb ms.Count > 0 then
			kind = ms.Item(0).SubMatches(0)
			for i = 0 to ms.Count - 1
				res = res + ms.Item(i).Value
				if s = 0 then
					if ms.Item(i).SubMatches(1) = ";" and result = EMPTY then
						result = copy(res, 1, length(res) - 1)
						res = ""
					endif
				elseif s = 1 and result = EMPTY then
					wk = wk + ms.Item(i).Value
					if ms.Item(i).SubMatches(1) = "," or ms.Item(i).SubMatches(1) = ")" then
						if length(p) <= step then resize(p, step)
						p[step] = trim(copy(wk, 1, length(wk) - 1))
						wk = ""
						step = step + 1
					endif
				else
					wk = wk + ms.Item(i).Value
				endif
				if ms.Item(i).SubMatches(1) = "(" then s = s + 1
				if ms.Item(i).SubMatches(1) = ")" and s > 0 then s = s - 1
			next
		endif
		if result = EMPTY then
			if res = EMPTY then res = cmd
			result = res
			cmd = EMPTY
		else
			cmd = trim(res)
			if length(cmd) = 0 then cmd = EMPTY
		endif
		// =を:=に
		result = __r1.Replace(trim(result), "$1:=")
		// ===に
		result = __r2.Replace(result, "$1")
	fend

	FUNCTION VarTypeEx(val[])
		TRY
			RESULT = VarType(val)
		EXCEPT
			IFB POS("が定義されていません", TRY_ERRMSG) THEN
				RESULT = VAR_HASH
			ELSE
				RESULT = VAR_VARIANT + VAR_ARRAY
			ENDIF
		ENDTRY
	FEND

	CONST SEC_TYPE = "types"
	CONST SEC_VAL = "vals"
	FUNCTION GetName(name, i)
		RESULT = name + "["
		IF CHKNUM(i) THEN RESULT = RESULT + i + "]" ELSE RESULT = RESULT + "<#DBL>" + i + "<#DBL>]"
	FEND
	PROCEDURE SaveP(name, data)
		DIM type=VARTYPE(data), i, l, u
		// safearrayはこっちで処理
		IFB type >= VAR_ARRAY THEN
			WRITEINI(SEC_TYPE, name, type)
			u = RESIZE(data)
			l = u - LENGTH(data) + 1
			WRITEINI(SEC_VAL, name, l + "," + u)
			FOR i = l TO u
				type = VARTYPE(data[i])
				IFB type >= VAR_ARRAY THEN
					SaveP(GetName(name, i), data[i])
				ELSE
					WRITEINI(SEC_TYPE, GetName(name, i), type)
					WRITEINI(SEC_VAL, GetName(name, i), data[i])
				ENDIF
			NEXT
		ELSE
			WRITEINI(SEC_TYPE, name, type)
			WRITEINI(SEC_VAL, name, data)
		ENDIF
	FEND
	PROCEDURE Save(name, data[])
		DIM type, i
		TRY
			type = VarTypeEx(data)
		EXCEPT
			type = VARTYPE(data)
		ENDTRY
		// 通常配列がある場合は、それもここで処理
		IFB type = VAR_HASH THEN
			WRITEINI(SEC_TYPE, name, type)
			WRITEINI(SEC_VAL, name, LENGTH(data))
			FOR i = 0 TO LENGTH(data) - 1
				type = VARTYPE(data[i, HASH_VAL])
				IFB type >= VAR_ARRAY THEN
					SaveP(GetName(name, data[i, HASH_KEY]), data[i, HASH_VAL])
				ELSE
					WRITEINI(SEC_TYPE, GetName(name, data[i, HASH_KEY]), type)
					WRITEINI(SEC_VAL, GetName(name, data[i, HASH_KEY]), data[i, HASH_VAL])
				ENDIF
			NEXT
		ELSE
			SaveP(name, data)
		ENDIF
	FEND
	DEF_DLL GetPrivateProfileStringA(string,string,string,DWORD,DWORD,string): DWORD: kernel32
	DEF_DLL RtlMoveMemory(var string,DWORD,DWORD): kernel32
	DEF_DLL lstrlenA(DWORD): int: kernel32
	FUNCTION ReadSections(sec, ini=EMPTY)
		DIM size=2048, ptr = 0, i = size, len, val
		IF LENGTH(ini) = 0 THEN ini = ".\" + REPLACE(GET_UWSC_NAME, ".uws", ".ini")
		WHILE i >= size - 2
			IF ptr THEN Asm.Free(ptr)
			size = size * 2
			ptr = Asm.Alloc(size)
			IF ptr THEN i = GetPrivateProfileStringA(sec, NULL, NULL, ptr, size, ini)
		WEND
		HASHTBL buf
		IFB ptr THEN
			i = ptr
			len = lstrlenA(i)
			WHILE len > 0
				val = FORMAT(CHR(0), len)
				RtlMoveMemory(val, i, len)
				buf[LENGTH(buf)] = val
				i = i + len + 1
				len = lstrlenA(i)
			WEND
			Asm.Free(ptr)
		ENDIF
		len = LENGTH(buf) - 1
		RESULT = SAFEARRAY(0, len)
		FOR i = 0 TO len
			RESULT[i] = buf[i]
		NEXT
	FEND
	FUNCTION Load(name)
		RESULT = NULL
		DIM type = READINI(SEC_TYPE, name), val = READINI(SEC_VAL, name), i, secs
		TRY
			IFB type >= VAR_ARRAY THEN
				IFB type = VAR_HASH THEN
					EVAL(name + ":=HASH_REMOVEALL")
					secs = ReadSections(SEC_TYPE)
					FOR i = 0 TO LENGTH(secs) - 1
						IFB POS(name + "[", secs[i]) = 1 AND POS("[", COPY(secs[i], LENGTH(name) + 2)) = 0 THEN
							type = READINI(SEC_TYPE, secs[i])
							IFB type >= VAR_ARRAY THEN
								val = Load(secs[i])
								EVAL(secs[i] + ":=val")
							ELSE
								EVAL(secs[i] + ":=VARTYPE(<#DBL>" + READINI(SEC_VAL, secs[i]) + "<#DBL>," + type + ")")
							ENDIF
						ENDIF
					NEXT
					RESULT = VAR_HASH
				ELSE
					secs = VAL(COPY(val, 1, POS(",", val) - 1))
					val = VAL(COPY(val, POS(",", val) + 1))
					RESULT = SAFEARRAY(secs, val)
					FOR i = secs TO val
						type = READINI(SEC_TYPE, GetName(name, i))
						IFB type >= VAR_ARRAY THEN
							RESULT[i] = Load(GetName(name, i))
						ELSE
							RESULT[i] = VARTYPE(READINI(SEC_VAL, GetName(name, i)), type)
						ENDIF
					NEXT
				ENDIF
			ELSE
				RESULT = VARTYPE(val, type)
			ENDIF
		EXCEPT
			// nop
		ENDTRY
	FEND
	PROCEDURE Delete(name)
		DIM type = READINI(SEC_TYPE, name), val = READINI(SEC_VAL, name), i, secs
		IFB type >= VAR_ARRAY THEN
			IFB type = VAR_HASH THEN
				secs = ReadSections(SEC_TYPE)
				FOR i = 0 TO LENGTH(secs) - 1
					IFB POS(name + "[", secs[i]) = 1 THEN
						DELETEINI(SEC_TYPE, secs[i])
						DELETEINI(SEC_VAL, secs[i])
					ENDIF
				NEXT
			ELSE
				secs = VAL(COPY(val, 1, POS(",", val) - 1))
				val = VAL(COPY(val, POS(",", val) + 1))
				FOR i = secs TO val
					type = READINI(SEC_TYPE, GetName(name, i))
					IFB type >= VAR_ARRAY THEN
						Delete(GetName(name, i))
					ELSE
						DELETEINI(SEC_TYPE, GetName(name, i))
						DELETEINI(SEC_VAL, GetName(name, i))
					ENDIF
				NEXT
			ENDIF
		ENDIF
		DELETEINI(SEC_TYPE, name)
		DELETEINI(SEC_VAL, name)
	FEND

	PROCEDURE Complete()
		DIM __r = CREATEOLEOBJ("VBScript.RegExp"), id = CUI.ID(), buf, ret, n, k
		__r.Pattern = "[^ !<#DBL>$%&'()\-=^\\|@`[\]{}+*;:<>,./?]+.?$"
		WHILE TRUE
			IFB GETKEYSTATE(VK_TAB) AND GETID(GET_ACTIVE_WIN) = id THEN
				buf = CUI.GetBuffer()
				ret = __r.Execute(CUI.GetBuffer())
				IF ret.Count > 0 THEN buf = ret.Item(0).Value
				IFB buf <> TRIM(_comp_code) THEN
					_comp_base = buf
					_comp_sels = HASH_REMOVEALL
					FOR k = 0 TO LENGTH(_comps) - 1
						FOR n = 0 TO LENGTH(_comps[k]) - 1
							IF POS(buf, _comps[k][n]) = 1 THEN _comp_sels[n] = _comps[k][n]
						NEXT
					NEXT
					_comp_n = 0
				ELSE
					buf = _comp_code
				ENDIF
				IF LENGTH(_comp_sels) THEN _comp_code = _comp_sels[_comp_n, HASH_VAL] ELSE _comp_code = buf
				_comp_n = _comp_n + 1
				IF LENGTH(_comp_sels) <= _comp_n THEN _comp_n = 0
				CUI.PushSckey(COPY(_comp_code, LENGTH(_comp_base) + 1), LENGTH(buf) - LENGTH(_comp_base) + 1, true)
			ENDIF
			SLEEP(0.1)
			GETKEYSTATE(VK_TAB)
		WEND
	FEND

	HASHTBL _comp_sels
	DIM _comp_base = EMPTY, _comp_code = EMPTY, _comp_n = 0
	DIM _comps[] = SPLIT("help  window  enum  Load( Save( Delete( DIM  PUBLIC  CONST  IF( THEN  ELSE  IFB  ELSEIF  ENDIF SELECT  CASE  DEFAULT SELEND FOR  NEXT TO  STEP  WHILE( WEND REPEAT UNTIL  CALL  BREAK CONTINUE EXIT  PRINT  AND  OR  XOR  MOD  PROCEDURE  FUNCTION  FEND RESULT  VAR  DEF_DLL  OPTION  THREAD  CLASS  ENDCLASS THIS. GLOBAL WITH  ENDWITH TEXTBLOCK  ENDTEXTBLOCK HASHTBL  TRY ENDTRY EXCEPT FINALLY GETID( CLKITEM( CHKBTN( CTRLWIN( SENDSTR( GETSTR( GETITEM( GETSLCTLST( SETSLIDER( GETSLIDER( SCKEY( GETALLWIN( STATUS( MOUSEORG( PEEKCOLOR( CHKIMG( SAVEIMG( MUSCUR( POSACC( INPUT( MSGBOX( SLCTBOX( POPUPMENU( BALLOON( FUKIDASI( STOPFORM( LOGPRINT( MONITOR( EXEC( SLEEP( DOSCMD( POWERSHELL( SOUND( GETTIME( POFF( KINDOFOS( CPUUSERATE( GETKEYSTATE( SETHOTKEY( LOCKHARD( EVAL( GETCTLHND( IDTOHND( HNDTOID( VARTYPE( MMV( BTN( KBD( ACW( COPY( POS( LENGTH( CHKNUM( VAL( REPLACE( TRIM( FORMAT( CHR( ASC( ISUNICODE( STRCONV( TOKEN( BETWEENSTR( COPYB( LENGTHB( POSB( CHRB( ASCB( RESIZE( SETCLEAR( SHIFTARRAY( CALCARRAY( SPLIT( JOIN( SLICE( QSORT( FOPEN( FGET( FPUT( FDELLINE( FCLOSE( GETDIR( DROPFILE( READINI( WRITEINI( DELETEINI( CREATEOLEOBJ( GETACTIVEOLEOBJ( GETOLEITEM( OLEEVENT( COM_ERR_IGN COM_ERR_RET SAFEARRAY( SPEAK( RECOSTATE( DICTATE( IEGETDATA( IESETDATA( IEGETSRC( IESETSRC( IELINK( ENCODE( DECODE( CREATEFORM( GETFORMDATA( SETFORMDATA( XLOPEN( XLCLOSE( XLACTIVATE( XLSHEET( XLGETDATA( XLSETDATA( RANDOM( ABS( ZCUT( INT( CEIL( ROUND( SQRT( POWER( EXP( LN( LOGN( SIN( COS( TAN( ARCSIN( ARCCOS( ARCTAN( PARAM_STR[ ALL_WIN_ID[ ALL_ITEM_LIST[ GETDIR_FILES[ ALL_OLE_ITEM[ EVENT_PRM[ GET_WIN_DIR GET_SYS_DIR GET_CUR_DIR GET_APPDATA_DIR GET_UWSC_DIR GET_UWSC_VER GET_UWSC_NAME COM_ERR_FLG G_IMG_X G_IMG_Y G_SCREEN_W G_SCREEN_H G_SCREEN_C G_MOUSE_X G_MOUSE_Y TRY_ERRMSG TRY_ERRLINE HOTKEY_VK HOTKEY_MOD G_TIME_YY G_TIME_MM G_TIME_DD G_TIME_HH G_TIME_NN G_TIME_SS G_TIME_ZZ G_TIME_WW G_TIME_YY2 G_TIME_MM2 G_TIME_DD2 G_TIME_HH2 G_TIME_NN2 G_TIME_SS2 G_TIME_ZZ2 G_TIME_YY4 TRUE FALSE NULL EMPTY NOTHING ERR_VALUE GET_SLIDER GET_MENU_HND GET_SYSMENU_HND ACTIVATE CLOSE CLOSE2 HIDE SHOW MIN MAX NORMAL TOPMOST NOTOPMOST TOPNOACTV CLICK DOWN UP LEFT RIGHT MIDDLE WHEEL F_READ F_WRITE F_EXISTS F_EXCLUSIVE F_TAB F_NOCR F_WRITE8 F_WRITE8B F_WRITE16 F_WRITE1 F_LINECOUNT F_ALLTEXT F_INSERT CLK_BTN CLK_LIST CLK_TAB CLK_MENU CLK_TREEVIEW CLK_LISTVIEW CLK_TOOLBAR CLK_ACC CLK_SHORT CLK_BACK CLK_MOUSEMOVE CLK_RIGHTCLK CLK_LEFTCLK CLK_DBLCLK<e3><80><80>CLK_FROMLAST CLK_CONTINUE STR_EDIT STR_STATIC STR_STATUS STR_ACC_EDIT STR_ACC_STATIC BTN_YES BTN_NO BTN_OK BTN_CANCEL BTN_ABORT BTN_RETRY BTN_IGNORE SLCT_BTN SLCT_CHK SLCT_RDO SLCT_CMB SLCT_LST SLCT_STR SLCT_NUM SLCT_1~31 ITM_BTN ITM_LIST ITM_TAB ITM_MENU ITM_TREEVIEW ITM_LISTVIEW ITM_EDIT ITM_STATIC ITM_STATUSBAR ITM_TOOLBAR ITM_ACCCLK ITM_ACCCLK2 ITM_ACCTXT ITM_BACK ST_TITLE ST_CLASS ST_X ST_Y ST_WIDTH ST_HEIGHT ST_CLX ST_CLY ST_CLWIDTH ST_CLHEIGHT ST_PARENT ST_ICON ST_MAXIMIZED ST_VISIBLE ST_ACTIVE ST_BUSY ST_PATH ST_PROCESS ST_WIN64 ST_MONITOR CUR_APPSTARTING CUR_ARROW CUR_CROSS CUR_HAND CUR_HELP CUR_IBEAM CUR_NO CUR_SIZEALL CUR_SIZENESW CUR_SIZENS CUR_SIZENWSE CUR_SIZEWE CUR_UPARROW CUR_WAIT ACC_ACC ACC_API ACC_NAME ACC_VALUE ACC_ROLE ACC_STATE ACC_DESCRIPTION ACC_LOCATION ACC_BACK P_POWEROFF P_LOGOFF P_REBOOT P_SUSPEND P_SUSPEND2 P_MONIPOWER P_MONIPOWER2 P_MONIPOWER3 P_SCREENSAVE P_UWSC_REEXEC P_FORCE TGL_IME TGL_NUMLOCK TGL_CAPSLOCK TGL_SCROLLLOCK TGL_KANALOCK GET_ACTIVE_WIN GET_FROMPOINT_WIN GET_FROMPOINT_OBJ GET_THISUWSC_WIN GET_LOGPRINT_WIN GET_FUKIDASI_WIN GET_FORM_WIN GET_SCHEDULE_WIN SC_LOWERCASE SC_UPPERCASE SC_HIRAGANA SC_KATAKANA SC_HALFWIDTH SC_FULLWIDTH FOM_NOICON FOM_MINIMIZE FOM_MAXIMIZE FOM_NOHIDE FOM_NOSUBMIT FOM_NORESIZE FOM_NOLUNA FOM_BROWSER CODE_URL CODE_UTF8 CODE_BYTEARRAY CODE_BYTEARRAYW CALC_ADD CALC_MIN CALC_MAX CALC_AVR IMG_MSK_BGR1~4 IMG_MSK_B1~4 IMG_MSK_G1~4 IMG_MSK_R1~4 MOD_ALT MOD_CONTROL MOD_SHIFT MOD_WIN SLD_POS SLD_MIN SLD_MAX SLD_PAGE SLD_BAR SLD_X SLD_Y MON_X MON_Y MON_WIDTH MON_HEIGHT COL_RGB COL_R COL_G COL_B MORG_CLIENT MORG_DIRECT MORG_FORE MORG_BACK VK_START VK_WIN VK_ALT VK_CTRL VK_RCTRL VK_ESC VK_BACK VK_TAB VK_CLEAR VK_RETURN VK_RRETURN VK_SHIFT VK_RSHIFT VK_CONTROL VK_MENU VK_PAUSE VK_CAPITAL VK_KANA VK_FINAL VK_KANJI VK_CONVERT VK_NONCONVERT VK_ACCEPT VK_MODECHANGE VK_ESCAPE VK_SPACE VK_PRIOR VK_NEXT VK_END VK_HOME VK_LEFT VK_UP VK_RIGHT VK_DOWN VK_SELECT VK_PRINT VK_EXECUTE VK_SNAPSHOT VK_INSERT VK_DELETE VK_HELP VK_APPS VK_MULTIPLY VK_ADD VK_SEPARATOR VK_SUBTRACT VK_DECIMAL VK_DIVIDE VK_NUMPAD0~9 VK_F1~12 VK_NUMLOCK VK_SCROLL VK_SLEEP VK_BROWSER_BACK VK_BROWSER_FORWARD VK_BROWSER_REFRESH VK_BROWSER_STOP VK_BROWSER_SEARCH VK_BROWSER_FAVORITES VK_BROWSER_HOME VK_VOLUME_MUTE VK_VOLUME_DOWN VK_VOLUME_UP VK_MEDIA_NEXT_TRACK VK_MEDIA_PREV_TRACK VK_MEDIA_STOP VK_MEDIA_PLAY_PAUSE VK_LAUNCH_MEDIA_SELECT VK_LAUNCH_MAIL VK_LAUNCH_APP1 VK_LAUNCH_APP2 VK_OEM_PLUS VK_OEM_COMMA VK_OEM_MINUS VK_OEM_PERIOD VK_OEM_1~8 VK_OEM_RESET VK_OEM_JUMP VK_OEM_PA1~3 HASH_CASECARE HASH_SORT HASH_EXISTS HASH_REMOVE HASH_KEY HASH_VAL HASH_REMOVEALL VAR_EMPTY VAR_NULL VAR_SMALLINT VAR_INTEGER VAR_SINGLE VAR_DOUBLE VAR_CURRENCY VAR_DATE VAR_BSTR VAR_DISPATCH VAR_ERROR VAR_BOOLEAN VAR_VARIANT VAR_UNKNOWN VAR_SBYTE VAR_BYTE VAR_WORD VAR_DWORD VAR_INT64 VAR_ARRAY", " "), SAFEARRAY(0,-1)

	procedure Help()
		CUI.Output()
		CUI.Output("※使い方", TRUE, "lb")
		CUI.Output("  式を入力すると実行します")
		CUI.Output("  式の戻り値はコンソールに表示されます")
		CUI.Output()
		CUI.Output("※変数について", TRUE, "lb")
		CUI.Output("  連想配列は ", FALSE)
		CUI.Output("hash hash2", FALSE, "y")
		CUI.Output(" を使ってください")
		CUI.Output("  配列は SAFEARRAY関数で作成してください")
		CUI.Output("  先頭の比較演算子は ", FALSE)
		CUI.Output("==", FALSE, "y")
		CUI.Output(" を使用してください(先頭の=は代入文と認識されます)")
		CUI.Output()
		CUI.Output("※特殊コマンド一覧", TRUE, "lb")
		CUI.Output("help [uwsc]", TRUE, "y")
		CUI.Output("   このヘルプを表示します", TRUE)
		CUI.Output("   オプション指定でuwscのヘルプを表示します", TRUE)
		CUI.Output("window [all]", TRUE, "y")
		CUI.Output("   ウィンドウIDを列挙、オプション指定で非表示ウィンドウも列挙します", TRUE)
		CUI.Output("enum 配列名", TRUE, "y")
		CUI.Output("   配列の内容を列挙します", TRUE)
		CUI.Output("exit [silent]", TRUE, "y")
		CUI.Output("   " + GET_UWSC_NAME + "を終了します、オプション指定で確認なしで終了", TRUE)
		CUI.Output()
	fend

endmodule

対応内容

特殊コマンドの#はずし

#が押しにくいので外しました。
既存コマンドとはかぶってないしね。
戻したければ、Run関数のselect文を修正してください。

補完

クリップボード依存じゃなくなりました!
少々もっさりしてるので、タブ連打は許してくらはい。
補完の順番に不満がある場合は、がーっと書いてある文字列の順番を直してください。


ポインターが指している目障りなTopMostウインドウのTopMost解除をしたい場合、
ct<#TAB>ge<#TAB>get_f<#TAB>),no<#TAB><#TAB><#TAB>)<#CR>
で入力が可能!23タイプの節約!52%OFFですよ!

複文

tokenによる;区切りの複文を入れました。
それに伴い、#waitコマンドを廃止しました。
stuncloudさんのモジュールで、

#wait 1 msgbox("ok?")

sleep(1);msgbox("ok?")

となります。
特殊コマンドの後の複文は上手くいかないケースが多いです。

enum拡張

getallwinとかした時に、all_win_idをenumしたいじゃないですか。
やむなく、関数を統合して、巨大なRun関数にしました。
これで、enumの制限がなくなります。


ついでに、enumによる列挙処理をできるようにしました。
「window all」は

getallwin()
enum all_win_id status(__val,st_title)+" : "+status(__val,st_class)

とかでも似たような感じですね。


また、ハッシュテーブルを二つ用意しましたが、ハッシュテーブルのコピーは、

enum hash hash2[__key]=__val

でOKです。
enum」便利!

thread起動

指定したコマンドをスレッドで実行します。

thread sleep(3);msgbox("proc?")

スレッド側から出力があると、画面表示がおかしくなりますが、まあ気にしない。
ほとんどの変数は渡せませんが、hash/hash2だけはスレッドからも参照できます。
参照できる変数を増やしたければ、モジュール変数を増やしてください。

if分岐

if関数がなんとなく実装されています。
いろいろ不完全ですが、、、。

if(a==1,msgbox("OK"),msgbox("NG"))

が可能となっています。
が、この場合aが存在しないと、おかしなことになります。
また、条件のないif文は、両方を実行するので、複文として機能します。

whileループ

while関数によるループを実装しました

i=0;while(i<10,j=cui.output(i);i=i+1)

jで受けているのは、cui.outputの結果を表示させないため。
一応ネスト可能だけど、if関数とは仲が悪そう(バグりそう)、、、。

代入演算子と等号

先頭の「=」を「:=」にしたり、先頭の「==」を「=」にしたりしてます。
if/while関数内はそれぞれが文とみなされるので、それぞれ先頭だったりします。
複文も分割後が先頭。
先頭以外は置換されないので、留意願います。

変数の保存

Save/Load/Delete関数で変数の保存/復元/削除ができます。
保存先は、スクリプト名.ini。


Save関数の第一引数は、保存する名前。
ただし、ハッシュテーブルは変数名と同じでないと使えません。
第二引数が、保存する変数自身。

Save("hash", hash)


Load関数の引数は、名前だけ。
ハッシュテーブルは、同名のテーブルにセット(RESULTは、Interpreter.VAR_HASH)
それ以外はRESULTで返す。

a = Load("a")


iniに保存された変数が不要になったら、Delete関数に名前を渡して削除してください。

Delete("a")
Startup

iniファイルの[settings] startupキーにコマンドを定義します。
想定としては、Load関数を呼ぶかなー、と思っています。


補完用の配列_compsは、二次元配列になっています。
(_comps[0]以外は現在空ですが)
これを利用して_comps[1]等追加しても良いかと思います。

Exit

iniファイルの[settings] exitキーにコマンドを定義すると、「exit」の際に実行します。
想定としては、、、Save関数を呼ぶかなー、と。

マクロ

iniファイルの[settings]セクションに「#」で始まるキーに設定すると、その名前で呼べます。
よくやる複文を登録すると便利かもしれません。
登録は、iniファイルに直接書くか、「WRITEINI(SEC_SETTINGS, "#キー名", "コマンド")」