おのれwow64て久しぶりになった話

UWSCの話です

win7でSP1当たってるかどうか確かめるのにレジストリ見てたんですけどね

with createoleobj("WScript.Shell")
    CSDVersion = .RegRead("HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CSDVersion")
endwith
msgbox(CSDVersion)

としてたら x64 環境でエラーになってだめだったんですね
Wow6432Node のほう見てたっぽい
そっちには CSDVersion がない

WMIで解決する
なんか WbemScripting.SWbemNamedValueSet というやつです
それで __ProviderArchitecture が64だよって教えてやるとリダイレクトしない

key    = $80000002
subkey = "SOFTWARE\Microsoft\Windows NT\CurrentVersion\"
name   = "CSDVersion"
if kindofos(TRUE) then
    type = 64
else
    type = 32
endif
CSDVersion = EMPTY

valueSet = CreateOleObj("WbemScripting.SWbemNamedValueSet")
valueSet.Add("__ProviderArchitecture", type)
locator = CreateOleObj("Wbemscripting.SWbemLocator")
service = locator.ConnectServer("", "root\default",,,,,,valueSet)
stdRegProv = service.Get("StdRegProv")
if stdRegProv.GetStringValue(key, subkey, name, var CSDVersion) = 0 then
    msgbox(CSDVersion)
endif

SWbemNamedValueSet 渡すのにもっとこう気持ち悪くない方法ないんじゃろか…

最近はUWSCの終息ムードがありつらいですね
そんでUWSCの代替としてのAutoIt的な記事を書こうかと思ってたんだけど
どうやらAutoItをぜんぜん触ってないから書けなさそうというフィールがあります
だってそれUWSCで書けばよくねってなるからね
なるんだよ

広告
カテゴリー: UWSC

UWSCでタスクトレイアイコンを表示する (COM版)

お仕事で使ってるツールで常駐型のがあるんですけどね
タスクトレイアイコンが出せるといろいろ便利だろうなーてことで

UWSCでタスクトレイ常駐する – じゅんじゅんのきまぐれ

を使ってアイコン出してたんですね
不自由なく使えててゴキゲンだったんですけどある日事件が起こる
F-SecureのアンチウイルスソフトにUWSCから呼んだPowerShellが…

ブロックされてしまった!

例外登録とかもできずどうしようもなかったのだ…
ぼくは悲しみに暮れた

のでCOMで使えるやつをつくった
createoleobj() できるやつだ!
C#で書きました

サンプルコードは一番下にあるよ

どういうのか

任意のアイコンを表示させられます
oleevent() でアイコンをクリックしたときのイベントを処理できます
アイコンは途中で変更できます

大変だったこと

C#ほとんど書かないのでまず書き方を忘れていたよね
COM用のDLLはむかーしちょっとしたの書いたことがあったので
それを掘り起こしつつ地味に書き進めました…が

動かない!想定どおりに動かない!
とくにイベント周り!

公開するインタフェースの各メンバに DispId 書いてなかったんですよね最初
その状態でもプロパティにアクセスしたりメソッド叩いたりはできたので油断したよね
イベント発火するとイベントハンドラが未定義だ!って怒られるわけよ
oleevent() は成功してるのに!
(ちなみにこの状態でもPowerShellから呼ぶと成功するので混乱した)

んで、そこを乗り越えたら簡単なイベントはちゃんと扱えるようになりました
ヨカッタネ☆ …て思うじゃん?
今度はもっと深みにはまった…
アイコンをクリックした時のイベントハンドラからデリゲートを呼ぶようにしてたのさ
(そのデリゲートが公開してるイベントなわけね)
するとどうだ、イベント発火時にUWSCがクラッシュするではないか
つらい

これが全然原因わからなくって…
いやなんかうすうすSTAとかそういうのじゃないかなとか考えてはいたんですけど
そうだとしても実装方法がさっぱりわかんなかったのだ…
ぐぐっても出てこないというかどうやってぐぐればいいかもわかんなかった系でね
RegAsmすると ThreadingModelBoth になってるし…
なにこれ Apartment に書き換えればいいの…?
関係ねぇよ!! (関係なかった

つらい

まぁでも解決しました
結論を言うとイベントハンドラを追加してたメソッドに
[STAThread]
付けただけなんですけどね

いやだってさーこれさーexe書くときにエントリポイントにつけるもんです!
って解説されてるわけでさー…じゃあDLLのときはどうすんのとかわかんないわけよ!
ダメ元で書いたけど当初他の理由でうまくいかなかったりしててね…
しばらくこれが正解だって気付かなかったんだよね

まぁなんかそういうことしながらなんとかやりました
ほめて

使う時の心得

RegAsmしなきゃなので管理者特権が必要です
スクリプトから登録もしたいという場合は注意してね

RegAsmしなくて使う方法もあるんだけどそれはそれでいろいろめんどくさい
reg free com とかでぐぐると出てくるよ

サンプルコード

エラーハンドリング全くしてないのでなんかしくじると落ちます

カテゴリー: UWSC

UWSC5.3.0出てましたね

変更点
http://www3.rocketbbs.com/13/bbs.cgi?id=umiumi&mode=pickup&no=3740

今回はSublime Text用のUWSCパッケージに手を入れる必要がなくてヨカッタネ

いやー、正直やりたくねーめんどくせーとか思ったのでありがたい
…やりたくねーめんどくせーと思ってしまった

Visual Studio Code向けUWSC拡張も結局完成させずに放置してたら完全にやる気を失ってしまった
(ちなみにそれはVSCode正式版が出る前に着手したものなんで当時と仕様変わっててちゃんとうごかねーんじゃねーかなってことでなかったことにしました、今)

あんなに好きだったUWSCに対してでもモチベートできない
UWSCに限らないんだが、まぁ

カテゴリー: UWSC

UWSCのFormでX-UA-CompatibleがIE=9以上だとoleevent()が使えないのをどうにかする

UWSCのFormでこう、ちょっとしたUIを作りたいときに

<meta http-equiv="X-UA-Compatible" content="IE=edge">

などと書きたいわけですよ、つらいから
だけどそうすると oleevent が使えなくなっちゃうんですね
そんなイベントはねー!みたいなエラーになってしまう
こまる、つらい

じゃあもう自分でやるしかないな!

f = createform("about:blank", "test", TRUE)
f.document.write(html)

while f.visible
    select f.document.parentWindow.value
        case 1,2,3,4
            print f.document.parentWindow.value + " が押されました"
            f.document.parentWindow.value = 0
    selend
    sleep(0.01)
wend

textblock html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
<script>
value = 0;
var hoge = function(n)
{
    value = n;
}
</script>
</head>
<body>
    <input type="button" name="1" value="1" onclick="hoge(this.value);">
    <input type="button" name="2" value="2" onclick="hoge(this.value);">
    <input type="button" name="3" value="3" onclick="hoge(this.value);">
    <input type="button" name="4" value="4" onclick="hoge(this.value);">
</body>
</html>
endtextblock

はい

script タグ内で宣言してる

value = 0;

の値をForm内でjs使って書き換えるようにして、それをUWSC側から監視してしまおうというわけです
これは要するに window.value なのでUWSC側からは f.document.parentWindow.value で見える

あとはおなじみFormの visible 監視ループの中でその変数の値を見てそれに応じて処理を行う、というすんぽーよ
毎回その変数を初期化するのを忘れずにね
取りこぼしがあるかもしれないからあんまり激しいのはNGよ!

とまぁこんな感じでごまかしながらやってんだけど oleevent() こうするとできるよーみたいな情報ある人は教えてください…

カテゴリー: UWSC

UWSCで配列リテラルっぽいなにか

UWSCって配列リテラルないじゃないですか
なので配列リテラルっぽい気持ちになれる関数を作る

function Array(arg0 = NoValue, arg1 = NoValue, arg2 = NoValue, arg3 = NoValue, arg4 = NoValue, arg5 = NoValue, arg6 = NoValue, arg7 = NoValue, arg8 = NoValue, arg9 = NoValue, arg10 = NoValue, arg11 = NoValue, arg12 = NoValue, arg13 = NoValue, arg14 = NoValue, arg15 = NoValue, arg16 = NoValue, arg17 = NoValue, arg18 = NoValue, arg19 = NoValue, arg20 = NoValue, arg21 = NoValue, arg22 = NoValue, arg23 = NoValue, arg24 = NoValue, arg25 = NoValue, arg26 = NoValue, arg27 = NoValue, arg28 = NoValue, arg29 = NoValue, arg30 = NoValue, arg31 = NoValue)
    const NoValue = null + null + null
    tmp = safearray(0, 31)
    i = 0
    while TRUE
        value = eval("arg" + i)
        if value = NoValue then
            break
        else
            tmp[i] = value
            i = i + 1
        endif
    wend
    result = slice(tmp, 0, i - 1)
fend

function @(arg0 = NoValue, arg1 = NoValue, arg2 = NoValue, arg3 = NoValue, arg4 = NoValue, arg5 = NoValue, arg6 = NoValue, arg7 = NoValue, arg8 = NoValue, arg9 = NoValue, arg10 = NoValue, arg11 = NoValue, arg12 = NoValue, arg13 = NoValue, arg14 = NoValue, arg15 = NoValue, arg16 = NoValue, arg17 = NoValue, arg18 = NoValue, arg19 = NoValue, arg20 = NoValue, arg21 = NoValue, arg22 = NoValue, arg23 = NoValue, arg24 = NoValue, arg25 = NoValue, arg26 = NoValue, arg27 = NoValue, arg28 = NoValue, arg29 = NoValue, arg30 = NoValue, arg31 = NoValue)
    result = Array(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16, arg17, arg18, arg19, arg20, arg21, arg22, arg23, arg24, arg25, arg26, arg27, arg28, arg29, arg30, arg31)
fend

はい

for item in Array("foo", "bar", "baz")
    print item
next

for item in @(1,3,5,7,9)
    print item
next

こんな感じで使えます
Array() 関数に適当に引数渡せばSafeArrayの要素として返ってくるってすんぽーよ
Array() だと書くのめんどくさいから@() という別名も用意、PowerShell使いだからすごく配列に見える

最大32個の要素を持つ配列が作れます
32なのは slctbox に倣ってるからです、とりあえず使う分には十分でしょう
(可変長引数があればね…こんなことしなくてもいいんだけど…)

const NoValue はそれぞれの引数の初期値として定義しています
おそらく絶対に渡されないであろう値ならなんでも良いんですがとりあえず null を3つ足しました
えるしってるか null は足せる(というか文字列結合してる、lengthは3になるぞ)
関数内で宣言してても const のスコープはグローバルです
なぜそんなことしてるのかと言うと、引数の初期値に変数を渡す場合はその変数がグローバルスコープじゃないとだめだからなんですね
関数内に書いてるのは外に書くと邪魔かなって気がしたからですが特に深い意味はないです

カテゴリー: UWSC

SLCT_LSTをダブルクリックで選択確定する

SLCT_LSTをダブルクリックで選択確定する

2たんで見た

SLCTBOXのSLCT_LSTで定型文字列を選べるスクリプト組んだんだけど、
マウス直下のウィンドウに貼り付ける手抜き処理なんで、OKボタンを
使わなくてもできるようにしたい。

今のトコEnterキーでやっつけてるのだけど、それをダブルクリックで
実行できるようにもしたい。

良いやり方あるかな?

実際のとここのケースなら popupmenu のが楽なのは間違いないんだけど、 SLCT_LST でダブルクリックして選びたいって思ったこと何度かあるのでじゃあやってみようぜっていう

そいや過去にUWSCでダブルクリックを検知して任意の関数を実行するモジュール書いてたっけ、これ使おう

call DoubleClick.uws

list = split("hoge fuga piyo")

DoubleClickEvent.SetEvent("ClickOk") // ダブルクリック検知開始
ret = slctbox(SLCT_LST or SLCT_STR, 0, "えらぶ", list)
DoubleClickEvent.RemoveEvent()       // ダブルクリック検知終了

if ret = -1 then exit
msgbox("選んだのは " + token("<#TAB>", ret))

procedure ClickOk
    id = getid(GET_ACTIVE_WIN)
    if status(id, ST_CLASS) = "TUslctBox_uwsc" then
        clkitem(id, "OK")
    endif
fend

はい

DoubleClickEvent モジュールについては元記事見てもらうとして
ダブルクリックを検知したら ClickOk() 関数が呼ばれます
こいつは単純にダブルクリックが検知されたときに slctbox がアクティブならOKを押すってもの
ダブルクリックされたときは間違いなくアクティブだろうというすんぽーです
ちなみにボタン以外がダブルクリックされても反応するけどそこはまぁ、紳士協定です
ちゃんとボタンだけで反応するのもできなくもないとは思うので、その場合は ClickOk() で頑張ります
僕は頑張らないが

あと、 token 使ってるのは複数選択対策です
うっかり複数選択されても一番上のだけ値を返すよっていう
僕はこの小細工をよくやる

実際何度か試してみたけどわりとゴキゲンに動作しますね
いいんじゃないかしら!
まぁきっと popupmenu 使うけども!

カテゴリー: UWSC

UWSCで色選択ダイアログを使う

balloon (fukidasi) 関数なんかに渡す色を取得できる

戻り値はbool、ダイアログのOK押したかキャンセルしたかがわかる
第一引数のcolorには色のBGR値を入れる
第二引数は色選択ダイアログの親ウィンドウIDを入れる
例えば getid(GET_FORM_WIN) とか入れるとフォームが親になる

サンプルスクリプトでは吹き出しの色を選んで変更できるよ

カテゴリー: UWSC