UWSCのpowershell関数をスピードアップする

以前、powershellからUWSCを制御する方法を書いた。
PowershellとUWSCの連携 - じゅんじゅんのきまぐれ
しかし、そうじゃないだろ、という気がしたので、再度考えた。


問題は、powershell関数のイニシャルコストが高いこと。
powershell関数を起動させっぱなしにすればいいじゃない!



スクリプト

psh.uws

OPTION EXPLICIT

IFB GET_UWSC_NAME = "psh.uws" THEN
	STOPFORM(FALSE)
	psh.Create()

	DIM code = ""
	WHILE code <> EMPTY
		PRINT psh.Run(code)
		code = INPUT("?")
	WEND

	psh.Dispose()
	STOPFORM(TRUE)
ENDIF


MODULE psh

	DIM _ie, _from, _to

	TEXTBLOCK _psh_code
		$u2p = New-Object PSObject
		$u2p | Add-Member NoteProperty "ie" (New-Object -ComObject InternetExplorer.Application)
		$u2p.ie.Navigate("about:blank")
		#$u2p.ie.Visible = $true
		$u2p | Add-Member NoteProperty "to" $u2p.ie.Document.createElement("input")
		$u2p.to.type = "hidden"
		$u2p.to.name = "p2u"
		[VOID]$u2p.ie.Document.body.appendChild($u2p.to)
		$u2p | Add-Member NoteProperty "from" $u2p.ie.Document.createElement("input")
		$u2p.from.type = "hidden"
		$u2p.from.name = "u2p"
		[VOID]$u2p.ie.Document.body.appendChild($u2p.from)
		$u2p.ie.Document.title = "Powershell uwsc bridge"
		try {
			while($true) {
				if(($u2p.from.value.length -gt 0) -and ($u2p.to.value.length -lt 1)) {
					try {
						$u2p.to.value = iex $u2p.from.value
					} catch {
						$u2p.to.value = "Invoke error. " + $u2p.from.value + "`n" + ($Error[0] | Out-String)
					}
					$u2p.from.value = ""
				} else {
					Start-Sleep -Milliseconds 100
				}
			}
		} catch {
			Write-Host "Exit."
		}
	ENDTEXTBLOCK

	PROCEDURE Create(disp=FALSE)
		DIM code = _psh_code
		IF disp THEN code = REPLACE(_psh_code, "#", "")
		POWERSHELL(code, TRUE, disp)
		_ie = 0
		WHILE _ie = 0
			COM_ERR_IGN
				_ie = GETACTIVEOLEOBJ("InternetExplorer.Application", "Powershell uwsc bridge")
				IF COM_ERR_FLG THEN _ie = 0
			COM_ERR_RET
		WEND
		IFB GetOleItem(_ie.document.body.children) > 1 THEN
			_from = ALL_OLE_ITEM[0]
			_to = ALL_OLE_ITEM[1]
			_from.value = ""
			_to.value = ""
		ENDIF
	FEND

	PROCEDURE Dispose()
		_to.value = "exit"
		SLEEP(0.5)
		_ie.Quit()
	FEND

	FUNCTION Run(code)
		RESULT = EMPTY
		IFB LENGTH(_from.value) THEN
			PRINT "Unknown message. " + _from.value
			_from.value = ""
		ENDIF
		IF LENGTH(code) = 0 THEN EXIT
		WHILE LENGTH(_to.value); SLEEP(0.1); WEND
		_to.value = code
		WHILE LENGTH(_to.value); SLEEP(0.1); WEND
		RESULT = _from.value
		_from.value = ""
	FEND

ENDMODULE

使い方

単体の場合は、実行するとInputボックスが出ます。
powershellスクリプトを入力すると、結果がPRINTされます。
IE経由で、文字列を受け渡しています。
Inputボックスにキャンセルすると、終了します。


「dir」とか入れてオブジェクトが文字列変換されて見難い場合、「dir | out-string」とかが良いです。


実際には、モジュールとしてCALLして使うかと思います。
Scriptの最初でpsh.Createして、最後にpsh.Disposeする。
で、powershell関数を呼んでいたところを、psh.Runにすれば多分OK。

解説

、、、するほどのことは、ないですね。
uwsc <-> ie <-> powershellとなっています。
psh.Createで、powershellを呼び出しieを作る。
psh.Disposeで、powershellを終了させieを閉じる。
psh.Disposeしないで終了してしまうと、ゾンビが残るので要注意。


ゾンビがいると、以降の動作が正常に行くか不明です。
(GetActiveOleObjでゾンビを取得してしまうと、連携できなくなる)
ieのタイトルに時間情報とかを付加するとか、UWSCで取得後タイトルを変えるとかすれば、問題ないかもしれないけど、ま、ゾンビが残ることに変わりはないので放置。
STOPFORMされてしまうと、ゾンビが残るので、STOPFORMは消してます。

雑感

インターネットの片隅で、UWSCで遊ぶ。
uwsc getlasterror」でGoogle先生に聞くと、上位を制圧しているのが嬉しい。
、、、とはいえ、できないのにDEF_DLLしているモジュールは恥ずかしいかもしれない。
気が向いたら修正しよう。
しかし、2chUWSCスレッドにURLが貼られているとは、、、意外と見てる人いるのね。
ま、Win32APIをUWSCから呼ぼうとする変態が少ないということか。


UWSCにできないことはない。
妥協が必要なことはあるかもしれないけど。
できないのは、、、己の技術不足。
それを何とかするのが楽しい、というところ。


今回は、powershell連携を改善してみました。
え?文字列になってしまうのをなんとかできないか?
ScriptControlを使うとかすれば、JScriptのオブジェクトが扱えると思うけど、面倒。
ま、それくらいはやりたい人ががんばってみてよ。(私の興味の範疇外)
問題は、powershellJScriptオブジェクトを上手く扱えないこと。


ふむ、UWSCの強みは、DLLが呼べてJScriptオブジェクトが透過的に扱えて、COM操作ができることか。