Internet Watchで知りましたが、IPAがGPLv3の日本語訳と解説を作成し公開したようです。すばらしい。でも256ページもあります。読むのが大変そう。
しかし、IPAってなんで文書という文書をPDFで公開するんでしょうねえ。紙が好きなのかな。文書のレイアウトに絶対の自信を持っている?
Internet Watchで知りましたが、IPAがGPLv3の日本語訳と解説を作成し公開したようです。すばらしい。でも256ページもあります。読むのが大変そう。
しかし、IPAってなんで文書という文書をPDFで公開するんでしょうねえ。紙が好きなのかな。文書のレイアウトに絶対の自信を持っている?
いつのころからか、Microsoftはアプリケーションの二重起動防止コードサンプルとして、MutexとFindWindow()を組み合わせた方法を推奨するようになった。
なんでアプリケーションの起動ごときにMutexとWindowという二つの仕組みを併用せねばならんのか、と不満だったのだが、今まで深く考えたことが無かったので、見直してみた。
自分のウィンドウをFindWindowして、見つからなかった場合は新規作成。見つかった場合は、既存のウィンドウをSetForegroundWindowする。昔のアプリケーションは皆こうしていた。簡単で非常に結構なのだが、少し考えればまずい点は明らかだ。FindWindowからCreateWindowの流れはアトミックな操作ではないので、どう頑張っても二重起動する可能性を消しきれない。単なるアプリケーションウィンドウの二重起動ならば、それほど神経質にならなくても良かろうが、KanaInputのようにシステムの動作にかかわるアプリケーションは、カーネルオブジェクトを使って、確実に二重起動を制御すべきである。(KanaInput 1.0.1 ではやってません。次のバージョンで対応します )
で、カーネルオブジェクトの登場となるわけだが、CreateMutexを使って、自分が作成者かどうかだけを判断の材料にする。自分が作成者であれば、おもむろにウィンドウを作成する。自分が作成者でなければ、既存のウィンドウをFindWindowしにいく。同期オブジェクトとしてMutexを使っているにもかかわらず、FindWindowなどという、どちらかといえば不確かな手段を使わなければならないのが気に入らない。同期オブジェクトを使っているのだから、既存ウィンドウのForeground化も同期オブジェクトを使って行えないものか、と考えてしまう。
Mutexは、所有/非所有の判断しかできないので、Eventを使うことを考えてみた。CreateEventして、自分が作成者ならば、ウィンドウを作成し、メッセージループでEvent待ちを併用する。自分が作成者でなければ、既存のEventオブジェクトに対してSetEventする。既存プロセスが、Eventを受け取ったら、SetForegroundWindowを実行する。きれいではないか。ところが実際にやってみると、難がある。まず、デスクトップでは、Windows 2000以降、SetForegourndWindowの実行に制限がかかっているので、バックグラウンドにくすぶっているプロセスが、自分のウィンドウに対してSetForegourndWindowしても、Foregroundになってくれない。Windows Mobileでは今のところそのような制限は無いが、メッセージループが回っていないと、Eventが取れない。つまり、モーダルループが回っている間は、使えない。モーダルウィンドウを出さないアプリケーションならば、使えるかもしれない。(ChooseIM 1.0.1 がこの方法を使っています)
つまり、後から起動したほうが、既存ウィンドウに対してSetForegroundWindowを実行しなければならないわけだが、そのためには、ウィンドウハンドルが必要になる。Windowsでは、特にWindows Mobileでは、プロセス間のデータ共有方法はFile Mappingしかない。で、Semaphoreを使って制御しながら、File Mappingでウィンドウハンドルを後続プロセスに渡す方法で組んでみた。問題なく動作する。FindWindowのような不確かな要素も入らない。しかし、受け渡すデータが4バイトなのに対して、共有メモリーの最小単位が64KBと大きいのである。65532バイトが丸々無駄になる。メモリの潤沢なデスクトップ環境ならばいざ知らず、Windows Mobileでは、このような無駄は極力避けたい。
以上のような理由から、結局、Mutex+FindWindowが落しどころと言うことになるのだろう。Microsoftの推奨にも理由はあるのだろうが、やはり自分で考えてみないと納得がいかない。
FindWindowは、ウィンドウ名とウィンドウクラス名で検索する。ウィンドウ名というのは、ユーザーに見せるものだから、その表現内容は自ずと限られる。ウィンドウクラス名はユーザーに見えないので、長くて醜い名前、特にURLを利用した一意的な名前を使えば、他アプリケーションとの衝突は事実上避けられる。しかし、ダイアログアプリケーションでは、そうはいかない。ダイアログクラスは、システムが用意するものだから、クラス名は選べない。ダイアログウィンドウの検索は、実質ウィンドウ名だけで行われることになる。これは避けたい。「KanaInput」と言う名前のダイアログを他人が悪意なく作成することなど、いくらでもありえるではないか。これを避けるために、KanaInputや、ChooseIM 1.0.0 などは、表示されないフレームウィンドウを、FindWindowのために持たせていたのである。ところが、この構成は、SetForegroundWindowに難がある。
最近、ダイアログウィンドウを他のウィンドウの子ウィンドウとして貼り付けることができるということを知った(知りませんでした)。この方法を使えば、ダイアログを利用しながら、独自のクラス名を持ったウィンドウを作成することができる。単独のウィンドウだからSetForegroundWindowによるフォーカスの問題も起きにくい。PropertySheetだと、多少厄介なのだが、やり方はここに書いてある。ChooseIMとKanaInputの次バージョンは、この方法を採用する予定である。
ChooseIMの1.0.1を公開しました。こちらから。
機能的な差はありません。バックグラウンドから呼び出したときに、クリックするまでキーボードが使えない問題などが修正されてます。
「影のフレームウィンドウ」をやめて、ノン・モーダル・ダイアログにしてみました。[メモリ]コントロールパネルから終了できるようになったのはうれしいのですが、なぜかTodayの[実行中のプログラム]にカウントされなくなってしまいました。謎だらけ。
Powered by WordPress