[2019/04/24] 名前付きパイプを使ったプロセス間通信についてその2(複数クライアントとの通信) (No.94)
[2019/03/08] 名前付きパイプを使ったプロセス間通信について (No.88)
[2019/02/06] 日付型データの比較について (No.78)
[2019/02/01] 日付型データの使い方(月末、年末の日付取得) (No.77)
-
名前付きパイプを使った複数クライアントプログラムとの通信の説明は以下の記事で行いましたが、 今回はこれにクライアントとの双方向通信が出来る様に変更してみました。
⇒名前付きパイプを使ったプロセス間通信についてその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チャネル)
PR -
名前付きパイプを使ったクライアントプログラムとの通信の説明は以下の記事で行いましたが、 今回はこれを複数のクライアントとの通信が出来る様に変更してみました。
⇒名前付きパイプを使ったプロセス間通信について
変更個所は、通信開始の時にサーバー処理のスレッドを複数立ち上げて、別々のクライアントからの受信をそれぞれのスレッドで受ける様にします。 サーバースレッドの終わりでは、スレッドの終了をスレッドの個数分終わった時点で、再度の通信開始のボタンを押下出来る様にしています。
今回のサーバースレッドの起動数は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(クライアントとの双方向通信)
-
名前付きパイプは、パイプサーバーと複数のパイプクライアントとの間でのプロセス間通信ができます。 名前付きパイプは、メッセージ単位で通信するモードと、バイトストリームとして読み書きするモードのいずれかが選べます。
どちらのモードでも、名前付きパイプを生成し、接続した後は、サーバー側・クライアント側とも通常のファイル入出力を使うことで通信が行われます。
名前付きパイプの例として以下の2通りの方法を示します。■1回の通信毎に名前付きパイプを生成する方法
サーバー側では、名前付きパイプ処理を行うスレッドを立ち上げて、そのスレッドの中で通信処理を行います。
フォームに通信開始ボタンと、受信メッセージ等の表示用にリッチテキストボックスを設置します。
スレッド処理では NamedPipeServerStream によりサーバー用名前付きパイプクラスを生成します。
その後 WaitForConnection メソッドで接続を待ち StreamReader によりパイプからの受信文字列を取得します。 取得文字列をリッチテキストボックスにデリゲートで表示し、すぐに StreamReader とサーバー用名前付きパイプクラスを閉じます。
サーバー側プログラム
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 'カレット位置までスクロール RichTextBox1.ScrollToCaret() 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 '[Start NamePipe]ボタンクリックイベント Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles Button1.Click '名前付きパイプによる通信スレッド開始 Dim server As New Thread(AddressOf ServerThread) server.Start() AppendTextRichTextBox1("名前付きパイプ通信待機中..." & vbNewLine) '[Start NamePipe]ボタン不可設定 Me.Button1.Enabled = False End Sub '名前付きパイプによる通信スレッド Private Sub ServerThread() 'サーバー用のパイプを双方向でクライアント数は2個で生成 Dim pipeServer As New NamedPipeServerStream("PIPE_TEST", PipeDirection.InOut, 2) Try 'パイプスレッド取得 Dim threadId As Integer = Thread.CurrentThread.ManagedThreadId Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ生成完了(スレッドID:" & threadId & ")" & vbNewLine}) 'パイプの接続を待つ pipeServer.WaitForConnection() Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ接続完了" & vbNewLine}) 'ストリーム読込を生成 Dim pipeStreamReader As New StreamReader(pipeServer) Try 'パイプからの文字列受信(入力) Dim strRead As String = pipeStreamReader.ReadLine() '受信文字列の表示 Me.Invoke(RichTextBox1Delegate, _ New Object() {"受信文字列:" & strRead & vbNewLine}) Catch ex As IOException MsgBox(ex.Message) Finally pipeStreamReader.Close() End Try Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ通信の終了" & vbNewLine}) '[Button1]の許可設定 Me.Invoke(SetButton1Delegate, New Object() {True}) Catch ex As Exception MsgBox(ex.Message) Finally pipeServer.Close() End Try End Sub End Class
クライアント側プログラムは送信ボタンとメッセージ入力用にテキストボックスを設置します。
送信ボタンを押下した時に NamedPipeClientStream によりクライアント用名前付きパイプを生成します。
そのクラスの Connect メソッドで接続後ストリームライタで1回のみ文字列送信を行います。
送信後、ストリームライタとクライアント用名前付きパイプを閉じます。
クライアント側プログラム
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プログラムの実行は以下の様になります。
■名前付きパイプを生成後、複数回の通信を行う方法
1回の通信毎に名前付きパイプを生成するのでは効率が悪いですし、連続での通信ができませんので サーバー側のスレッド部分を複数回の通信が出来る様に変更します。
変更した部分は、スレッド内でストリーム読込を生成をした後で、ループを形成しパイプからの文字列受信を繰り返す様にしています。 受信した文字列がNULLの場合にストリームライタとクライアント用名前付きパイプを閉じます。
サーバー側プログラム
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 '[Start NamePipe]ボタンクリックイベント Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click '名前付きパイプによる通信スレッド開始 Dim server As New Thread(AddressOf ServerThread) server.Start() AppendTextRichTextBox1("名前付きパイプ通信待機中・・・" & vbNewLine) '[Start NamePipe]ボタン不可設定 Me.Button1.Enabled = False End Sub '名前付きパイプによる通信スレッド Private Sub ServerThread() 'サーバー用のパイプを双方向でクライアント数は2個で生成 Dim pipeServer As New NamedPipeServerStream("PIPE_TEST", PipeDirection.InOut, 2) Try 'パイプスレッド取得 Dim threadId As Integer = Thread.CurrentThread.ManagedThreadId Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ生成完了(スレッドID :" & threadId & ")" & vbNewLine}) 'パイプの接続を待つ pipeServer.WaitForConnection() Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ接続完了" & 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() {"受信文字列:" & strRead & vbNewLine}) End While Catch ex As IOException MsgBox(ex.Message) Finally pipeStreamReader.Close() End Try Me.Invoke(RichTextBox1Delegate, _ New Object() {"パイプ通信の終了" & vbNewLine}) '[Button1]の許可設定 Me.Invoke(SetButton1Delegate, New Object() {True}) Catch ex As Exception MsgBox(ex.Message) Finally pipeServer.Close() End Try End Sub End Class
クライアント側プログラムではパイプ開始ボタンとメッセージ送信ボタンを設置します。
パイプ開始ボタン押下で名前付きパイプ・クライアント生成と接続、及びストリームライタ生成を行います。
この生成されるクラスは、メッセージ送信ボタン押下時にも使用されるため、関数の外で静的変数として宣言します。
メッセージ送信ボタン押下時にはストリームライタでメッセージを送信します。 メッセージが空白の場合はストリームライタ、名前付きパイプ・クライアントを閉じます。
クライアント側プログラム
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 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 '[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 pipeSw.WriteLine(strTX) If strTX = "" Then 'ストリームライタを閉じる pipeSw.Close() '名前付きパイプを閉じる pipeStream.Close() '[Start Named Pipe]ボタンを許可設定 Me.Button1.Enabled = True '[Send Message]ボタンを不可設定 Me.Button2.Enabled = False 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 pipeStream Is Nothing Then pipeStream.Close() End If End Sub End Classクライアント側でフォームを閉じることでパイプの接続が閉じるため、サーバー側に通信の終了が通知できます。
プログラムの実行は以下の様になります。関連する記事
⇒Remoting の IPC を使ったプロセス間通信について
⇒Remoting の IPC を使ったプロセス間通信についてその2(HTTPチャネル)
⇒名前付きパイプを使ったプロセス間通信についてその2(複数クライアントとの通信)
⇒名前付きパイプを使ったプロセス間通信についてその3(クライアントとの双方向通信)
-
今回は日付型データの比較を取り扱います。日付型データはその変数名を使って直接 If 文等で比較することが出来ます。 数値データの様に日付型データを比較演算子で結んで値の大小等が比較できます。
以下の例は2個の日付型データに 2019/02/01 と 2019/02/02 の異なる日付を設定し、等しいかどうかの判定を If 文で行っています。簡単な日付型(Date)データの比較
Dim dateWK1 As Date = New DateTime(2019, 2, 1) '2019/02/01 Dim dateWK2 As Date = New DateTime(2019, 2, 2) '2019/02/02 '2個の日時の比較 If dateWK1 = dateWK2 Then Console.WriteLine("dateWK1 と dateWK2 は等しい") Else Console.WriteLine("dateWK1 と dateWK2 は異なる") End If Console.WriteLine("dateWK1:" & dateWK1.ToString("yyyy/MM/dd HH:mm:ss")) Console.WriteLine("dateWK2:" & dateWK2.ToString("yyyy/MM/dd HH:mm:ss"))実行結果は当然、2個の日付型データが異なっていることが分かります。
dateWK1 と dateWK2 は異なる dateWK1:2019/02/01 00:00:00 dateWK2:2019/02/02 00:00:00
上の例では問題無く判定が行われましたが、以下の例を見て下さい。 上の例の様に、2個の日付型データは変数を Date として宣言し、 最初の変数に Now() 関数で現在日時を設定し、さらにその後10秒待った後で、2個目の変数に現在日時を設定しています。 その後、2個の変数を比較すると、やはり異なっているとの判定がされます。
2個の変数の中身を表示してやれば、最初の時刻から10秒経過した時刻が設定されていることが分かります。
これは当然と言えばそうなのですが、変数が Date 型だからと勘違いして、日付部分しか比較しないのかと考えると ミスが発生します。お恥ずかしい話、私自身も以前にはこれでバグを発生させたことがありました。日付型(Date)データの比較
'現在日時を取得 Dim dateWK1 As Date = Now() '10秒待つ System.Threading.Thread.Sleep(10 * 1000) '現在日時を取得 Dim dateWK2 As Date = Now() '2個の日時の比較 If dateWK1 = dateWK2 Then Console.WriteLine("dateWK1 と dateWK2 は等しい") Else Console.WriteLine("dateWK1 と dateWK2 は異なる") End If Console.WriteLine("dateWK1:" & dateWK1.ToString("yyyy/MM/dd HH:mm:ss")) Console.WriteLine("dateWK2:" & dateWK2.ToString("yyyy/MM/dd HH:mm:ss")) '日付型データの比較関数 Console.WriteLine("Date.Compare(dateWK1, dateWK2):" & Date.Compare(dateWK1, dateWK2))上記の処理を実行すると以下の様な表示が、「出力」ウインドウに表示されます。
dateWK1 と dateWK2 は異なる dateWK1:2019/02/06 18:16:49 dateWK2:2019/02/06 18:16:59 Date.Compare(dateWK1, dateWK2):-1
Date 型にはプロパティとして Date というものを持っているのでこれを使うと 純然たる日付のみのデータが取得できますので、日付のみの比較が正しく行えます。 以下のソースは If 文の部分を変更して実行してみました。日付型(Date)データの日付のみの比較
'現在日時を取得 Dim dateWK1 As Date = Now() '10秒待つ System.Threading.Thread.Sleep(10 * 1000) '現在日時を取得 Dim dateWK2 As Date = Now() '2個の日時の比較 If dateWK1.Date = dateWK2.Date Then Console.WriteLine("dateWK1 と dateWK2 は等しい") Else Console.WriteLine("dateWK1 と dateWK2 は異なる") End If上記の処理を実行すると以下の様な表示が、「出力」ウインドウに表示されます。
dateWK1 と dateWK2 は等しい
さらに日付型の Compare メソッドを使って比較処理ができます。
Date.Compare( para1, para2 ) ... 「-1, 0, 1」のInteger型の値を返します。 結果の値の意味は以下の通りです。 ・[-1] : para1 < para2 ・[ 0] : para1 = para2 ・[ 1] : para1 > para2
日付型の Compare メソッドでの比較
Dim date0 As New DateTime(2019, 8, 27, 10, 30, 0) Dim date1 As New DateTime(2019, 8, 26, 10, 10, 0) Dim date2 As New DateTime(2019, 8, 27, 10, 15, 0) Dim date3 As New DateTime(2019, 8, 28, 10, 20, 0) ' 日付のみで比較 Console.WriteLine("Compare(date1.Date, date0.Date) = {0}", Date.Compare(date1.Date, date0.Date)) Console.WriteLine("Compare(date2.Date, date0.Date) = {0}", Date.Compare(date2.Date, date0.Date)) Console.WriteLine("Compare(date3.Date, date0.Date) = {0}", Date.Compare(date3.Date, date0.Date)) ' 日付の時刻を含み比較 Console.WriteLine("Compare(date1, date0) = {0}", Date.Compare(date1, date0)) Console.WriteLine("Compare(date2, date0) = {0}", Date.Compare(date2, date0)) Console.WriteLine("Compare(date3, date0) = {0}", Date.Compare(date3, date0))上記の処理を実行すると以下の様な表示が、「出力」ウインドウに表示されます。
Compare(date1.Date, date0.Date) = -1 Compare(date2.Date, date0.Date) = 0 Compare(date3.Date, date0.Date) = 1 Compare(date1, date0) = -1 Compare(date2, date0) = -1 Compare(date3, date0) = 1
DateTime型の CompareTo メソッドを使っても同様の結果が得られます。
DateTime型の CompareTo メソッドでの比較
Dim date0 As New DateTime(2019, 8, 27, 10, 30, 0) Dim date1 As New DateTime(2019, 8, 26, 10, 10, 0) Dim date2 As New DateTime(2019, 8, 27, 10, 15, 0) Dim date3 As New DateTime(2019, 8, 28, 10, 20, 0) ' さらにDateTime型のCompareToを利用 Console.WriteLine("date1.Date.CompareTo(date0.Date) = {0}", date1.Date.CompareTo(date0.Date)) Console.WriteLine("date2.Date.CompareTo(date0.Date) = {0}", date2.Date.CompareTo(date0.Date)) Console.WriteLine("date3.Date.CompareTo(date0.Date) = {0}", date3.Date.CompareTo(date0.Date))
関連する記事
⇒日付型データの使い方 :[Date,DateTime]
⇒日付型データの使い方(月末、年末の日付取得)
-
日付を取り扱う処理は多いもので、請求処理や月締処理などで対象月の月末の日付を計算する場合があります。 また、年度の替わりが年末である場合には年末の日付を計算する必要があります。
月末で問題なのは2月で、うるう年の月末は29日のため、そのことを考える必要があります。
月末の取得は、私がよく使っているのですが指定された年月での月の最初日から翌月の最初日を計算し、 その結果の1日前を計算しています。 また、DateTime の DaysInMonth メソッドを使って該当年月の日数を使うことでも可能です。
それでは、指定された年月日に対応する月末の日付を取得する方法と、年末の日付を取得する方法を以下に示します。月末、年末の日付取得
'月末の取得 Dim dateWK As DateTime dateWK = Now() Console.WriteLine("現在:" & dateWK.ToString("yyyy/MM/dd")) dateWK = New Date(dateWK.Year, dateWK.Month, 1) '指定年月での最初日 dateWK = dateWK.AddMonths(1).AddDays(-1) '次月の1日前を計算 Console.WriteLine("月末の取得1:" & dateWK.ToString("yyyy/MM/dd")) '[DateTime.DaysInMonth]メソッドによる方法 dateWK = Me.DateTimePicker1.Value dateWK = New Date(dateWK.Year, dateWK.Month, DateTime.DaysInMonth(dateWK.Year, dateWK.Month)) Console.WriteLine("月末の取得2:" & dateWK.ToString("yyyy/MM/dd")) '年末の取得(直接12月31日指定) dateWK = Me.DateTimePicker1.Value dateWK = New Date(dateWK.Year, 12, 31) '指定年での最終日(直接12月31日指定) Console.WriteLine("年末の取得の取得1:" & dateWK.ToString("yyyy/MM/dd")) '年末の取得(指定年の翌年の元旦の1日前の計算) dateWK = Me.DateTimePicker1.Value dateWK = New Date(dateWK.Year, 1, 1) '指定年での最初日(元旦) dateWK = dateWK.AddYears(1).AddDays(-1) '次年の1日前を計算 Console.WriteLine("年末の取得の取得1:" & dateWK.ToString("yyyy/MM/dd"))上記の処理を実行すると以下の様な表示が、「出力」ウインドウに表示されます。
現在:2019/02/01 月末の取得1:2019/02/28 月末の取得2:2019/02/28 年末の取得の取得1:2019/12/31 年末の取得の取得1:2019/12/31
ところで、年の加算を行った場合にうるう年を跨いだ場合にどうなるのかが少し気になりましたので、 以下の様なソースで実行してみました。1年後の計算(AddYearsメソッド)
'1年後の計算(AddYearsメソッド) dateWK = New Date(2019, 2, 28) '指定年月:2019/02/28 dateWK = dateWK.AddYears(1) Console.WriteLine("うるう年では無い年の1年後:" & dateWK.ToString("yyyy/MM/dd")) dateWK = New Date(2020, 2, 28) '指定年月:2020/02/28 dateWK = dateWK.AddYears(1) Console.WriteLine("うるう年の場合の年の1年後:" & dateWK.ToString("yyyy/MM/dd")) dateWK = New Date(2020, 2, 29) '指定年月:2020/02/29 dateWK = dateWK.AddYears(1) Console.WriteLine("うるう日からの1年後:" & dateWK.ToString("yyyy/MM/dd")) dateWK = New Date(2019, 3, 1) '指定年月:2019/03/01 dateWK = dateWK.AddYears(1) Console.WriteLine("うるう日を挟む場合の1年後:" & dateWK.ToString("yyyy/MM/dd"))上記の処理を実行すると以下の様な表示が、「出力」ウインドウに表示されます。
2019/02/28 を指定した場合、2019年はうるう年では無いので、1年後の計算は当然 2020/02/28 になります。
2020/02/28 を指定した場合、2020年はうるう年なので 2020/02/29 があるので1日ずれるかと思いきや、1年後の計算は当然 2021/02/28 になります。 これはうるう日を特別に計算しているのか、もしくはただ単に年の数値を1加算しているのかはわかりません。
2020/02/29 を指定した場合でも結果は 1年後の計算は当然 2021/02/28 になります。やはり、うるう日を加味されている様です。
このことは、ある指定日からの1年間を計算する場合に問題になるかもしれません。 1年は365日とするのかそれともうるう日を含めるのかはその時々の仕様によると思います。うるう年では無い年の1年後:2020/02/28 うるう年の場合の年の1年後:2021/02/28 うるう日からの1年後:2021/02/28 うるう日を挟む場合の1年後:2020/03/01
関連する記事
⇒日付型データの使い方 :[Date,DateTime]
⇒日付型データの比較について
⇒文字列から数値型への変換(parse - tryparse)
⇒オブジェクト型から数値型への変換(TryParse)