UWSCでグローバルアトムを使う

前回の実行状態を保持したい場合、INIファイルを使うのが一般的ですね。
でも、POFF(P_UWSC_REEXEC,TRUE)で処理を続行するのにもINIを使うのは、なんかくやしくないですか?


そこで登場するのが、Global Atom



まあ、制約多くて使いにくいですが

GlobalAtom、もうちょっとなんとかならなかったのか、という残念な仕様です。
使いにくい。

  • NULL終端最大255バイト文字列が可能できるだけ
  • キーという概念はない!(格納するとその時空いてた番号をくれる)
  • 電源切ると消える


ま、電源切ると消えるのは良い点でもありますが、キーがないのは使えない。
ということで、全検索してキーを作りだすことにしました。
Atom.uws

OPTION EXPLICIT

IFB GET_UWSC_NAME = "Atom.uws" THEN
	DIM loop = TRUE, key, buf
	WHILE loop
		DIM cmd = SLCTBOX(SLCT_STR, 0, "コマンドを選択してください", "Find", "Update", "Delete", "Dump")
		IFB cmd = "Dump" THEN
			Atom.Dump()
		ELSEIF cmd = -1 THEN
			loop = FALSE
		ELSE
			key = INPUT("キーを入力してください", "テストAtomキー")
			IFB cmd = "Delete" THEN
				IFB Atom.Delete(key) = 0 THEN
					PRINT cmd + " 成功"
				ELSE
					PRINT cmd + " 失敗"
				ENDIF
			ELSE
				buf = Atom.Find(key)
				IFB cmd = "Update" THEN
					IFB Atom.Update(key, INPUT("更新内容を入力してください", buf)) THEN
						PRINT cmd + " 成功"
					ELSE
						PRINT cmd + " 失敗"
					ENDIF
				ELSE
					PRINT cmd + " : " + buf
				ENDIF
			ENDIF
		ENDIF
	WEND
ENDIF


MODULE Atom
	CONST _BUF_SIZE = 256
	CONST _BASE_NAME = "_juneUwscGAtom:"
	CONST _SEP = ":"
	CONST _MIN_ATOM_NO = $C001
	CONST _MAX_ATOM_NO = $FFFF

	DIM _top, _baseLen

	DEF_DLL GlobalFindAtomW(wstring): WORD: kernel32
	DEF_DLL GlobalAddAtomW(wstring): WORD: kernel32
	DEF_DLL GlobalGetAtomNameW(WORD,var wstring,int): DWORD: kernel32
	DEF_DLL GlobalDeleteAtom(WORD): WORD: kernel32

	PROCEDURE Atom
		_baseLen = LENGTH(_BASE_NAME)
		//DIM buf, ret
		//FOR _top = _MIN_ATOM_NO TO _MAX_ATOM_NO
		//	buf = FORMAT(CHR(0), _baseLen)
		//	ret = GlobalGetAtomNameW(_top, buf, _baseLen)
		//	IF buf = _BASE_NAME OR ret = 0 THEN BREAK
		//NEXT
		_top = _MIN_ATOM_NO
		_baseLen = _baseLen + 1
	FEND

	FUNCTION Get(no)
		RESULT = EMPTY
		DIM len = 0, buf, ret = len
		WHILE ret > len - 2
			len = len + _BUF_SIZE
			buf = FORMAT(CHR(0), len)
			ret = GlobalGetAtomNameW(no, buf, len)
		WEND
		IF ret > 0 THEN RESULT = buf
	FEND

	FUNCTION FindNo(name)
		RESULT = 0
		DIM len = LENGTH(name) + _baseLen, buf, i
		DIM tar = _BASE_NAME + name + _SEP, ret
		FOR i = _top TO _MAX_ATOM_NO
			buf = FORMAT(CHR(0), len)
			ret = GlobalGetAtomNameW(i, buf, len)
			IFB buf = tar THEN
				RESULT = i
				BREAK
			ELSEIF ret = 0 THEN
				BREAK
			ENDIF
		NEXT
	FEND
	FUNCTION Find(name)
		DIM no = FindNo(name)
		IFB no = 0 THEN
			RESULT = EMPTY
		ELSE
			RESULT = COPY(Get(no), LENGTH(name) + _baseLen + 1)
		ENDIF
	FEND

	FUNCTION Update(name, data)
		DIM no = FindNo(name)
		IF no > 0 THEN GlobalDeleteAtom(no)
		RESULT = GlobalAddAtomW(_BASE_NAME + name + _SEP + data)
	FEND

	FUNCTION Delete(name)
		// 0が成功。失敗はAtom
		RESULT = GlobalDeleteAtom(FindNo(name))
	FEND

	PROCEDURE Dump(t=ERR_VALUE)
		DIM i, buf
		IF t = ERR_VALUE THEN t = _top
		PRINT "Dump start " + t
		FOR i = t TO _MAX_ATOM_NO
			buf = Get(i)
			IFB LENGTH(buf) THEN
				PRINT i + ":" + buf
			ELSE
				BREAK
			ENDIF
		NEXT
	FEND

ENDMODULE

使い方

Atom.uwsを実行してもわかるかもしれませんが、再起動絡みですとだいたいこんな想定です。

CALL Atom

DIM key = "テストスクリプト用のキー"
SELECT Atom.Find(key)
	CASE "再起動後"
		Atom.Delete(key)

		MSGBOX("メモリクリアされてすっきりなUWSC")

	DEFAULT

		MSGBOX("実行されました。いろいろ処理してメモリクリアしたくなる")

		Atom.Update(key, "再起動後")
		POFF(P_UWSC_REEXEC, TRUE)
SELEND


もしくは、こんなスクリプト間連携にも使えます。
両方のスクリプトを実行してください。
こっちを実行すると、FUKIDASIが出ます。

CALL Atom

DIM key = "連携キー", val = EMPTY
FUKIDASI("連携相手待ち")
WHILE val = EMPTY
	SLEEP(0.1)
	val = Atom.Find(key)
WEND
Atom.Delete(key)
FUKIDASI()
MSGBOX("「"+val+"」って言われた〜")


んで、こっちで入力したメッセージが、上のスクリプトに伝わるしかけ。

CALL Atom

DIM key = "連携キー"
Atom.Update(key, INPUT("連携相手にメッセージをどうぞ"))

言うまでもないですが、キー名は任意です。
両方のスクリプトで同じであればよいのです。