UWSCのメモリーを管理する

VirtualAllocとかHeapAllocとかしてしまうと、終了時に解放する必要がある。
どちらもプロセスメモリーだから、プロセスを終了してしまうのなら特に問題にはならないけど、UWSC本体を立ち上げっぱなしで、何回も実行するスクリプトだと、そのうちメモリーがいっぱいになる。


だから私の書くモジュールには、「Disposeしてください」という表記が目立つ。


しかし、、、これは人手でやることだろうか?
否、否、否、断じて否である。
記憶力の良い阿呆(=コンピューター)が覚えていて解放すれば良いだけのこと。
これを解決する方法を考える。



方針

プロセスメモリーはプロセスが解放されない限り存在する。
だから、プロセスメモリーに管理情報を入れれば良い。
でも、そのアドレスがわからなくなる、、、。


グローバルアトムに書いてしまう?
、、、いや、次の起動時にわからない、、、。
、、、
そうだ、メモリーマップドファイルだ!
あれなら、名前でハンドルが取れる。
プロセスが終わる時にハンドルは閉じられるし、最後のハンドルが閉じればメモリーも解放される。
モリーマップドファイルにアドレスを入れよう。
そして、次の実行ではそこを開けば良い。
最初の実行では開いてもないから、メモリーは復元されない。
次の実行で復元される、という次第。


あとは、スクリプトの変数をメモリーに格納する方法を考える必要がある。
UWSCのほとんどの数値はVARTYPEによるとdoubleらしい。
までも少しメモリーの節約は入れよう。
配列と連想配列を関数に渡すのがやっかいだけど、[]をトリッキーに使えばなんとかなる模様。
で、作りました。

スクリプト

MemMgr.uws

OPTION EXPLICIT

IFB GET_UWSC_NAME = "MemMgr.uws" THEN

	HASHTBL datas

	DIM dump = "hashtbl<#CR>", size, i = MemMgr.GetEx("datas", datas)
	IF i <> NULL THEN MSGBOX("Read! " + i)

	DIM key = INPUT("hashtbl add?")
	IF LENGTH(key) THEN datas[key] = INPUT("value? " + key)
	FOR i = 0 TO LENGTH(datas) - 1
		dump = dump + datas[i, HASH_KEY] + "=" + datas[i, HASH_VAL] + "<#CR>"
	NEXT
	MemMgr.Set("datas", datas)
	MSGBOX(dump)

	IF MSGBOX("Dispose?", BTN_YES OR BTN_NO) = BTN_YES THEN MemMgr.Dispose()
ENDIF


MODULE MemMgr
	DEF_DLL CreateFileMappingW(DWORD,DWORD,DWORD,DWORD,DWORD,wstring): DWORD: kernel32
	DEF_DLL OpenFileMappingW(DWORD,BOOL,wstring): DWORD: kernel32
	DEF_DLL CloseHandle(DWORD): BOOL: kernel32
	DEF_DLL MapViewOfFile(DWORD,DWORD,DWORD,DWORD,DWORD): DWORD: kernel32
	DEF_DLL UnmapViewOfFile(DWORD): BOOL: kernel32

	DEF_DLL VirtualAlloc(DWORD, DWORD, DWORD, DWORD): DWORD: kernel32
	DEF_DLL VirtualFree(DWORD, DWORD, DWORD): bool: kernel32
	DEF_DLL GetProcessHeap(): DWORD: kernel32
	DEF_DLL HeapAlloc(DWORD,DWORD,DWORD): DWORD: kernel32
	DEF_DLL HeapFree(DWORD,DWORD,DWORD): BOOL: kernel32
	DEF_DLL HeapReAlloc(DWORD,DWORD,DWORD,DWORD): DWORD: kernel32

	CONST DEF_NAME = "UwscMemMgr"
	CONST MNGR_NAME = "UwscMemoryManagerReserveAreaName"
	CONST DEF_SIZE = 8
	CONST HEAP_ALLOC_SIZE = 2048
	CONST VAR_HASH = VAR_ARRAY * 2	// 勝手定義

	HASHTBL _hs, _szs, _ms, _allocs, _sets
	DIM _alloc = NULL, _sz = 0, _heap = NULL, _mngr = FALSE
	DIM _dummy[0]

	PROCEDURE Dispose()
		WHILE LENGTH(_hs)
			Close(_hs[LENGTH(_hs)-1, HASH_KEY], FALSE)
		WEND
		WHILE LENGTH(_szs)
			Close(_szs[LENGTH(_szs)-1, HASH_KEY], FALSE)
		WEND
		WHILE LENGTH(_ms)
			Close(_ms[LENGTH(_ms)-1, HASH_VAL], FALSE)
		WEND
		WHILE LENGTH(_sets)
			Remove(_sets[0, HASH_KEY])
		WEND
		WHILE LENGTH(_allocs)
			Free(VAL(_allocs[0, HASH_KEY]))
		WEND
	FEND

	FUNCTION Open(sz=DEF_SIZE, name=DEF_NAME, local=TRUE, bCreate=TRUE)
		OpenMngr()
		name = GetName(name, local)
		DIM hMem = 0
		IFB _hs[name, HASH_EXISTS] THEN
			hMem = _hs[name]
		ELSEIF name <> NULL THEN
			hMem = OpenFileMappingW(2, FALSE, name)
			IF !hMem AND bCreate THEN hMem = CreateFileMappingW(-1, 0, 4, INT(sz / $100000000), sz MOD $100000000, name)
			IFB hMem <> 0 AND hMem <> _hs[name] THEN
				_hs[name] = hMem
				_szs[name] = sz
			ENDIF
		ENDIF
		RESULT = 0
		IF hMem THEN RESULT = MapViewOfFile(hMem, 2, 0, 0, sz)
		IF RESULT THEN _ms[RESULT] = name
		UpdateMngr()
	FEND

	PROCEDURE Close(name=DEF_NAME, local=TRUE, ptr=0)
		OpenMngr()
		name = GetName(name, local)
		DIM res, i = 0
		IFB ptr THEN
			UnmapViewOfFile(ptr)
			res = _ms[ptr, HASH_REMOVE]
			res = TRUE
			FOR i = 0 TO LENGTH(_ms) - 1
				IFB _ms[i, HASH_VAL] = name THEN
					res = FALSE
					BREAK
				ENDIF
			NEXT
		ELSEIF name <> NULL THEN
			WHILE (i < LENGTH(_ms))
				IFB _ms[i, HASH_VAL] = name THEN
					ptr = VAL(_ms[i, HASH_KEY])
					res = _ms[ptr, HASH_REMOVE]
					UnmapViewOfFile(ptr)
				ELSE
					i = i + 1
				ENDIF
			WEND
			res = TRUE
		ENDIF
		IFB res AND name <> NULL THEN
			IFB _hs[name, HASH_EXISTS] THEN
				CloseHandle(_hs[name])
				res = _hs[name, HASH_REMOVE]
			ENDIF
			IF _szs[name, HASH_EXISTS] THEN res = _szs[name, HASH_REMOVE]
		ENDIF
		UpdateMngr()
	FEND

	FUNCTION Size(ptr=0, name=DEF_NAME, local=TRUE)
		OpenMngr()
		IF ptr AND _ms[ptr, HASH_EXISTS] THEN name = _ms[ptr] ELSE name = GetName(name, local)
		RESULT = 0
		IF _szs[name, HASH_EXISTS] THEN RESULT = _szs[name]
		IF _allocs[ptr, HASH_EXISTS] THEN RESULT = _allocs[ptr]
	FEND

	FUNCTION GetName(name=DEF_NAME, local=TRUE)
		RESULT = name
		IF local AND name <> NULL THEN RESULT = name + STATUS(GETID(GET_THISUWSC_WIN), ST_PROCESS)
	FEND

	FUNCTION Read(ptr, bFree=TRUE, def=NULL, body=TRUE)
		RESULT = ReadEx(ptr, 0, _dummy, bFree, def, body)
	FEND
	FUNCTION ReadEx(ptr, var size, var hash[], bFree=TRUE, def=NULL, body=TRUE)
		// ptrにはSetされたものが入っている想定
		RESULT = def
		DIM type, i = 8, j = 0, s
		IFB ptr <> NULL AND ptr > 0 THEN
			DEF_DLL RtlMoveMemory(var DWORD,DWORD,DWORD): kernel32
			RtlMoveMemory(type, ptr, 4)
			RtlMoveMemory(size, ptr + 4, 4)
		ELSE
			EXIT
		ENDIF
		IFB body THEN
			SELECT type
			CASE VAR_EMPTY
				RESULT = EMPTY
			CASE VAR_NULL
				RESULT = NULL
			CASE VAR_INTEGER
				RESULT = 0
				DEF_DLL RtlMoveMemory(var int,DWORD,DWORD): kernel32
				RtlMoveMemory(RESULT, ptr + i, size - i)
				RESULT = VARTYPE(RESULT, type)
			CASE VAR_DOUBLE
				RESULT = 0
				DEF_DLL RtlMoveMemory(var double,DWORD,DWORD): kernel32
				RtlMoveMemory(RESULT, ptr + i, size - i)
			CASE VAR_BSTR
				RESULT = FORMAT(CHR(0), size - i)
				DEF_DLL RtlMoveMemory(var string,DWORD,DWORD): kernel32
				RtlMoveMemory(RESULT, ptr + i, size - i)
			CASE VAR_BOOLEAN, VAR_DWORD
				RESULT = 0
				DEF_DLL RtlMoveMemory(var DWORD,DWORD,DWORD): kernel32
				RtlMoveMemory(RESULT, ptr + i, size - i)
				RESULT = VARTYPE(RESULT, type)
			CASE VAR_HASH
				RESULT = VAR_HASH
				IFB VarTypeEx(hash) = VAR_HASH THEN
					DEF_DLL RtlMoveMemory(var DWORD,DWORD,DWORD): kernel32
					RtlMoveMemory(j, ptr + i, 4)
					i = i + 4
					DIM key, val
					FOR j = j TO 1 STEP -1
						key = ReadEx(ptr + i, s, _dummy, FALSE, def)
						i = i + s
						val = ReadEx(ptr + i, s, _dummy, FALSE, def)
						i = i + s
						hash[key] = val
					NEXT
				ENDIF
			DEFAULT
				IFB type >= VAR_ARRAY THEN
					s = 0
					DEF_DLL RtlMoveMemory(var DWORD,DWORD,DWORD): kernel32
					RtlMoveMemory(s, ptr + i, 4)
					i = i + 4
					RESULT = VarTypeEx(hash)
					IFB RESULT >= VAR_ARRAY AND (LENGTH(hash) >= s OR RESULT = VAR_HASH) THEN
						FOR j = 0 TO s - 1
							hash[j] = ReadEx(ptr + i, s, _dummy, FALSE, def)
							i = i + s
						NEXT
					ELSE
						RESULT = SAFEARRAY(0, s - 1)
						FOR j = 0 TO s - 1
							RESULT[j] = ReadEx(ptr + i, s, _dummy, FALSE, def)
							i = i + s
						NEXT
					ENDIF
				ENDIF
			SELEND
		ELSE
			RESULT = size
		ENDIF
		IF bFree THEN Free(ptr)
	FEND
	FUNCTION WriteP(val, ptr=NULL)
		// ptr は NULLなら確保して、ポインターを返す
		// NULL以外なら、CalcSize(val)の大きさがあるものとする
		DIM type = VarTypeExP(val), size = CalcSizeP(val), i = 8, j
		RESULT = size
		IFB RESULT > 0 THEN
			IFB ptr = NULL THEN
				ptr = Alloc(size)
				RESULT = ptr
			ENDIF
			DEF_DLL RtlMoveMemory(DWORD,var DWORD,DWORD): kernel32
			RtlMoveMemory(ptr, type, 4)
			RtlMoveMemory(ptr + 4, size, 4)
			SELECT type
			CASE VAR_EMPTY, VAR_NULL
				// nop
			CASE VAR_INTEGER
				DEF_DLL RtlMoveMemory(DWORD,var int,DWORD): kernel32
				RtlMoveMemory(ptr + i, val, size - i)
			CASE VAR_DOUBLE
				DEF_DLL RtlMoveMemory(DWORD,var double,DWORD): kernel32
				RtlMoveMemory(ptr + i, val, size - i)
			CASE VAR_BSTR
				DEF_DLL RtlMoveMemory(DWORD,var string,DWORD): kernel32
				RtlMoveMemory(ptr + i, val, size - i)
			CASE VAR_BOOLEAN, VAR_DWORD
				DEF_DLL RtlMoveMemory(DWORD,var DWORD,DWORD): kernel32
				RtlMoveMemory(ptr + i, val, size - i)
			DEFAULT
				IFB type >= VAR_ARRAY THEN
					DEF_DLL RtlMoveMemory(DWORD,var DWORD,DWORD): kernel32
					RtlMoveMemory(ptr + i, LENGTH(val), 4)
					i = i + 4
					FOR j = 0 TO LENGTH(val) - 1
						i = i + WriteP(val[j], ptr + i)
					NEXT
				ELSE
					RESULT = FALSE
				ENDIF
			SELEND
		ENDIF
	FEND
	FUNCTION Write(val[], ptr=NULL)
		// ptr は NULLなら確保して、ポインターを返す
		// NULL以外なら、CalcSize(val)の大きさがあるものとする
		DIM type = VarTypeEx(val), size = CalcSize(val), i = 8, j
		RESULT = size
		IFB RESULT > 0 THEN
			IFB ptr = NULL THEN
				ptr = Alloc(size)
				RESULT = ptr
			ENDIF
			DEF_DLL RtlMoveMemory(DWORD,var DWORD,DWORD): kernel32
			RtlMoveMemory(ptr, type, 4)
			RtlMoveMemory(ptr + 4, size, 4)
			SELECT type
			CASE VAR_HASH
				DEF_DLL RtlMoveMemory(DWORD,var DWORD,DWORD): kernel32
				RtlMoveMemory(ptr + i, LENGTH(val), 4)
				i = i + 4
				FOR j = 0 TO LENGTH(val) - 1
					i = i + WriteP(val[j, HASH_KEY], ptr + i)
					i = i + WriteP(val[j, HASH_VAL], ptr + i)
				NEXT
			DEFAULT
				IFB type >= VAR_ARRAY THEN
					DEF_DLL RtlMoveMemory(DWORD,var DWORD,DWORD): kernel32
					RtlMoveMemory(ptr + i, LENGTH(val), 4)
					i = i + 4
					FOR j = 0 TO LENGTH(val) - 1
						i = i + WriteP(val[j], ptr + i)
					NEXT
				ELSE
					i = WriteP(val, ptr)
					IFB i <> size THEN
						IF RESULT = ptr THEN Free(ptr)
						RESULT = FALSE
					ENDIF
				ENDIF
			SELEND
		ENDIF
	FEND
	FUNCTION CalcSizeP(val)
		RESULT = 8
		// 型は、VAR_EMPTY(0) VAR_NULL(1) VAR_DOUBLE(5) VAR_BSTR(8) VAR_BOOLEAN(11) VAR_ARRAY($2000)
		DIM type = VarTypeExP(val), i
		SELECT type
		CASE VAR_EMPTY, VAR_NULL
			// nop
		CASE VAR_DOUBLE
			RESULT = RESULT + 8
		CASE VAR_BSTR
			RESULT = RESULT + LENGTHB(val)
		CASE VAR_BOOLEAN, VAR_DWORD, VAR_INTEGER
			RESULT = RESULT + 4
		DEFAULT
			IFB type >= VAR_ARRAY THEN
				RESULT = RESULT + 4
				FOR i = 0 TO LENGTH(val) - 1
					type = CalcSizeP(val[i])
					IFB type < 0 THEN
						RESULT = type
						BREAK
					ENDIF
					RESULT = RESULT + type
				NEXT
			ELSE
				RESULT = -1
			ENDIF
		SELEND
	FEND
	FUNCTION CalcSize(val[])
		RESULT = 8
		// 型は、VAR_EMPTY(0) VAR_NULL(1) VAR_DOUBLE(5) VAR_BSTR(8) VAR_BOOLEAN(11) VAR_ARRAY($2000)
		DIM type = VarTypeEx(val), i
		SELECT type
		CASE VAR_HASH
			RESULT = RESULT + 4
			FOR i = 0 TO LENGTH(val) - 1
				type = CalcSizeP(val[i, HASH_KEY])
				IFB type < 0 THEN
					RESULT = type
					BREAK
				ENDIF
				RESULT = RESULT + type
				type = CalcSizeP(val[i, HASH_VAL])
				IFB type < 0 THEN
					RESULT = type
					BREAK
				ENDIF
				RESULT = RESULT + type
			NEXT
		DEFAULT
			IFB type >= VAR_ARRAY THEN
				RESULT = RESULT + 4
				FOR i = 0 TO LENGTH(val) - 1
					type = CalcSizeP(val[i])
					IFB type < 0 THEN
						RESULT = type
						BREAK
					ENDIF
					RESULT = RESULT + type
				NEXT
			ELSE
				RESULT = CalcSizeP(val)
			ENDIF
		SELEND
	FEND
	FUNCTION VarTypeExP(val)
		RESULT = VARTYPE(val)
		IFB RESULT = VAR_DOUBLE THEN
			IFB val = INT(val) THEN
				IF val >= 0 AND val < 4294967296 THEN RESULT = VAR_DWORD
				IF val < 0 AND val > -2147483649 THEN RESULT = VAR_INTEGER
			ENDIF
		ELSEIF RESULT = 72 OR RESULT = 256 THEN
			// http://www3.bigcosmic.com/board/s/board.cgi?id=umiumi&mode=all&no=2132&log=ON&cnt=18
			RESULT = VAR_BSTR
		ENDIF
	FEND
	FUNCTION VarTypeEx(val[])
		TRY
			RESULT = VarTypeExP(val)
		EXCEPT
			IFB POS("が定義されていません", TRY_ERRMSG) THEN
				RESULT = VAR_HASH
			ELSE
				RESULT = VAR_VARIANT + VAR_ARRAY
			ENDIF
		ENDTRY
	FEND

	FUNCTION Get(name, def=NULL)
		RESULT = GetEx(name, _dummy, def)
	FEND
	FUNCTION GetEx(name, var hash[], def=NULL)
		OpenMngr()
		RESULT = def
		IFB _sets[name, HASH_EXISTS] THEN
			DIM size
			RESULT = ReadEx(_sets[name], size, hash, FALSE, def)
		ENDIF
	FEND
	FUNCTION SetP(name, val)
		OpenMngr()
		_mngr = TRUE
		IF _sets[name, HASH_EXISTS] THEN Free(_sets[name])
		_sets[name] = WriteP(val)
		RESULT = (_sets[name] > 0)
		_mngr = FALSE
		UpdateMngr()
	FEND
	FUNCTION Set(name, val[])
		OpenMngr()
		_mngr = TRUE
		IF _sets[name, HASH_EXISTS] THEN Free(_sets[name])
		_sets[name] = Write(val)
		RESULT = (_sets[name] > 0)
		_mngr = FALSE
		UpdateMngr()
	FEND
	FUNCTION Remove(name)
		OpenMngr()
		_mngr = TRUE
		RESULT = FALSE
		IFB _sets[name, HASH_EXISTS] THEN
			DIM ptr = _sets[name]
			RESULT = _sets[name, HASH_REMOVE]
			Free(ptr)
		ENDIF
		_mngr = FALSE
		UpdateMngr()
	FEND

	FUNCTION OpenMngr()
		RESULT = FALSE
		IF _mngr OR _alloc <> NULL THEN EXIT
		_mngr = TRUE
		DIM ptr = Open(DEF_SIZE, MNGR_NAME, TRUE, FALSE)
		_alloc = 0
		IFB ptr THEN
			RESULT = TRUE
			DEF_DLL RtlMoveMemory(var DWORD,DWORD,DWORD): kernel32
			RtlMoveMemory(_alloc, ptr, 4)
			RtlMoveMemory(_sz, ptr + 4, 4)
			Close(MNGR_NAME, TRUE)
		ENDIF
		DIM res, i = 0, size
		IFB _alloc <> NULL AND _alloc <> 0 THEN
			res = ReadEx(_alloc + i, size, _hs, FALSE)
			IF res = VAR_HASH THEN i = i + size
			res = ReadEx(_alloc + i, size, _szs, FALSE)
			IF res = VAR_HASH THEN i = i + size
			res = ReadEx(_alloc + i, size, _ms, FALSE)
			IF res = VAR_HASH THEN i = i + size
			res = ReadEx(_alloc + i, size, _allocs, FALSE)
			IF res = VAR_HASH THEN i = i + size
			res = ReadEx(_alloc + i, size, _sets, FALSE)
			IF res = VAR_HASH THEN i = i + size
		ENDIF
		_mngr = FALSE
	FEND
	PROCEDURE UpdateMngr()
		IF _mngr OR _alloc = NULL THEN EXIT
		_mngr = TRUE
		DIM hs = CalcSize(_hs), szs = CalcSize(_szs), ms = CalcSize(_ms), as = CalcSize(_allocs)
		DIM sz = hs + szs + ms + as + CalcSize(_sets), bUp = FALSE
		IFB _sz < sz THEN
			VirtualFree(_alloc, 0, $8000)
			_alloc = VirtualAlloc(0, sz, $1000, 4)
			_sz = sz
			bUp = TRUE
		ENDIF
		Write(_hs, _alloc)
		Write(_szs, _alloc + hs)
		Write(_ms, _alloc + hs + szs)
		Write(_allocs, _alloc + hs + szs + ms)
		Write(_sets, _alloc + hs + szs + ms + as)
		IFB bUp THEN
			DIM ptr = Open(DEF_SIZE, MNGR_NAME, TRUE, FALSE)
			IFB !ptr THEN
				ptr = Open(DEF_SIZE, MNGR_NAME, TRUE)
				bUp = FALSE
			ENDIF
			IFB ptr THEN
				DEF_DLL RtlMoveMemory(DWORD,var DWORD,DWORD): kernel32
				RtlMoveMemory(ptr, _alloc, 4)
				RtlMoveMemory(ptr + 4, _sz, 4)
				IF bUp THEN Close(MNGR_NAME, TRUE)
			ENDIF
		ENDIF
		_mngr = FALSE
	FEND

	FUNCTION Alloc(size, ptr=NULL, pro=4, hf=8)	// 4:PAGE_READWRITE 8:HEAP_ZERO_MEMORY
		RESULT = NULL
		OpenMngr()
		IF _heap = NULL THEN _heap = GetProcessHeap()
		DIM sz = HEAP_ALLOC_SIZE
		IFB ptr <> NULL THEN
			IF _allocs[ptr, HASH_EXISTS] THEN sz = _allocs[ptr]
			IFB size < HEAP_ALLOC_SIZE AND sz < HEAP_ALLOC_SIZE THEN
				RESULT = _allocs[ptr, HASH_REMOVE]
				RESULT = HeapReAlloc(_heap, hf, ptr, size)
				ptr = NULL
			ENDIF
		ENDIF
		IFB RESULT = NULL THEN
			IFB size < HEAP_ALLOC_SIZE THEN
				RESULT = HeapAlloc(_heap, hf, size)
			ELSE
				RESULT = VirtualAlloc(0, size, $1000, pro)	// $1000:MEM_COMMIT
			ENDIF
		ENDIF
		IF RESULT THEN _allocs[RESULT] = size
		IFB ptr <> NULL THEN
			DIM res = _allocs[ptr, HASH_REMOVE]
			IF size < sz THEN sz = size
			DEF_DLL RtlMoveMemory(DWORD, DWORD, DWORD): kernel32
			RtlMoveMemory(RESULT, ptr, sz)
			Free(ptr)
		ENDIF
		UpdateMngr()
	FEND
	FUNCTION Free(ptr)
		OpenMngr()
		IF _heap = NULL THEN _heap = GetProcessHeap()
		DIM sz = HEAP_ALLOC_SIZE
		IFB _allocs[ptr, HASH_EXISTS] THEN
			sz = _allocs[ptr]
			RESULT = _allocs[ptr, HASH_REMOVE]
		ENDIF
		IFB sz < HEAP_ALLOC_SIZE THEN
			RESULT = HeapFree(_heap, 0, VAL(ptr))
		ELSE
			RESULT = VirtualFree(ptr, 0, $8000)
		ENDIF
		UpdateMngr()
	FEND

ENDMODULE

お試しは、サンプルコードがあるので、そのまま実行してください。
INPUTに何か入れると、それをキーとし次のINPUTを値として、連想配列に格納します。
Disposeしなければ、次の起動で読み込みが行われます。
Disposeすると破棄されるので、読み込むものがなくなります。

使い方

例えば、modeという変数をプロセス起動中保持したい場合(初期値:258)
スクリプトの最初で

	mode = MemMgr.Get("mode", 258)

とする。
保存済みであれば保存値が、保存値がなければ258が返ります。
で、modeを保持したくなったら

	MemMgr.Set("mode", mode)

Setしておけば、次にGetした値が更新されます。
modeが不要になったら

	MemMgr.Remove("mode")

しといてください。(通常必要なし)


ただし、値を直接Setしたい場合は、SetPしてください。

	MemMgr.SetP("mode", 111)

Setではエラーになります。(連想配列のキーや値、配列の中身も)


配列や連想配列の場合もSetが使えます。
ただ、取得の際は、GetExを使います。

	MemMgr.GetEx("mode", hash)

取得できたか知りたい場合は、リターンがNULL(または第三引数)でないことを確認してください。

雑感

プロセス起動中に覚えておきたいことは、これでOK。
OS起動中ずっと覚えておきたいことは、少しならグローバルアトムか。
(大量なら、グローバルアトム+ファイルか)
電源切っても覚えておきたいなら、ファイルに書いてください。


モジュール変数はDIM宣言だと、他スクリプトからは見えないけど、stuncloudさんや私が作ったInterpreterからだと見えます!
UWSCにPrivateは存在しないのです。
InterpreterでCALLしとけば、モジュールの動作確認が楽チンだったりします!


連想配列や配列を判定するVarTypeExでダサいことしてます。
UWSCの仕様が変わったら、変更が必要になりそうです。
また、VARTYPEの戻り値はヘルプにない文字列の定義があるようです。


p for primitive
関数の引数に[]をつけると、変数ならなんでも受け付けるけど、即値等がダメ。
そこがなんとかなれば、Pのついた関数は統合可能。