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

アプリ実行前から起動済みの他アプリを終了させるには?

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

プログラミングの初心者です。
WindousXP/VS2005でアプリを作成中です。
3つのアプリ(MasterAppli,SlaveAppliA,SlaveAppliB)を作成しています。

■質問内容
SlaveAppliA,SlaveAppliBを起動済みの状態でMasterAppliを実行します。
最後に起動したMasterappliを終了するときに、
SlaveAppliA,SlaveAppliBも同時に終了させるにはどうすれば良いのでしょうか?

■現状コードの状況
MasterAppliを起動してから、proocessコンポーネントでappliA,appliBを
起動させている場合にはMasterAppliのクローズイベントで
Process○.CloseMainWindow()を使って終了できます。
先にSlaveAppliA,SlaveAppliBのアプリを実行している場合には、
Process○.CloseMainWindow()ではSlaveAppliA,SlaveAppliBが終了されません。

■現状コード(質問用に簡素化してます。)
Public Class MasterAppli

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles MyBase.Load
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e _
As System.EventArgs) Handles Button1.Click
Me.Process1.StartInfo.FileName _
= "C:\SlaveAppliA\SlaveAppliA\bin\Release\SlaveAppliA.exe"
Me.Process1.Start()
End Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button2.Click
Me.Process2.StartInfo.FileName = "C:\SlaveAppliB\SlaveAppliB\bin\Release\SlaveAppliB.exe"
Me.Process2.Start()
End Sub

Private Sub form1_FormClosing(ByVal sender As Object, ByVal e As _
System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
Process1.CloseMainWindow()
Process2.CloseMainWindow()
End Sub
End Class

■初歩的な質問かもしれませんが御教示お願い致します。
■No21957に返信(げーんさんの記事)
> プログラミングの初心者です。
> WindousXP/VS2005でアプリを作成中です。
> 3つのアプリ(MasterAppli,SlaveAppliA,SlaveAppliB)を作成しています。
>
> ■質問内容
> SlaveAppliA,SlaveAppliBを起動済みの状態でMasterAppliを実行します。
> 最後に起動したMasterappliを終了するときに、
> SlaveAppliA,SlaveAppliBも同時に終了させるにはどうすれば良いのでしょうか?
>

別案ですが、監視用にもう一つフォームを用意します。
分かりやすくいうと、メニューです。
このメニューは非表示にしておくとして、全てのアプリは
メニューフォームを介して起動させるようにすればいいです。
MasterAppli終了→メニューで終了検知→SlaveAppliA,SlaveAppliB終了
> 別案ですが、監視用にもう一つフォームを用意します。
> 分かりやすくいうと、メニューです。
> このメニューは非表示にしておくとして、全てのアプリは
> メニューフォームを介して起動させるようにすればいいです。
> MasterAppli終了→メニューで終了検知→SlaveAppliA,SlaveAppliB終了

やじゅ さん 早速ご回答頂きありがとうございます。

説明不足ですみませんでした。
MasterAppliのフォームをメニュー?として運用するつもりです。
せっかく提案に対して申し訳ありませんが、
フォームの追加は避けたいと思います。

通常運用としてはメニューであるMasterAppliにて
SlaveAppliA,SlaveAppliBのiniファイルを選択して起動させますが、
SlaveAppliA,SlaveAppliBをexeファイルで直接起動させて単独使用することも想定しています。
メニューとして使用しているMasterAppliを終了するときには、
SlaveAppliがexeファイルで直接起動されていたとしても同時に終了させたいと思っています。
exeファイルで直接実行されているSlaveAppliアプリを
MasterAppliから終了させることは可能でしょうか?
■No21961に返信(げーんさんの記事)
> MasterAppliのフォームをメニュー?として運用するつもりです。
> せっかく提案に対して申し訳ありませんが、
> フォームの追加は避けたいと思います。
>

あくまでMasterAppliで両アプリを終了させたいと考えます。
MasterAppliで両アプリを終了出来ていることを前提とします。

SlaveAppliA,SlaveAppliBのいずれかが終了したら、
MasterAppliに対して、メッセージを送ります。
MasterAppliがメッセージを受け取ったら、両アプリを終了させます。

メッセージ送信にはWM_COPYDATAを使います。
メッセージは"終了"とかの文字列でいいでしょう。
http://bbs.wankuma.com/index.cgi?mode=al2&namber=8322&KLOG=8

プロセスの終了
http://dobon.net/vb/bbs/log3-2/1011.html
あれ、私おもいっきし質問を読み間違えてますね
単独のSlaveAppliAが終了したら、他の起動アプリも終了させるのかと
思ってしまってた。
■No21964に返信(やじゅさんの記事)
> あれ、私おもいっきし質問を読み間違えてますね
> 単独のSlaveAppliAが終了したら、他の起動アプリも終了させるのかと
> 思ってしまってた。

やじゅさん、回答ありがとうございます。
やりたいことはMaster終了時のSlave終了です。。。
いずれにせよ、外部プロセスを終了させることには変わりありませんので
  >プロセスの終了
  >http://dobon.net/vb/bbs/log3-2/1011.html
をそのままコードに追加しました。
exeでSlaveを起動後にMasterを起動→終了させたところ。
  Call PostMessage(lngHandle, WM_QUIT, 0, 0)
の部分で下記エラーとなります。
また、Slaveが起動していないときにMasterを終了しても、
lngHandle に値が入ってしまいます。
そもそもhttp://dobon.net/vb/bbs/log3-2/1011.htmlのコードが
VB2005に対応しているのかどうかも不安です。
そのあたりも踏まえて対処法を御教授頂きたく。。。
宜しくお願い致します。

■エラー内容
PInvokeStackImbalance が検出されました。
Message: PInvoke 関数 'MasterAppli!WindowsApplication1.MasterAppli::PostMessage' が
スタックを不安定にしています。PInvoke シグネチャがアンマネージ ターゲット シグネチャに
一致していないことが原因として考えられます。
呼び出し規約、および PInvoke シグネチャのパラメータがターゲットのアンマネージ シグネチャに
一致していることを確認してください。


■現状コード
Public Class MasterAppli

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles MyBase.Load


End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e _
As System.EventArgs) Handles Button1.Click
Me.Process1.StartInfo.FileName _
= "C:\SlaveAppliA\SlaveAppliA\bin\Release\SlaveAppliA.exe"
Me.Process1.Start()
End Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button2.Click
Me.Process2.StartInfo.FileName = "C:\SlaveAppliB\SlaveAppliB\bin\Release\SlaveAppliB.exe"
Me.Process2.Start()
End Sub

Private Sub form1_FormClosing(ByVal sender As Object, ByVal e As _
System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
'Process1.CloseMainWindow()
'Process2.CloseMainWindow()

gfm_lngKillProcess("SlaveAppliA")

End Sub


Public Declare Function FindWindow Lib "USER32" Alias "FindWindowA" (ByVal _
lpClassName As String, ByVal lpWindowName As String) As Long


Public Declare Function PostMessage Lib "USER32" Alias "PostMessageA" _
(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam _
As Long) As Long
Public Const WM_QUIT = &H12


Public Function gfm_lngKillProcess(ByVal pi_strTitle As String) As Long
'RET(0) : 削除対象なし()
'1: 削除成功()
'マイナス: 削除失敗()
Dim lngHandle As Long

On Error GoTo LBL_ERR_FUNCTION

'ウィンドウのハンドルを取得()
lngHandle = FindWindow(vbNullString, pi_strTitle)
If lngHandle = 0 Then
''起動されていない()
gfm_lngKillProcess = 0
Exit Function
Else
'削除実行()
Call PostMessage(lngHandle, WM_QUIT, 0, 0)
gfm_lngKillProcess = 1
End If

Exit Function

LBL_ERR_FUNCTION:

'削除エラー()
gfm_lngKillProcess = Err.Number

End Function

End Class
■No21966に返信(げーんさんの記事)

指定のプロセス名をすべて終了させてみます。

' 対象プロセス名は"SlaveAppliA"
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  Dim p As New System.Diagnostics.Process
  Dim inst As Process
  Dim myProcess() As Process

  myProcess = System.Diagnostics.Process.GetProcessesByName("SlaveAppliA")
  For Each inst In myProcess
    p = System.Diagnostics.Process.GetProcessById(inst.Id)
    p.Kill()
  Next
End Sub
こっちかな
グラフィカルインターフェイスのあるプロセスの終了を要求する
http://dobon.net/vb/dotnet/process/killprocesse.html
■No21975に返信(やじゅさんの記事)
> こっちかな
> グラフィカルインターフェイスのあるプロセスの終了を要求する
> http://dobon.net/vb/dotnet/process/killprocesse.html


やじゅさん、御教授ありがとうございます。
いろいろと試行錯誤して下記のようなコードになりました。
killprocesseもCloseMainWindowも、
ハンドルされているprocessにのみ有効なようでして、
外部プロセス(エクスローラーからexeファイルを直接実行して起動しているプロセス)には無効なようです。
ハンドルされたプロセスを終了させるfunctionと
ハンドルされていないプロセスを終了させるfunctionを作って
両方を使用しています。
これで一応はどちらでも終了できるようになったのですが、
無駄が多い気がして・・・。
もっとスマートにする方法はないものでしょうか?
ハンドルされていないプロセスをハンドルさせるメソッド、
またはハンドルされている、いないに関係なくプロセスを終了させるメソッド
といったものがあればもう少しスマートになりそうな気がしています。
そのようなメソッドはあるのでしょうか?
ヘルプ等を見ても当方判断つかず、御教授お願い致します。



■現状のコード
Public Class MasterAppli
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles MyBase.Load
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e _
As System.EventArgs) Handles Button1.Click
Me.Process1.StartInfo.FileName _
= "C:\SlaveAppliA\SlaveAppliA\bin\Release\SlaveAppliA.exe"
Me.Process1.Start()
End Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button2.Click
Me.Process2.StartInfo.FileName = "C:\SlaveAppliB\SlaveAppliB\bin\Release\SlaveAppliB.exe"
Me.Process2.Start()
End Sub

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
gfm_lngKillProcess("SlaveAppliA")
gfm_closeProcess("SlaveAppliA")
End Sub

Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click, Button4.Click
gfm_lngKillProcess("SlaveAppliB")
gfm_closeProcess("SlaveAppliB")
End Sub

Private Sub form1_FormClosing(ByVal sender As Object, ByVal e As _
System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
gfm_lngKillProcess("SlaveAppliA")
gfm_closeProcess("SlaveAppliA")
gfm_lngKillProcess("SlaveAppliB")
gfm_closeProcess("SlaveAppliB")
End Sub

Public Declare Function FindWindow Lib "USER32" Alias "FindWindowA" (ByVal _
lpClassName As String, ByVal lpWindowName As String) As Integer

Public Declare Function PostMessage Lib "USER32" Alias "PostMessageA" _
(ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam _
As Integer) As Integer
Public Const WM_QUIT = &H12

'////ハンドルされていないプロセスを終了させるファンクション////
Public Function gfm_lngKillProcess(ByVal pi_strTitle As String) As Long
'RET(0) : 削除対象なし()
'1: 削除成功()
'マイナス: 削除失敗()
Dim lngHandle As Long

On Error GoTo LBL_ERR_FUNCTION

'ウィンドウのハンドルを取得()
lngHandle = FindWindow(vbNullString, pi_strTitle)
If lngHandle = 0 Then
''起動されていない()
gfm_lngKillProcess = 0
Exit Function
Else
'削除実行()
Call PostMessage(lngHandle, WM_QUIT, 0, 0)
gfm_lngKillProcess = 1
End If

Exit Function

LBL_ERR_FUNCTION:

'削除エラー()
gfm_lngKillProcess = Err.Number

End Function

'////ハンドルされているプロセスを終了させるファンクション////
Public Function gfm_closeProcess(ByVal pi_strTitle As String) As Long
Dim myProcess As System.Diagnostics.Process() = _
System.Diagnostics.Process.GetProcessesByName(pi_strTitle)
Dim process As System.Diagnostics.Process
For Each process In myProcess
process.CloseMainWindow()
Next process
End Function

End Class
H.K.R.と申します。

私の環境で試してみたところ、以下のコードで、外部で起動したメモ帳を終了させることができましたので、試してみてはいかがでしょうか?
# もし外れていたらごめんなさい。m(_ _)m

※ 私が試した環境:WindowsXPHomeSP2、VisualBasic2008ExpressEdition(対象Frameworkは、.NET Framework 2.0)

Public Class TestForm4
  Inherits Form
 
  Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
   Me.Text = Me.GetType.Name
   MyBase.OnLoad(e)
  End Sub
 
  ' 予めメモ帳を起動しておく
  Protected Overrides Sub OnDoubleClick(ByVal e As System.EventArgs)
    For Each p As Process In Process.GetProcesses
    'Dim ss As String = p.MainModule.FileName ' Win32Exceptionがスローされるので、使えないようです。
    Dim ss2 As String = p.ProcessName
   ' ProcessNameのみで判断してよいのかどうかは分かりませんが・・・
    If ss2 = "notepad" Then
     Console.WriteLine(p.ProcessName)
     Console.WriteLine(p.MainWindowTitle)
     MsgBox(p.MainWindowTitle + "を閉じます。")
     p.CloseMainWindow()
    End If
   Next
 
   MyBase.OnDoubleClick(e)
  End Sub
End Class
■No21979に返信(H.K.R.さんの記事)
> H.K.R.と申します。
>
> 私の環境で試してみたところ、以下のコードで、外部で起動したメモ帳を終了させることができましたので、試してみてはいかがでしょうか?
> # もし外れていたらごめんなさい。m(_ _)m
>

H.K.Rさん、コードありがとうございます。
早速、試させて頂いたところ、メモ帳は終了できるのですが、
自作アプリを終了させることはできませんでした。
これはおかしいと調べていますと、
自作アプリ(終了させたいアプリ)の実行方法が間違っていました。
自作アプリをデバッグモードで走らせて、
アプリを実行したつもりになっていました。
exeファイルで自作アプリを実行してから試したところ、
終了させることができました。

当方のデバッグ方法の勘違いでお騒がせしてしまい申し訳ありませんでした。
みなさまの投稿を参考になんとかやりたいことが実現できました。
本当にありがとうございました。

■現状コード
Public Class MasterAppli
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles MyBase.Load
End Sub

'Button1でSlaveAppliAを起動
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e _
As System.EventArgs) Handles Button1.Click
Me.Process1.StartInfo.FileName _
= "C:\SlaveAppliA\SlaveAppliA\bin\Release\SlaveAppliA.exe"
Me.Process1.Start()
End Sub

'Button2でSlaveAppliBを起動
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As _
System.EventArgs) Handles Button2.Click
Me.Process2.StartInfo.FileName = "C:\SlaveAppliB\SlaveAppliB\bin\Release\SlaveAppliB.exe"
Me.Process2.Start()
End Sub

'Button3でSlaveAppliAを終了
Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
gfm_closeProcess("SlaveAppliA")
End Sub

'Button4でSlaveAppliBを終了
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click, Button4.Click
gfm_closeProcess("SlaveAppliB")
End Sub

'MasterAppli終了時にはSlaveAppliA,SlaveAppliBも同時に終了
Private Sub form1_FormClosing(ByVal sender As Object, ByVal e As _
System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
gfm_closeProcess("SlaveAppliA")
gfm_closeProcess("SlaveAppliB")
End Sub

'プロセスを終了させるファンクション
Public Function gfm_closeProcess(ByVal pi_strTitle As String) As Long
Dim myProcess As System.Diagnostics.Process() = _
System.Diagnostics.Process.GetProcessesByName(pi_strTitle)
Dim p As System.Diagnostics.Process
For Each p In myProcess
Console.WriteLine(p.MainWindowTitle)
MsgBox(p.MainWindowTitle + "を閉じます。")
p.CloseMainWindow()
Next p
End Function

End Class
解決済み!

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