-
業務システムでは似た様なフォームを作成することがありますが、 共通となる部分を基本的なフォームクラスとして作成しておき、 それを継承することでデザインの統一を図ったりします。
今回はフォームクラスの継承の方法について順を追って説明します。
まず最初に基本となるフォームに1個のパネルを設置し、その上に1個のボタンを置く様にしました。 (フォームの名前は「BaseForm」としました。) デザイン画面は以下の様な感じです。
その後、ボタンのクリックイベントで簡単なメッセージを表示する様にしました。最初のBaseFormのソース
Public Class BaseForm Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click MsgBox("BaseForm : Button1_Click") End Sub End Class
ここで一旦このプロジェクトのビルドを行います。 その後、プロジェクトにこの「BaseForm」を利用するフォームの追加を行ってみます。 プロジェクトへの項目の追加を行うために、 「新しい項目追加」ダイアログで 「Windows Forms」 の中の 「継承されたフォーム」 を選択します。 フォーム名は Form1 のままとします。
この後で「継承ピッカー」ダイアログが表示されますので 「BaseForm」 を選択して「OK」をクリックします。
結果的に以下の様にフォームデザイン画面が表示されます。 (フォーム上のパネルとボタンの所に小さいマークが付いていますが、このデザインからは修正できない状態であるロックが掛かった状態であることを示しています。)
この後、このプロジェクトのプロパティを開いて 「スタートアップ オブジェクト」 を 「Form1」 に設定します。
このプロジェクトを実行させると以下の様になります。画面はボタンをクリックした時の状態を示します。
Form1 では特に何もソースは記述していませんが、継承されたフォームの処理が出来ることが分かると思います。
このままでは面白くないので 「BaseForm」 にボタンのタイトルを変更するプロパティを追加してみます。タイトルプロパティの追加のBaseFormのソース
Public Class BaseForm ' ボタン1タイトルに文字列設定 Public Property Button1Title As String Get Return Me.Button1.Text End Get Set(value As String) Me.Button1.Text = value End Set End Property Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click MsgBox("BaseForm : Button1_Click") End Sub End Class
Form1のソース
Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load 'ボタンタイトル設定 Me.Button1Title = "ボタン" 'フォームタイトルも設定 Me.Text = "Form1 から設定されたタイトル" End Sub End Class
これを実行すると以下の様に、ボタンのタイトルが「ボタン」に変わったことが分かります。
さらに、ボタン処理を継承先から上書き変更できる様にしたいと思います。 BaseForm 側では Button1_Click メソッドに Overridable 修飾子を付加して上書き可能であることを宣言します。
Form1 側では Button1_Click メソッドを上書きすることを宣言するため Overrides 修飾子を付加します。
ボタン処理をオーバーライド指定としたBaseFormのソース
Public Class BaseForm ' ボタン1タイトルに文字列設定 Public Property Button1Title As String Get Return Me.Button1.Text End Get Set(value As String) Me.Button1.Text = value End Set End Property 'オーバーライド指定 Protected Overridable Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click MsgBox("BaseForm : Button1_Click") End Sub End Class
Form1のソース:ボタン処理をオーバーライド
Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load 'ボタンタイトル設定 Me.Button1Title = "ボタン" 'フォームタイトルも設定 Me.Text = "Form1 から設定されたタイトル" End Sub Protected Overrides Sub Button1_Click(sender As Object, e As EventArgs) '別の処理にする MsgBox("Form1 からのボタン処理") End Sub End Class
これを実行すると以下の様に、ボタンクリック処理が Form1 の処理しか行われないことが分かります。
もし、継承元のボタン処理も行いたい場合には Form1 を以下の様に変更します。 これを実行し、ボタンをクリックすると2回メッセージボックスが表示されます。Form1のソース:ボタン処理をオーバーライド2
Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load 'ボタンタイトル設定 Me.Button1Title = "ボタン" 'フォームタイトルも設定 Me.Text = "Form1 から設定されたタイトル" End Sub Protected Overrides Sub Button1_Click(sender As Object, e As EventArgs) '継承元のボタン処理 MyBase.Button1_Click(sender, e) '別の処理にする MsgBox("Form1 からのボタン処理") End Sub End Class
関連する記事
⇒フォームクラス生成時に設定値を同時に渡す方法
⇒フォーム上の複数のコントロール(TextBox)の入力変化を確認する方法について
⇒フォームのマウスカーソルを待機状態する方法について
⇒フォームクラスの継承の方法について
PR -
スレッドを以下のプログラムの様に作成し開始させると Main 関数は先に終了しますが、スレッドは約5秒後にしか終了しないため、 全体のプログラムの実行はスレッドが終了するまで続きます。
コンソールへの表示からも分かりますが「Main終了」が表示された後で「...スレッド」が5回表示されます。
これは何もおかしいことでは無く、プログラムは属する全てのスレッドが終了するまでは終われないのです。
Main 関数も1個のスレッド(メインスレッド)として実行されるのでが処理的に先に終わってしまいます。
しかし、別のスレッドとして起動された ThreadProc 関数が残っているので、このスレッドが終了するまで プログラムは終了しません。スレッドの実行プログラム
Imports System Imports System.Threading Module ThreadStop 'メイン関数(エントリーポイント) Sub Main() 'スレッド生成 Dim pThread As Thread = New Thread(AddressOf ThreadProc) Console.WriteLine("スレッド開始") 'スレッド開始 pThread.Start() Console.WriteLine("Main終了") End Sub 'スレッド関数 Sub ThreadProc() For i As Integer = 1 To 5 Threading.Thread.Sleep(1000) Console.WriteLine("...スレッド") Next End Sub End Module
取敢えず、実行結果がコンソールに以下の様に表示されます。
スレッド開始 Main終了 ...スレッド ...スレッド ...スレッド ...スレッド ...スレッド
それでは Main 関数の中でスレッドの終了を待つにはどうすればいいのでしょうか。 そのために Thread クラスには Join メソッドがあります。 この Join をスレッドの実行側で呼出せば、呼び出し側の処理がブロックされます。
(Join 実行した場合に制御が戻ってこない)
先ほどの例に Join を追加した例を示します。
スレッドの実行を待つプログラム
Imports System Imports System.Threading Module ThreadStop 'メイン関数(エントリーポイント) Sub Main() 'スレッド生成 Dim pThread As Thread = New Thread(AddressOf ThreadProc) Console.WriteLine("スレッド開始") 'スレッド開始 pThread.Start() 'スレッド終了待機 pThread.Join() Console.WriteLine("Main終了") End Sub 'スレッド関数 Sub ThreadProc() For i As Integer = 1 To 5 Threading.Thread.Sleep(1000) Console.WriteLine("...スレッド") Next End Sub End Module
実行結果は以下の様に「Main終了」が最後に表示されます。
スレッド開始 ...スレッド ...スレッド ...スレッド ...スレッド ...スレッド Main終了
Join の替わりにスレッドが生きているかどうかのフラグを見て以下の様にしても同じです。
スレッドの実行を待つプログラム2
Imports System Imports System.Threading Module ThreadStop 'メイン関数(エントリーポイント) Sub Main() 'スレッド生成 Dim pThread As Thread = New Thread(AddressOf ThreadProc) Console.WriteLine("スレッド開始") 'スレッド開始 pThread.Start() 'スレッド終了待機 While pThread.IsAlive = True Threading.Thread.Sleep(10) End While Console.WriteLine("Main終了") End Sub 'スレッド関数 Sub ThreadProc() For i As Integer = 1 To 5 Threading.Thread.Sleep(1000) Console.WriteLine("...スレッド") Next End Sub End Module
それでは標題にある様にスレッドを停止させる方法についてですが、 Abort メソッドを使用します。 スレッドの中で非常に時間の掛かる処理を行わせて、途中で停止させる例を以下に示します。スレッドの実行を途中で停止させるプログラム
Imports System Imports System.Threading Module ThreadStop 'メイン関数(エントリーポイント) Sub Main() 'スレッド生成 Dim pThread As Thread = New Thread(AddressOf ThreadProc) Console.WriteLine("スレッド開始") 'スレッド開始 pThread.Start() '10秒待つ Threading.Thread.Sleep(10000) 'スレッド停止 pThread.Abort() 'スレッド終了待機 pThread.Join() Console.WriteLine("Main終了") End Sub 'スレッド関数 Sub ThreadProc() For i As Integer = 1 To 1000 '時間の掛かる処理 Threading.Thread.Sleep(1000) Console.WriteLine("...スレッド") Next End Sub End Module
実行結果は以下の様になり「...スレッド」が10回表示されてからスレッドが停止していることが分かります。
スレッド開始 ...スレッド ...スレッド ...スレッド ...スレッド ...スレッド ...スレッド ...スレッド ...スレッド ...スレッド ...スレッド Main終了
スレッドをいきなり終了させるのも怖いので、スレッドに対してフラグで指示を行い、 スレッドの中でフラグを見て終了させるようにしてみます。
尚、スレッドを含む処理をクラスの中に入れ込んで、 その中で開始メソッドや停止メソッドを作成します。スレッドの実行を途中で停止させるクラスの導入
Imports System Imports System.Threading Module ThreadStop 'メイン関数(エントリーポイント) Sub Main() 'スレッド用クラスの生成 Dim pclsThread As New clsThread 'スレッド開始 pclsThread.Start() Console.WriteLine("スレッド開始") '10秒待つ Threading.Thread.Sleep(10000) 'スレッド停止 pclsThread.Abort() Console.WriteLine("Main終了") End Sub 'スレッド用クラス Class clsThread 'スレッド Private mThread As Thread = Nothing 'スレッド停止フラグ Private mblnStop As Boolean = False '開始メソッド Sub Start() mThread = New Thread(AddressOf ThreadProc) 'スレッド開始 mThread.Start() End Sub '停止メソッド Sub Abort() 'スレッド停止フラグON mblnStop = True 'スレッド停止を待つ mThread.Join() End Sub 'スレッド関数 Private Sub ThreadProc() For i As Integer = 1 To 1000 If Me.mblnStop = True Then 'スレッド停止フラグONならば処理中断 Exit For End If Threading.Thread.Sleep(1000) Console.WriteLine("...スレッド") Next End Sub End Class End Module
-
スレッドの関数に引数を渡して別々の処理をさせたい場合があると思いますが、 スレッドを生成する時にアドレス指定のところに ParameterizedThreadStart デリゲートを使うことで可能です。
以下に実行プログラムの例を示します。引数があるスレッドの実行プログラム
Imports System Imports System.Threading Module ThreadParam 'メイン関数(エントリーポイント) Sub Main() '[ParameterizedThreadStart]デリゲートを使ったスレッド生成 Dim pThread As Thread = New Thread(New ParameterizedThreadStart(AddressOf ThreadParam)) '(ParameterizedThreadStart)の指定が無くてもOKですが 'Dim pThread As Thread = New Thread(AddressOf ThreadParam) 'スレッド開始 pThread.Start("スレッド...") Console.WriteLine("スレッド開始") End Sub '引数を持つスレッド関数(引数は1個のオブジェクトのみ) Sub ThreadParam(ByVal obj As Object) For i As Integer = 1 To 5 Threading.Thread.Sleep(1000) Console.WriteLine(obj.ToString()) Next End Sub End Module
取敢えず、実行結果がコンソールに以下の様に表示されます。
スレッド開始 スレッド... スレッド... スレッド... スレッド... スレッド...
ParameterizedThreadStart の利用は引数が1個なので複数のデータを渡す場合には 配列等(リストオブジェクト等)にして、スレッド関数側で配列の内容について処理を規定する必要があります。
以下に引数をオブジェクトの配列として渡す場合の例を示します。引数があるスレッドの実行プログラム2
Imports System Imports System.Threading Module ThreadParam 'メイン関数(エントリーポイント) Sub Main() '[ParameterizedThreadStart]デリゲートを使ったスレッド生成 Dim pThread As Thread = New Thread(New ParameterizedThreadStart(AddressOf ThreadParam)) 'オブジェクト配列データ設定 Dim arrObj(2) As Object arrObj(0) = "1111" arrObj(1) = 12345 arrObj(2) = True 'スレッド開始 pThread.Start(arrObj) Console.WriteLine("スレッド開始") End Sub '引数を持つスレッド関数(引数は1個のオブジェクトのみ) Sub ThreadParam(ByVal obj As Object) '引数をオブジェクト配列として扱う Dim pArr As Object() = CType(obj, Object()) For i As Integer = 0 To pArr.Length - 1 Threading.Thread.Sleep(1000) Console.WriteLine(pArr(i).ToString()) Next End Sub End Module
引数にして渡さなくても、スレッド実行をラップする様なクラスを導入して コンストラクタの引数でデータ型を規定することもできます。
以下に、スレッドを実行するためのクラスを導入した例を示します。スレッドの引数としてクラスでラップした実行プログラム
Imports System Imports System.Threading Module ThreadParam 'メイン関数(エントリーポイント) Sub Main() 'スレッド用クラスの生成 Dim pclsThread As New clsThread(12345, "AAA", False) 'スレッド開始 pclsThread.Start() Console.WriteLine("スレッド開始") End Sub 'スレッド用クラス Class clsThread 'クラス生成時に退避されるデータ群 Private mintData As Integer Private mstrData As String Private mblnData As Boolean 'コンストラクタ Sub New(ByVal aintData As Integer, ByVal astrData As String, ByVal ablnData As Boolean) Me.mintData = aintData Me.mstrData = astrData Me.mblnData = ablnData End Sub '内部スレッドの開始メソッド Sub Start() Dim pThread As Thread = New Thread(AddressOf ThreadProc) 'スレッド開始 pThread.Start() End Sub 'スレッド関数(この関数はプライベート) Private Sub ThreadProc() Threading.Thread.Sleep(1000) Console.WriteLine("Integer Data = " & Me.mintData) Threading.Thread.Sleep(1000) Console.WriteLine("String Data = " & Me.mstrData) Threading.Thread.Sleep(1000) Console.WriteLine("Boolean Data = " & Me.mblnData) End Sub End Class End Module
実行結果は以下の様に表示されます。
スレッド開始 Integer Data = 12345 String Data = AAA Boolean Data = False
スレッドを含む処理をクラスの中に入れ込んで、 その中で開始や停止などを行った方がスレッド処理を呼出す側との処理の分離がうまくいくと思います。
-
名前付きパイプを使った複数クライアントプログラムとの通信の説明は以下の記事で行いましたが、 今回はこれにクライアントとの双方向通信が出来る様に変更してみました。
⇒名前付きパイプを使ったプロセス間通信についてその2(複数クライアントとの通信)
双方向と言っても、クライアント側からサーバー側に送信されてきた文字列に、応答の為に少しの文字列を付加してクライアント側に送信する処理を考えます。
サーバー側の変更個所は以下の通りです。- 通信スレッドの中でストリーム読込を生成を行った後で、ストリーム書込を生成します。
- 処理ループの中でストリーム読込を行った後で、受信した文字列に「...OK」の文字列を付加してストリーム書込を行います。
サーバー側プログラム
Imports System.Threading Imports System.IO Imports System.IO.Pipes Public Class frmNameServer '別スレッドからメッセージを処理するためデリゲートを利用 Delegate Sub SetRichTextBox1Delegate(ByVal Value As String) Private RichTextBox1Delegate As New SetRichTextBox1Delegate(AddressOf _ AppendTextRichTextBox1) 'リッチテキストボックスにメッセージを表示する Private Sub AppendTextRichTextBox1(ByVal message As String) RichTextBox1.AppendText(message) RichTextBox1.SelectionStart = RichTextBox1.TextLength End Sub '別スレッドからボタンの許可設定処理するためデリゲートを利用 Delegate Sub SetButton1EnabledDelegate(ByVal bln As Boolean) Private SetButton1Delegate As New SetButton1EnabledDelegate(AddressOf _ SetButton1Enabled) '[Buttin1]のEnabled設定 Private Sub SetButton1Enabled(ByVal blnEnabled As Boolean) Me.Button1.Enabled = blnEnabled End Sub 'サーバスレッドの数 Private Const numThreads As Integer = 3 'サーバスレッド配列 Private arrServer(numThreads - 1) As Thread 'サーバスレッド終了フラグ件数 Private intServerFinish As Integer = 0 '[Start NamePipe]ボタンクリックイベント Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Button1.Click intServerFinish = 0 '名前付きパイプによる通信スレッド開始 For i = 0 To UBound(arrServer) arrServer(i) = New Thread(AddressOf ServerThread) arrServer(i).Start() Next AppendTextRichTextBox1("名前付きパイプ通信待機中・・・" & vbNewLine) '[Start NamePipe]ボタン不可設定 Me.Button1.Enabled = False End Sub '名前付きパイプによる通信スレッド Private Sub ServerThread() 'サーバー用のパイプを双方向でクライアント数は指定値で生成 Dim pipeServer As New NamedPipeServerStream("PIPE_TEST", PipeDirection.InOut, numThreads) Try 'パイプスレッド取得 Dim threadId As Integer = Thread.CurrentThread.ManagedThreadId Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ生成完了(スレッドID:" & threadId & ")" & vbNewLine}) 'パイプの接続を待つ pipeServer.WaitForConnection() Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ接続完了(スレッドID:" & threadId & ")" & vbNewLine}) 'ストリーム読込を生成 Dim pipeStreamReader As New StreamReader(pipeServer) 'ストリーム書込を生成 Dim pipeStreamWriter As New StreamWriter(pipeServer) pipeStreamWriter.AutoFlush = True Try While True 'パイプからの文字列受信(入力) Dim strRead As String = pipeStreamReader.ReadLine() 'ストリームの末尾に到達した場合はNullなので、通信を終了する If strRead = "" Then Exit While End If '受信文字列の表示 Me.Invoke(RichTextBox1Delegate, _ New Object() {"受信文字列(スレッドID:" & threadId & "):" & strRead & vbNewLine}) '受信文字列に「...OK」を追加して返す Dim strTX As String = strRead & "...OK" pipeStreamWriter.WriteLine(strTX) Me.Invoke(RichTextBox1Delegate, _ New Object() {"受信文字列(スレッドID:" & threadId & "):" & strTX & vbNewLine}) End While Catch ex As IOException MsgBox(ex.Message) Finally pipeStreamReader.Close() '基になるストリームも閉じられるので 'pipeStreamWriter.Close() 'この close はエラーになるので実行しない!! End Try Me.Invoke(RichTextBox1Delegate, New Object() {"パイプ通信の終了" & vbNewLine}) intServerFinish += 1 If intServerFinish >= numThreads Then '[Button1]の許可設定 Me.Invoke(SetButton1Delegate, New Object() {True}) End If Catch ex As Exception MsgBox(ex.Message) Finally pipeServer.Close() End Try End Sub End Class
クライアント側の変更個所は以下の通りです。- パイプ用ストリーム読込オブジェクトを静的変数で宣言し、[Start Named Pipe]ボタンクリック時イベントで生成します。
- [Send Message]ボタンクリック時イベントでサーバーへ文字列を送信後、空文字でなければサーバーからの応答を受信し表示します。 尚、送信文字列が空文字の場合は、パイプ用ストリーム読込オブジェクトをCloseします。
- フォーム上に受信文字列を表示するTEXTBOXを追加します。
クライアント側プログラム
Imports System.IO.Pipes Imports System.IO Imports System.Security.Principal Public Class frmNameClient '名前付きパイプ・クライアント Private pipeStream As NamedPipeClientStream = Nothing 'パイプ用ストリーム書込 Private pipeSw As StreamWriter = Nothing 'パイプ用ストリーム読込 Private pipeSr As StreamReader = Nothing 'フォームロード時イベント Private Sub frmNameClient_Load(sender As Object, e As EventArgs) Handles Me.Load '[Start Named Pipe]ボタンを許可設定 Me.Button1.Enabled = True '[Send Message]ボタンを不可設定 Me.Button2.Enabled = False End Sub '[Start Named Pipe]ボタンクリック時イベント Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Try '名前付きパイプ・クライアント生成 pipeStream = New NamedPipeClientStream(".", "PIPE_TEST", _ PipeDirection.InOut, PipeOptions.None, _ TokenImpersonationLevel.Impersonation) '名前付きパイプ接続 pipeStream.Connect(1000) 'ストリーム書込生成 pipeSw = New StreamWriter(pipeStream) pipeSw.AutoFlush = True 'ストリーム読込生成 pipeSr = New StreamReader(pipeStream) '[Start Named Pipe]ボタンを不可設定 Me.Button1.Enabled = False '[Send Message]ボタンを許可設定 Me.Button2.Enabled = True Catch ex As Exception MsgBox(ex.Message) End Try End Sub '[Send Message]ボタンクリック時イベント Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click Try '送信文字列取得 Dim strTX As String = Me.TextBox1.Text.Trim Me.TextBox1.Text = "" '送信 pipeSw.WriteLine(strTX) If strTX = "" Then 'ストリーム書込を閉じる pipeSw.Close() 'ストリーム読込を閉じる pipeSr.Close() '名前付きパイプを閉じる pipeStream.Close() '[Start Named Pipe]ボタンを許可設定 Me.Button1.Enabled = True '[Send Message]ボタンを不可設定 Me.Button2.Enabled = False Else '送信後、サーバーからの応答を受信 Dim strRead As String = pipeSr.ReadLine '受信文字列を表示 Me.TextBox2.Text = strRead End If Catch ex As Exception MsgBox(ex.Message) End Try End Sub 'フォームクローズ時イベント Private Sub frmNameClient_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed 'ストリーム書込を閉じる If Not pipeSw Is Nothing Then pipeSw.Close() End If 'ストリーム読込を閉じる If Not pipeSr Is Nothing Then pipeSr.Close() End If '名前付きパイプを閉じる If Not pipeStream Is Nothing Then pipeStream.Close() End If End Sub End Class
サーバー側のEXEを実行した後、クライアントEXEを3個実行します。 その後、サーバー側の「Start NamePipe」ボタンを押下して、各クライアントの「Start Name Pipe」ボタンを順次押下します。 各クライアント側で送信文字列を入力して「Send Message」ボタンを押下した結果の画像です。 送信した文字列に「...OK」の文字列が追加されて送信されてくる様子が分かります。
尚、以下は各クライアント側で送信文字列をクリアして「Send Message」ボタンを押下して一連の処理を終了した結果の画像です。関連する記事
⇒名前付きパイプを使ったプロセス間通信について
⇒名前付きパイプを使ったプロセス間通信についてその2(複数クライアントとの通信)
⇒Remoting の IPC を使ったプロセス間通信について
⇒Remoting の IPC を使ったプロセス間通信についてその2(HTTPチャネル)
-
名前付きパイプを使ったクライアントプログラムとの通信の説明は以下の記事で行いましたが、 今回はこれを複数のクライアントとの通信が出来る様に変更してみました。
⇒名前付きパイプを使ったプロセス間通信について
変更個所は、通信開始の時にサーバー処理のスレッドを複数立ち上げて、別々のクライアントからの受信をそれぞれのスレッドで受ける様にします。 サーバースレッドの終わりでは、スレッドの終了をスレッドの個数分終わった時点で、再度の通信開始のボタンを押下出来る様にしています。
今回のサーバースレッドの起動数は3個に抑えてありますし、スレッドの終了判定などが適当ですので改良の余地はありますが。サーバー側プログラム
Imports System.Threading Imports System.IO Imports System.IO.Pipes Public Class frmNameServer '別スレッドからメッセージを処理するためデリゲートを利用 Delegate Sub SetRichTextBox1Delegate(ByVal Value As String) Private RichTextBox1Delegate As New SetRichTextBox1Delegate(AddressOf _ AppendTextRichTextBox1) 'リッチテキストボックスにメッセージを表示する Private Sub AppendTextRichTextBox1(ByVal message As String) RichTextBox1.AppendText(message) RichTextBox1.SelectionStart = RichTextBox1.TextLength End Sub '別スレッドからボタンの許可設定処理するためデリゲートを利用 Delegate Sub SetButton1EnabledDelegate(ByVal bln As Boolean) Private SetButton1Delegate As New SetButton1EnabledDelegate(AddressOf _ SetButton1Enabled) '[Buttin1]のEnabled設定 Private Sub SetButton1Enabled(ByVal blnEnabled As Boolean) Me.Button1.Enabled = blnEnabled End Sub 'サーバスレッドの数 Private Const numThreads As Integer = 3 'サーバスレッド配列 Private arrServer(numThreads - 1) As Thread 'サーバスレッド終了フラグ件数 Private intServerFinish As Integer = 0 '[Start NamePipe]ボタンクリックイベント Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Button1.Click intServerFinish = 0 '名前付きパイプによる通信スレッド開始 For i = 0 To UBound(arrServer) arrServer(i) = New Thread(AddressOf ServerThread) arrServer(i).Start() Next AppendTextRichTextBox1("名前付きパイプ通信待機中・・・" & vbNewLine) '[Start NamePipe]ボタン不可設定 Me.Button1.Enabled = False End Sub '名前付きパイプによる通信スレッド Private Sub ServerThread() 'サーバー用のパイプを双方向でクライアント数は指定値で生成 Dim pipeServer As New NamedPipeServerStream("PIPE_TEST", PipeDirection.InOut, numThreads) Try 'パイプスレッド取得 Dim threadId As Integer = Thread.CurrentThread.ManagedThreadId Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ生成完了(スレッドID:" & threadId & ")" & vbNewLine}) 'パイプの接続を待つ pipeServer.WaitForConnection() Me.Invoke(RichTextBox1Delegate, New Object() {"パイプ接続完了(スレッドID:" & threadId & ")" & vbNewLine}) 'ストリーム読込を生成 Dim pipeStreamReader As New StreamReader(pipeServer) Try While True 'パイプからの文字列受信(入力) Dim strRead As String = pipeStreamReader.ReadLine() 'ストリームの末尾に到達した場合はNullなので、通信を終了する 'If strRead Is Nothing Then If strRead = "" Then Exit While End If '受信文字列の表示 Me.Invoke(RichTextBox1Delegate, New Object() {"受信文字列(スレッドID:" & threadId & "):" & strRead & vbNewLine}) End While Catch ex As IOException MsgBox(ex.Message) Finally pipeStreamReader.Close() End Try 'invokeTextBox(dlg, "パイプ通信を終了します。") Me.Invoke(RichTextBox1Delegate, New Object() {"パイプ通信の終了" & vbNewLine}) intServerFinish += 1 If intServerFinish >= numThreads Then '[Button1]の許可設定 Me.Invoke(SetButton1Delegate, New Object() {True}) End If Catch ex As Exception MsgBox(ex.Message) Finally pipeServer.Close() End Try End Sub End Class
クライアント側プログラムは「名前付きパイプを使ったプロセス間通信について」のものと変わっていません。クライアント側プログラム
Imports System.IO Imports System.IO.Pipes Imports System.Security.Principal Public Class frmNameClient '[Start Named Pipe]ボタンクリック時イベント Private Sub ButtonSend_Click(sender As Object, e As EventArgs) _ Handles ButtonSend.Click Dim pipeStream As NamedPipeClientStream = Nothing Dim pipeSw As StreamWriter = Nothing Try '[Send Message]ボタンを不可設定 Me.ButtonSend.Enabled = False '名前付きパイプ・クライアント生成 pipeStream = New NamedPipeClientStream(".", "PIPE_TEST", _ PipeDirection.InOut, _ PipeOptions.None, _ TokenImpersonationLevel.Impersonation) '名前付きパイプ接続 pipeStream.Connect(1000) 'ストリームライタ pipeSw = New StreamWriter(pipeStream) pipeSw.AutoFlush = True '文字列送信 pipeSw.WriteLine(Me.TextBox1.Text.Trim) Catch ex As Exception MsgBox(ex.Message) Finally 'ストリームライタを閉じる If Not pipeSw Is Nothing Then pipeSw.Close() End If '名前付きパイプを閉じる If Not pipeStream Is Nothing Then pipeStream.Close() End If '[Send Message]ボタンを許可設定 Me.ButtonSend.Enabled = True End Try End Sub End Class
サーバー側のEXEを実行した後、クライアントEXEを3個実行します。 その後、サーバー側の「Start NamePipe」ボタンを押下して、各クライアントの「Start Name Pipe」ボタンを順次押下します。 各クライアント側で送信文字列を入力して「Send Message」ボタンを押下した結果の画像です。
尚、以下は各クライアント側で送信文字列をクリアして「Send Message」ボタンを押下して一連の処理を終了した結果の画像です。関連する記事
⇒Remoting の IPC を使ったプロセス間通信について
⇒Remoting の IPC を使ったプロセス間通信についてその2(HTTPチャネル)
⇒名前付きパイプを使ったプロセス間通信について
⇒名前付きパイプを使ったプロセス間通信についてその3(クライアントとの双方向通信)