Dies Aliquanti

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

AutoItで,アプリの多重起動の制御

Windows上のプログラムで複数起動を禁止したい場面が時々あります.今回は「禁止」ではなく,他のインスタンスが終了するまで,「待つ」機能が必要になりました.
 以前,よく使っていたUWSCでは,こんな記事もあります.こういう処理は本来,OSが用意するMutexを使うのが,「正道」でしょうが,件の記事は,UWSCの機能の範囲で実装しようとしています.

AutoIt(v3.3.0)では,どうするのか調べてみたら,FAQの中に,ユーザー定義関数の_Singleton()という関数を使え,と書いてあります._Singleton()関数のサンプルコードには,ほかのインスタンスが起動していたらメッセージボックスを出して終了する,という例が出ています.
 今回,作りたいのは,ほかのインスタンスが起動していたら,それが終了するまで待って動作する機能です._Singleton()を定期的に(ダサい)呼び出して,0(=失敗)以外が帰ってくるまで待ち続けるようにしましたが,うまくいきません.orz

 _Singleton()関数のソースコードを見てみると,Win32.DLLのCreateMutexを使っているようです.Googleとかで調べてみると,CreateMutexと組み合わせて,待つためにはWin32.DLLのなかのWaitForSingleObject()とかを使うようですが,このような関数はAutoItの中にはありません.そもそも_Singleton()関数はMutexの獲得に失敗したときは0を返してしまうので,その先どうしようもありません. orz

 どう考えても_Singleton()関数の仕様がヘンだと思うのですが,ソースコードをもう一度見てみると,ちょっと直せば何とかなりそうな気がしてきたので一箇所修整して,Mutexの獲得が出来た/出来ないにかかわらず,Mutxのhandleを返すことにしました。Mutexの獲得に関しては,@Errorシステム変数を通じて情報を得ることにします.
 また,Wind32.DLLのWaitForSingleObject()を呼び出すために関数Block()も作ります.DLLの呼び出しもAutoItで試すのは初めてですが,出来てしまえばなんということはありません.
 なお,Mutexの所有権を明示的に解放するためには,ReleaseMutex()という関数がWin32.DLLにありますが,プログラムの終了とともに自動的に解放されるようです.

 で,テストでつくったプログラムを最後にのせます.バッチファイル中から複数起動してもダイアログは一度に1つしかでません(5秒で自動的に消えます).

===================

#include <Misc.au3>
#include <WinAPI.au3>

; @Error code after Singleton()
Local Const $ERROR_ALREADY_EXISTS = 183
;return value of Block
Local Const $WAIT_OBJECT_0 = 0
Local Const $WAIT_ABANDONED = 0x80 ; 0x80 in hex
Local Const $WAIT_TIMEOUT = 0x102 ; 0x102 in hex
Local Const $WAIT_FAILED = -1 ; 0xFFFFFFFF  in hex


$handle = Singleton( @ScriptName,1)
If @error = $ERROR_ALREADY_EXISTS Then
 Block( $handle )
EndIf
MsgBox(0, "Test for Singleton & Block() " & @SEC & @MSEC , "Press OK", 5)
Exit

;Block( $handle, $timeOut )
;  check mutex for $handle
;      when $timeOut is
;       -1 (default) : waits until the mutex of handle can be grabed
;  0 :  returns promptly
;       positive value : wait in msec
;     @Error is available after call
;
Func Block( $handle, $timeOut = -1 )
 ;MsgBox(0, "Block() called", "Block() called")
 $h = DllCall("kernel32.dll", "int64", "WaitForSingleObject", "ptr", $handle, "ptr", $timeOut)
 $lastError = DllCall("kernel32.dll", "int", "GetLastError")
 If $lastError[0] = $ERROR_ALREADY_EXISTS Then
  Return SetError($lastError[0], $lastError[0], $h)
 EndIf
 Return $h
EndFunc ; => Block

;
; Delived from _Singleton() in Misc.au3
;
Func Singleton($sOccurenceName, $iFlag = 0)
 Local Const $ERROR_ALREADY_EXISTS = 183
 Local Const $SECURITY_DESCRIPTOR_REVISION = 1
 Local $handle, $lastError, $pSecurityAttributes = 0

 If BitAND($iFlag, 2) Then
  ; The size of SECURITY_DESCRIPTOR is 20 bytes.  We just
  ; need a block of memory the right size, we aren't going to
  ; access any members directly so it's not important what
  ; the members are, just that the total size is correct.
  Local $structSecurityDescriptor = DllStructCreate("dword[5]")
  Local $pSecurityDescriptor = DllStructGetPtr($structSecurityDescriptor)
  ; Initialize the security descriptor.
  Local $aRet = DllCall("advapi32.dll", "int", "InitializeSecurityDescriptor", _
    "ptr", $pSecurityDescriptor, "dword", $SECURITY_DESCRIPTOR_REVISION)
  If Not @error And $aRet[0] Then
   ; Add the NULL DACL specifying access to everybody.
   $aRet = DllCall("advapi32.dll", "int", "SetSecurityDescriptorDacl", _
     "ptr", $pSecurityDescriptor, "int", 1, "ptr", 0, "int", 0)
   If Not @error And $aRet[0] Then
    ; Create a SECURITY_ATTRIBUTES structure.
    Local $structSecurityAttributes = DllStructCreate("dword;ptr;int")
    ; Assign the members.
    DllStructSetData($structSecurityAttributes, 1, DllStructGetSize($structSecurityAttributes))
    DllStructSetData($structSecurityAttributes, 2, $pSecurityDescriptor)
    DllStructSetData($structSecurityAttributes, 3, 0)
    ; Everything went okay so update our pointer to point to our structure.
    $pSecurityAttributes = DllStructGetPtr($structSecurityAttributes)
   EndIf
  EndIf
 EndIf

 $handle = DllCall("kernel32.dll", "int", "CreateMutexA", "ptr", $pSecurityAttributes, "long", 1, "str", $sOccurenceName)
 $lastError = DllCall("kernel32.dll", "int", "GetLastError")
 If $lastError[0] = $ERROR_ALREADY_EXISTS Then
  If BitAND($iFlag, 1) Then
   Return SetError($lastError[0], $lastError[0], $handle[0])  ; !!! 変更したところ !!!
  Else
   Exit -1
  EndIf
 EndIf
 Return $handle[0]
EndFunc   ;==> Singleton



スポンサーサイト

RubyでAutoItX/COMオブジェクトを使う

 相変わらず,RubyとAutoItで遊んでます.AutoItには,AutoItXというCOMオブジェクト版があります.この2つを繋いでみました.(「.NETじゃだめ?」という,ツッコミはこの際無しね.笑)

 添付のプログラムは,AutoItX 3.3.0.0をインストールした環境で,ActiveScriptRuby(1.8.7)で試しました.実行すると,マウスカーソルが画面上の左上に移動します.それだけです(大笑)

 じゃ,そういうことで...

(2009/3/30追記)
COMオブジェクトの使い方に関していくつか見つけました。
 AutoIt v3.3.0のヘルプファイル
  「るびま」Win32OLE 活用法

-------------------------------
require 'win32ole'
oAutoItX = WIN32OLE.new("AutoItX3.Control")
sleep(0.1)
oAutoItX.MouseMove( 0, 0)



S/P DIFの経由の録音の続き その2

 んー、やっぱりうまくいきません。適当に手打ちでコマンドを入力したときは問題ないのですが、自動運転させるとやっぱり死んでしまいます。
 週日は時間があまり割けないうえ、録画したい番組はイロイロあるので、いったんドライバーを2009/2/21にダウンロード&makeしたものに戻しました。
 試してみたらこのバージョンでも、Kernal module optionでS/P DIF(光ディジタル)経由で録音できました。

これで問題が起きなければしばらくこれを使い続けることにします。(なんだかな・・・)



S/P DIFの経由の録音の続き

 ドライバをアップデートして、LinuxでもS/P DIF経由で録音できるようになったと、思ったら軒並み録画が失敗してしまうようになってしまいました。うまくいく場合もあるのですが・・・

 いろいろ調べた結果、デバイスをオープンしてからデータが出てくるまで異様に時間がかかる場合があることがわかりました。今使っているセットトップボックスが、HDの場合はD3出力とSDの場合はD1出力になるのですが、途中で解像度が変わると、デバイス(HD PVR)から何もデータが出てこなくなるの問題があります。この問題を回避するため、500ms以上データが出てこない場合、いったんデバイスをクローズして再度オープンするようにしていたのですが、待ち時間が足らないようです。長さ0のファイルが数十万以上できてしまいました。orz
 こうなってしまうと、Windowsのエクスプローラーは固まるし、Linux上でlsコマンドを打っても、何十秒も返ってこないしで、なにが起きたのか判らず、あせってしまいました。
 とりあえず、待ち時間を7秒まで増やして、様子見です。。。



芝の手入れ

 今年、始めての芝の管理をしました。

刈り込み 15mm(次回は、もう少し下げる)
苦土石灰 100g/坪 (今年はこれでおしまい)
化成肥料(8-8-8) 100g/坪 (これを来月もう一回、入梅前に一回、9月後半に一回予定)
目土3.5リットル/坪
肥料のあとはかならず、水巻。
目土はこの数倍は入れたいが、まあいいだろう。。。

侮れない量の作業で、大臀筋、背筋をはじめとしてあちこちが痛いです。



Buffalo RemoteStationを赤外線リモコンの受信用に使う

Buffalo RemoteStationの解析のおまけの話です。前回、Windows+Rubyで、PCから赤外線リモコンで外部の装置を制御できるようになりましたが、「学習」用に受信した赤外線データをチェックして、PCを動かすことはできないか?と思ってちょっと実験してみました。
 RemoteStationは、受信した240バイト分のデータをPC側で、記憶しておき、それをそのまま送信することで赤外線リモコンの出力としています。

まず、受信したデータを、記憶しているデータとそのままつき合わせてみます。

んー、不一致の数が適当に決めたスレッショルド以下だったら、一致判定としようと思いましたが、時々メチャメチャ不一致の値が大きくなりますね・・・データを眺めてみると、受信データに「リピート」(長押し)のコードが入っているようです。とりあえず200ビットほど'0'が続いたらそのあとは捨ててしまうように直します。

ぬ~、だいぶ確率は下がりましたが、やはり時々不一致の数が増えます…もう一度データを眺めてみると、1バイトなかのデータの並びが、LSB firstみたい。MSB firstで比べてた… orz

適当に直します(ビット順の反転ってRubyではどうやるのがスマートなんでしょ?) だいぶ、よくなりました。RemoteStationは受信データを「解釈」せずに、0.1ms程度でサンプルしたデータを送ってくるらしいので、場合によっては0→1、1→0のエッジがずれます。
そこで、本来の赤外線データよりも十分にサンプリングレートが速い、と仮定します。
 受信データと、参照用のデータをビットごとに比較し、一致した場合は、そのビットは一致(硬判定)とし、一致しなかった場合は前後のビットがともに一致する場合は一致(軟判定)としました。240ビット分調べて、不一致判定が0(もしくは数ビット)ならば、参照するデータに一致と最終的に判断します。ここまで、面倒くさいことをするくらいなら、データを「解釈」(家電協、NEC,ソニーフォーマットなどがあるらしい)してしまったほうがいいかも?

 ともかく、何とか受信データの判別ができるようになったので、Windowsのメディアセンター2005を動かしてみます。メディアセンター2005は起動や操作をすべてキーボードショートカットで操作できます。Rubyから、COMオブジェクトとしてWScript.Shellを呼び出し、Sendkeysでショートカット・キーを送ってみると、結構まともに動きます。悪くないですね。RemoteStation自体が、連続した赤外線データ受信には適していない(もともとそのための製品ではないですし)ようで、キー操作の間隔を少しあけなければならないです。制御プログラムのほうでも、受信データの判定がそこそこ重いと思うので、受信用のスレッドと判定用のスレッドを分ける(Ruby1.8xでは意味ないか・・・)とかする必要がありそうですが、実験としてはうまくいきました。
 メディアセンターのリモコンは死亡率が高い(3年間で2つ壊れた)ので、代替手段を持つのは、悪くないですね。

では、では。



晩酌のお供

にゃんこ達は大きくなったとはいえ、まだまだ小猫なのでいろんなものに興味を示します。写真は少し前のものですが、晩酌の安ウイスキーに興味津々の様子。

最近の家内の心配事は、にゃんこたちがすぐ食卓のテーブルに乗ってしまうこと。食事中とかは乗ってきませんが、キッチンで手の離せない用事をしてると、テーブルに飛び乗ってしまうそうです。「やってはいけないことをしている」意識はあるようで、テーブルの側に私たちが近づくとおります(メ)。
 一方、私の憂いごとは、最近なでようとすると、思いっきり背中を低くして「よける」こと。 orz. ルーツ飲んでゴー!(ささいなところに本音は見える)みたいな(笑)
 家内に向かって「おまえの躾がなってないからだっ!」(笑、もちろん冗談です)



HD PVR近況 LinuxでSP/DIFで録音できるようになりました (2)

 いや~めでたい。久しぶりに、MythTVのWikiのHauppauge HD-PVRの記事を見に行ったら、どうやらLinuxでも光ディジタルで録音(ただし2ch)ができるようになったらしい。いままで、Linuxではアナログ音声しかダメだったのでこれはウレシイです。
 早速、ファイルをjannauさんの所からゲットしてmakeしました。しかし、どうやってSP/DIFの設定するのかよくわかりません。「Kernal module option」って書いてありますけど、linuxはまだ半年も使ってないし…orz
 Googleで調べると、こんな記事が見つかりました。/etc/modprobe.d/optionを見てみると確かに、それらしいファイルです。このファイルに、

options hdpvr default_audio_input=2

の1行を加えて、リブート。早速、録画をしてみると、どうやらSP/DIFから音声を録画しているようです。
 完全に問題なしかどうかはもう少し様子を見なければなりませんが、とりあえず、いや~、めでたい。

んじゃ、そういうことで…



Regza Z2000用のNAS

我が家のTVは東芝のRegza37Z2000です。Zシリーズは、NASに録画ができます。現在、玄箱HGに、KURO-SATAでSATAを使えるようにして、WesternDigital WD10EADS(4枚プラッタ、キャッシュ16MBの旧モデル)で使っています。玄箱HGはDebian化しています。

 ところが最近録画が失敗するようになってしまいました。まだHDDの残り容量は45%以上あるのですが...どうも書き込み速度が間に合わなくなってしまっているようです。もちろんHDDは目一杯までまで使うものではないとは思いますが、いくら倉庫用鈍速HDDでもこれは酷すぎます。orz
おそらく玄箱が遅いのだと思いますが、衛星放送でも高々24Mbpsですから、1GLanが泣いてしまいます。

かといって、


【送料無料】KURO-BOX/PRO 玄人志向 玄箱Pro 1000BASE-T対応Linux-Box組み立てキット KURO-BOX/PROって高いし、所詮メモリもしょぼいから同じ問題がおきないとも言えないわけで、ちょっと困ってます。


Intel BOXD945GCLF2D デュアルコアAtom 330のCPU搭載Mini-ITXマザーボード
D945GCLF2D+メモリ+電源の方が安い(電源余ってるし) orz
どうしよう...



TME CaptureModuleをAutoitで動かす

 Hauppauge HD PVRだが、Windows用にArcSoftのTotalMediaExtreamが付属しています。いままでろくに使っていなかった(普段はLinuxベースのキャプチャを使用)のですが、ちょっと使ってみることにしました。コマンドラインから動作できるように、AutoItでプログラムを組んでみます。本当は多分APIがあるのだと思いますが、公開されていないので、AutoItの記録機能でとれた情報をもとにサクッと済ませるはずでしたが、細かいところに対応しているうちに300行くらいになってしまい、週末はつぶれてしまいました。orz

石田先生なくなったんだね。私の父より若いのに。。。先生の翻訳したK&Rはそれこそボロボロになるまで読んだものです。合掌。



FC2Ad

まとめ

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。