DOBON.NET DOBON.NETプログラミング掲示板過去ログ

複数起動プロセス間で共有のフォームを使いたい

環境/言語:[VB2008]
分類:[.NET]

2012/05/15(Tue) 15:57:33 編集(投稿者)

VB2008でソフトの作成を行っています。
このソフトは複数プロセス起動することが可能となっています。
今、このソフトに検索用画面のような子画面を用意したいと思うのですが、複数プロセスが起動している場合には、この子画面を各プロセスで共有したいのです。
この共有という意味は、例えば、以下のような感じです。
このソフトのプロセスP1・P2が起動しているとします。
プロセスP1上の操作(例えばボタンクリック)を行うことにより、子画面CがP1のメインフォームをオーナーとして開きます。
この状態でプロセスP2のメインフォームをアクティブにし、プロセスP2上の操作(例えばボタンクリック)を行うことにより、先ほど開いた子画面CのオーナーがP2のメインフォームに変わります(子画面Cに表示されている内容はそのまま)。
この時プロセスP1のメインフォームをアクティブにすると、子画面Cは表示されません。
(すでにプロセスP2のメインフォームは子画面Cのオーナーではないため)

ちょっとうまく説明ができなくて申し訳ないのですが、このようなことは可能なのでしょうか?
2012/05/15(Tue) 22:53:13 編集(投稿者)

フォーム(ウィンドウ)はスレッドに属し、スレッドはプロセスに属します。
あるウィンドウの親を別のプロセスのウィンドウにすることはできますが、親子関係が変わるだけで、ウィンドウの処理を行うスレッド・プロセスは変わりません。

たとえば、子画面 C の親を P2 のメインフォームにしたところで、子画面 C への操作によるイベントは P1 にしか起きません。
このように、ウィンドウの親子関係は変えられますが、ウィンドウとスレッド・プロセスの関係は変えられません。

今回やりたいことでアプローチとして考えられるのは以下のようなものでしょうか。

1.P2 で表示しようとしたときに P1 に対して子画面 C を閉じさせるような通信をして、P2 で改めて子画面 C を出すようにする。

2.どちらかのプロセスでのみ子画面 C を扱い、他方のプロセスは子画面を扱うプロセスにリクエスト(通信)して画面を表示してもらう。結果のフィードバックもそのプロセスから通信して受け取る。

// 複雑になるだけなので、できれば完全に別々にする、あるいは同一プロセスにする方向を模索した方がよいとは思いますが…。
■No30444に返信(サヴァさんの記事)

プロセス間通信でいろいろ検索してみて
どの方法で実装するのか決めて試してから
分からないところを質問された方がよいと思います。
2012/05/16(Wed) 20:12:03 編集(投稿者)

皆様、ご回答ありがとうございました。
皆様から寄せられた情報をもとに自分なりに調べてみたいと思い、お礼が遅くなり申し訳ございません。
まず、プロセス間通信というものを色々と調べ、その結果、以下を参考にし、WinAPIでプロセス間通信を行うことに挑戦しました。
参考:
 http://www.geocities.jp/hatanero/sendmessage1.html
 http://www.geocities.jp/hatanero/sendmessage2.html

また、子画面C用のプロジェクト(exe)を別途作成し、この子画面Cが、操作を行った親プロセスのメインウィンドウをオーナーとして表示させるために、以下の方法を行うことにより実現することができました。

1)親プロセスのメインウィンドウハンドルの情報をプロセス間通信で子画面Cに送信する。

2)IWin32Windowインタフェースを実装したクラスを用意し、子画面で受信した親プロセスのウィンドウハンドルの情報をもとにインスタンスを作成する。

3)子画面CのVisibleプロパティを一度Falseにしてから子画面でMe.Show(w)を行う(ここでwはIWin32Window実装クラスのインスタンス)。

※なお、ここでいう親・子というのはあくまでも私の概念であることであること、あらかじめご了承ください。
(おそらくWindowsの概念としては全く別のものなのでしょう。)

ついでに、子画面を閉じた後にまた子画面を開いたときに、子画面に表示されている状態をそのまま保存したい(例えばグリッドのスクロール状態などもそのままにしたい)と思い、子画面を常駐させるようにし、子画面が不要の時は非表示にするようにしてみたいと思って、以下を参考にいろいろとやってみました。

参考:
 http://shinshu.fm/MHz/88.44/archives/0000038341.html

ただ、以下のような問題が発生しました。
1)子画面が非表示になっている場合、親プロセスから子画面のプロセスをProcess.GetProcessesByNameメソッドで取得したときに、取得した子画面プロセスのMainWindowHandleは0となり、子画面のウィンドウハンドルを取得できません。
これは子画面をロードするときに子画面のハンドルをレジストリか一時ファイルなどに保存することにより、親プロセスからこの情報を参照することが可能になります、そもそも非表示ウィンドウのハンドルを取得する方法というものはあるのでしょうか?

2)親プロセスのアプリを終了する場合、他に親プロセスが起動していない場合は子画面のプロセスを終了させたいのですが、(常駐しているからなのか)子画面のプロセスをCloseMainWindowメソッドで終了させることができません(Falseが返る)。
Killメソッドを使えば子プロセスを終了することができることは分かったのですが、Killって強制終了のようなイメージがあるので、常駐プログラムの外部からの正しい終了方法を教えてくださいますでしょうか?
「プロセス間でウィンドウを共有する」ということ自体、かなりニッチなところです。
そういう外れた道を進む以上は、それなりに自分で調べる力、解決する力が求められます。
また、環境ややり方によってはうまく動かないかもしれません。その保障は自ら責任を持ってください。


「ウィンドウ 探す API」などで調べれば FindWindow ぐらいは見つけられそうですが、検索されていますか?
また、相手を終了させたいなら、相手に終了してほしいとメッセージを送ればよいでしょう。Message をすでに使っているのですよね?
(自分で勝手に使ってよいメッセージの範囲は決まっているのできちんと調べておいてください)

中途半端に .NET Framework のクラスライブラリで解決しようとするのではなく、Win32API での解決方法も模索するべきでしょう。
今の状況は普通の使い方ではないので、.NET Framework のクラスライブラリでは解消できない状況も多いと思われるため。
Azulean様、コメントありがとうございました。
確かに、今回やろうとしていることは、ご指摘の通り正当なやり方ではないですね。
このような複雑なことをやっておかしなトラブルがでてしまったら対処も大変そうですので、今回は複数起動をやめ、その代わりに一つのプロセスに同じフォームを複数表示することにより対応しようと思います。
こうすれば、別プロセス間でフォームの親子関係を設定するような面倒なことは行わなくて済みそうです。
(その代わり、アプリケーションの重複起動の禁止を行うことになりますが、これは色々と情報があるようですので、自分でやってみます。)

ただ、アドバイスの中でちょっと分からないことがありました。

> また、相手を終了させたいなら、相手に終了してほしいとメッセージを送ればよいでしょう。Message をすでに使っているのですよね?
> (自分で勝手に使ってよいメッセージの範囲は決まっているのできちんと調べておいてください)

ここでいう「メッセージの範囲」とは、具体的に何を指しているのでしょうか?
(SendMessageの第二引数に渡す値のことなのかと思いましたが、文面からは推測することができませんでした。)

■No30455に返信(サヴァさんの記事)
> ここでいう「メッセージの範囲」とは、具体的に何を指しているのでしょうか?
> (SendMessageの第二引数に渡す値のことなのかと思いましたが、文面からは推測することができませんでした。)

おそらくこのことではないでしょうか?
下記URLを見るとわかるかと思います。

http://msdn.microsoft.com/ja-jp/library/ms644931.aspx
http://msdn.microsoft.com/ja-jp/library/cc410981.aspx
■No30456に返信(kikuさんの記事)
>>ここでいう「メッセージの範囲」とは、具体的に何を指しているのでしょうか?
>>(SendMessageの第二引数に渡す値のことなのかと思いましたが、文面からは推測することができませんでした。)
>
> おそらくこのことではないでしょうか?

はい。それで合っています。
SendMessage/PostMessage の第 2 引数のことを指しており、ここは意味が決まっている数値、予約されている範囲、推奨される範囲とあります。
アプリケーションレベルで独自の値であれば、WM_APP 以降がよいかと思ってはいます。
Azulean様、kiku様、ご回答ありがとうございました。
SendMessageについては、追々勉強していこうと思います。
今後ともよろしくお願いします。
解決済み!
2012/05/20(Sun) 00:12:52 編集(投稿者)

■No30458に返信(サヴァさんの記事)
> Azulean様、kiku様、ご回答ありがとうございました。
> SendMessageについては、追々勉強していこうと思います。
> 今後ともよろしくお願いします。

ユーザ領域のウィンドウメッセージの番号を取得したいときは
RegisterWindowMessage
を使うと他のアプリケーションとの衝突を軽減することが出来ます。

MSDNのRegisterWindowMessage:
http://msdn.microsoft.com/ja-jp/library/cc410981.aspx

ウィンドウメッセージを使用したプロセス間通信を行うときは参考にして
みて下さい。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板