-
前回のSerialPortコントロールを使い方その2では、 最初にこちら側からデータを送信し、その後で装置側からのデータ受信を行うことを想定していました。
⇒SerialPortコントロールの使い方その2
しかし、外部装置でもコマンドのハンドシェイクを行わないで、常にデータを送信してくるものもあります。
表現としては汚いのですが、いわゆる「垂れ流し」的にデータが向こうからやってくる場合があります。 今回はこの様な場合を想定した「垂れ流し」受信専門のクラスを作成してみます。
「その2」のクラスと初期化・終了処理は全く同様ですが、今回はデータ受信時のイベント処理が変更されています。 クラスの概要は以下の通りです。- 外部装置との通信は調歩同期とし、Shift-JIS文字列での送受信を対象とする。
- 通信ポート設定は「ボーレート:9600BPS、データビット:8、パリティビット:無し、ストップビット:1」とする。
- 送受信文字列の開始コードは「STX:0x02」、終了コードは「ETX:0x03」とする。
- 外部装置からのデータは常に「STX」「ETX」の間にデータがはさまれているとする。
- 受信文字列は4096バイトのバッファで行う。
SerialPortコントロールの使い方その3(通信用クラス)
Imports System.IO.Ports ''' ----------------------------------------------------------------------- '''
''' シリアル通信クラス ''' ''' ----------------------------------------------------------------------- Public Class ClsSerialRcv '''''' シリアルポートクラス ''' Private SerialPort As SerialPort '''''' 読込データ格納先 ''' '''データ受信イベントで蓄えられる Private CmdBuf() As Byte '''''' STX受信フラグ(装置の送信が開始された証拠) ''' Private fSTX As Boolean = False '''''' 最終受信文字列(受信データからSTX,ETXを除いたもの) ''' Private strLastRxData As String = "" '''''' 送受信文字列の開始コード(STX:0x02) ''' Private Const CMD_STX As Byte = &H2 '''''' 送受信文字列の終了コード(ETX:0x03) ''' Private Const CMD_ETX As Byte = &H3 ''' ----------------------------------------------------------------------- '''''' コンストラクタ ''' ''' <param name="SerialPort">シリアルポートコントロール</param> ''' ----------------------------------------------------------------------- Sub New(ByVal SerialPort As SerialPort) 'シリアルポートの退避 Me.SerialPort = SerialPort '受信イベントのハンドラ設定 AddHandler Me.SerialPort.DataReceived, AddressOf DataReceivedHandler End Sub ''' ----------------------------------------------------------------------- '''''' 最終受信データプロパティ ''' '''受信データ文字列 ''' ----------------------------------------------------------------------- ReadOnly Property LastRxData As String Get Return Me.strLastRxData End Get End Property ''' ----------------------------------------------------------------------- '''''' ポートオープン ''' '''True:正常終了, False:エラー ''' ----------------------------------------------------------------------- Function Open() As Boolean '戻り値初期化 Open = False Try 'ポートチェック If Me.SerialPort.IsOpen = False Then '未オープンならば、オープンする 'ポート設定(ここは通信相手に合わせる) Me.SerialPort.PortName = "COM7" Me.SerialPort.BaudRate = 9600 Me.SerialPort.DataBits = 8 Me.SerialPort.Parity = Parity.None Me.SerialPort.StopBits = StopBits.One 'オープン Me.SerialPort.Open() End If '結果を返す Open = Me.SerialPort.IsOpen Catch ex As Exception 'エラー処理 End Try End Function ''' ----------------------------------------------------------------------- '''''' ポートクローズ ''' '''True:正常終了, False:エラー ''' ----------------------------------------------------------------------- Function Close() As Boolean '戻り値初期化 Close = False Try 'ポートチェック If Me.SerialPort.IsOpen = True Then 'オープン済みならば、クローズする Me.SerialPort.Close() End If '結果を返す Close = Not (Me.SerialPort.IsOpen) Catch ex As Exception 'エラー処理 End Try End Function ''' ----------------------------------------------------------------------- '''''' データ受信ハンドラ ''' '''''' [STX]から始まるデータをコマンドバッファに格納し[ETX]受信時に文字列に変換 ''' ''' ----------------------------------------------------------------------- Private Sub DataReceivedHandler(sender As Object, e As SerialDataReceivedEventArgs) Try Dim SP As SerialPort = CType(sender, SerialPort) 'バッファのバイト数チェック Dim DataLen As Integer = SP.BytesToRead If DataLen = 0 Then Exit Sub End If 'データ長チェック If DataLen > 4096 Then DataLen = 4096 End If '読込バッファの確保 Dim Buf(DataLen - 1) As Byte '読込 SP.Read(Buf, 0, DataLen) ' '受信バッファを先頭からチェックする For i As Integer = 0 To Buf.Length - 1 Select Case Buf(i) Case CMD_STX If Me.fSTX = True Then '再度のSTXはコマンドバッファをクリア Me.CmdBuf = Nothing End If 'STXフラグON Me.fSTX = True Case CMD_ETX If Me.fSTX = True Then 'STXフラグONの場合 'コマンドバッファを文字列変換 Me.strLastRxData = _ System.Text.Encoding.GetEncoding(932).GetString(Me.CmdBuf) 'STXフラグOFF Me.fSTX = False End If 'コマンドバッファクリア Me.CmdBuf = Nothing Case Else '[STX][ETX]以外 If Me.fSTX = True Then 'STXフラグONの場合 Dim intIdx As Integer = 0 If Me.CmdBuf Is Nothing Then '格納バッファの領域確保 Me.CmdBuf = Array.CreateInstance(GetType(Byte), 1) '先頭指定 intIdx = 0 Else '後ろに追加する Dim Length As Integer = Me.CmdBuf.Length ReDim Preserve Me.CmdBuf(Length) '最後尾指定 intIdx = Length End If '1文字コピー Me.CmdBuf(intIdx) = Buf(i) End If End Select Next Catch ex As Exception 'エラー処理 End Try End Sub End Class
データ受信ハンドラでは、「STX」を受信するまでは受信されたデータを対象としていません。 「STX」受信でフラグをONし、そのフラグを見てONしていれば受信されたデータを対象とします。 「ETX」受信でも「STX」受信フラグがOFFであれば、「ETX」の処理は行いません。
このクラスを使用した例を以下のソースに示します。
SerialPortコントロールの使い方その3(通信用クラスの使用例)
Public Class frmSerialRcv 'シリアル通信クラス Private mclsSerialRcv As ClsSerialRcv '前回受信データ Private mstrLastData As String '''
''' フォームクローズイベント ''' Private Sub frmSerialRcv_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed 'シリアル通信のクローズ mclsSerialRcv.Close() End Sub '''''' フォームロードイベント ''' Private Sub frmSerialRcv_Load(sender As Object, e As EventArgs) Handles Me.Load 'シリアル通信クラスの生成 mclsSerialRcv = New ClsSerialRcv(Me.SerialPort1) 'シリアル通信のオープン If mclsSerialRcv.Open() = False Then 'オープンエラー(必要があれば処理する) End If 'タイマ設定 Me.Timer1.Interval = 100 '100msec Me.Timer1.Enabled = True End Sub '''''' 100msec毎のタイマ処理 ''' Private Sub Timer1_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles Timer1.Elapsed '最終受信データ取得 Dim strLastData As String = Me.mclsSerialRcv.LastRxData '前回処理データとの比較 If strLastData <> Me.mstrLastData Then 'データ表示 Me.RichTextBox1.Text &= strLastData & vbCrLf '前回値へ退避 Me.mstrLastData = strLastData End If End Sub End Class
このフォームは、シリアルコントロールとタイマコントロール及び、受信データ表示用のリッチテキストボックスを画面に張り付けてあります。
タイマコントロールのイベントは100msec毎に発生させて、受信データが変化したかを判定し、変化が在った場合にリッチテキストボックスに表示しています。
通信相手のシミュレーションとしてフリーソフトの「RS232Cテストツール」を使用しています。 VBのプログラムを実行後、「RS232Cテストツール」を起動します。「RS232Cテストツール」で「起動」ボタン押下後、 「送信TEXT」のボックスに送信文字列を入力し、「TEXT送信」ボタン押下で文字列が送信されます。
上図は以下の文字列を送信しています。- <STX>DATA=1001<ETX>
- <STX>DATA=2002<ETX>
- <STX>DATA=3003<ETX>
尚、特殊な例として以下の文字列を送信すると(後ろの ETX を送信していません)
- <STX>DATA=4004<ETX><STX>DATA=5555
内部的に「DATA=5555」が保留されています。
その後、 ETX のみを送信すると、最後のデータが表示されます。
- <ETX>
以下は「その2」でも載せていますが、今回も、通信相手用の仮想シリアルポートとしてcom0com というドライバを使用し、 2個のシリアルポートを作成し、ループバックで接続しています。 今回のプログラムをテストするためには外部装置か、もう一台別のPCが必要になりますが、 そんなことをしなくてもデバッグができるソフト(ドライバ)が在ります。
com0com というドライバで、PC上に仮想シリアルポートを2個作成し、 その2個をループバックできる様に仮想的に接続できるという優れものです。 以下のリンク先でソフトをダウンロードしてインストールしてみて下さい。 (尚、もう一台のPCなどがあれば必要ありませんが)
⇒「com0com ドライバ」
COM8側を通信相手側(外部装置)とするのですが、通信テスト用に以下のフリーソフトを使いました。
「のん」さんといわれる方が作成された「RS232Cテストツール」です。
以下のサイトにアクセスすれば、ダウンロードできます。
⇒「RS232Cテストツール」USB ⇒ RS232C 変換ケーブル
シリアル通信に関連してですが、 最近のPC、特にノートPCの場合ではRS232Cのコネクタがついていることは殆んどありません。
そのため、以下の様なUSBを使ったRS232Cの変換ケーブルが使えそうです。
⇒iBUFFALO USBシリアルケーブル(USBtypeA to D-sub9ピン)1.0m ブラックスケルトン BSUSRC0610BS
⇒サンワサプライ USB-RS232Cコンバータ USB-CVRS9
⇒Plugable USB‐9ピンRS232シリアルアダプター (Prolific社製 PL2303HX Rev Dチップセット採用)
関連する記事
⇒SerialPortコントロールの使用方法 :[SerialPort,Invoke]
⇒SerialPortコントロールの使い方その2
⇒SerialPortコントロールの使い方その4(データ受信時にイベントを発生させる)
⇒SerialPortコントロールの使い方その5(データ受信時及び、信号変化時にイベントを発生させる)
⇒SerialPortコントロールの使い方その6(ハンドシェィクによるデータ送受信)
おすすめ本
PR -
前回「その2」では「メイン処理をスレッドにしたもので行います」と記しましたが、実際のプログラム例を示します。
プログラムの手順としては以下の様になります。- OnStart ではイベントログに開始メッセージを登録し、メイン処理スレッドの生成を行う。
- OnStop ではイベントログに停止メッセージを登録し、メインスレッド停止依頼フラグONし、メイン処理スレッドの生成を行う。
- メイン処理スレッドでは停止依頼フラグがONになるまでループ処理を行う。ループ内では Sleep で待ちを入れる。
■サービスPG・メイン処理をスレッド化
Public Class TestService Protected Overrides Sub OnStart(ByVal args() As String) ' サービスを開始するコードをここに追加します。このメソッドによって、 ' サービスが正しく実行されるようになります Me.EventLog.WriteEntry("サービスを開始します。") 'メイン処理スレッドの生成を行う Me.mThreadMain = New Threading.Thread(New Threading.ThreadStart(AddressOf Me.MainProc)) Me.mThreadMain.Start() End Sub Protected Overrides Sub OnStop() ' サービスを停止するのに必要な終了処理を実行するコードをここに追加します。 Me.EventLog.WriteEntry("サービスを停止します。") 'メインスレッド停止依頼フラグON Me.mblnThreadStop = True 'メインスレッドの停止を待つ Me.mThreadMain.Join() End Sub ' スレッドクラス Private mThreadMain As Threading.Thread ' スレッド停止フラグ Private mblnThreadStop As Boolean = False ' メイン処理スレッド Private Sub MainProc() Me.EventLog.WriteEntry("MainProc...開始。") Try While mblnThreadStop = False ' ここに本来のサービス処理を書く ' 待ち時間を入れる(このプロセスがCPUを独占しない様に) Threading.Thread.Sleep(100) End While Catch ex As Exception ' エラー処理があればここに書く End Try Me.EventLog.WriteEntry("MainProc...停止。") End Sub End Class
このプログラムの重要な点は、サービスの「開始」が行われた時に、 メイン処理をスレッドとして起動を掛けて即座に「開始」処理から抜け出るところです。 こうするのは「開始」処理の中では時間の掛かる処理が実行できないからです。
また、メインスレッド内でも Sleep 関数での待ちを入れていますが、 これが無いとこのスレッドがCPU時間を独占することになりますので、必要になります。
しかし、まだこれではサービスプログラムの枠を作っただけで、実際に何も処理は起こりません。
サービスプログラムが何を行うのかが、決まっていないからですが、 上記のソース内の「ここに本来のサービス処理を書く」の部分に書いていくことになります。
さて、今回のサービスプログラムを実際に「開始」し、「停止」した場合のイベントログを見てみます。
「コントロールパネル」⇒「管理ツール」⇒「イベント ビューアー」で起動し、 実際にイベントログが登録されているかを見てみます。確かにログは登録されている様ですが、上から6件のログを一覧で見てみます。
ログの一覧を表示するには、Windowsの powershell を起動し Get-EventLog コマンドを使います。
それでは 「Windows + R」 で「ファイル名を指定して実行」を開き、 powershell を起動します。
(尚 Get-EventLog の詳しい説明はここでは行いません)
powershell 起動後、以下のコマンドをキー入力してログ一覧を行います。- Get-EventLog Application -Newest 6
ログを見ますと、期待した通りの結果が得られました。
次の記事ではメイン処理スレッドの実際の中身を組み込んでいきたいと思います。関連する記事
⇒サービスプログラムの作り方について・その1
⇒サービスプログラムの作り方について・その2
⇒サービスプログラムの作り方について・その4(メイン処理内で別のスレッドを呼出す)
おすすめ書籍
-
前回「その1」ではサービスの処理として何もしなかったのですが、今回は少しだけ処理を追加していきます。
タイマコントロールを追加し、タイマイベント処理の中で、Windowsのイベントログに時刻の出力を行います。
TestService.vb[デザイン] のフォームを開き、タイマコントロールを設置するのですが、 「ツールボックス」にはタイマコントロールが現れていないと思いますので、 「ツールボックス」のウインドウの上でマウス右クリックして「アイテムの選択」を実行します。 さらに「.NET Framework コンポーネント」の名前が Timer で、名前空間が System.Timers をチェックし「OK」を押下します。
System.Timers.Timer を TestService.vb[デザイン] へドラッグ&ドロップし、タイマコントロールを設置します。 Timer1 のプロパティで以下の設定にします。- Enabled:False
- Interval:10000 (10秒毎のタイマイベントの発生)
尚、ServiceBase の TestService のプロパティ「AutoLog」が「True」になっているのを確認して下さい。 デフォルトで「True」なのですが、Microsoftのサイトによりますと、以下の様に書かれています。既定では、すべての Windows サービス プロジェクトはアプリケーション イベント ログとやり取りして、 そこに情報および例外を書き込むことができます。
アプリケーションにこの機能が必要かどうかを指定するには、 AutoLog プロパティを使用します。
既定では、Windows サービス プロジェクト テンプレートで作成したサービスには、ログが有効にされます。
EventLog クラスの静的フォームを使用すると、 EventLog コンポーネントのインスタンスを作成したり、手動でソースを登録したりすることなく、ログにサービス情報を書き込むことができます。では今回のソースとしては以下の様になります。
OnStart ではイベントログに開始メッセージを登録し、タイマの開始を指示します。
OnStop ではイベントログに停止メッセージを登録し、タイマの停止を指示します。
タイマイベントの Timer1_Elapsed では現在時刻をイベントログに登録します。Public Class TestService Protected Overrides Sub OnStart(ByVal args() As String) ' サービスを開始するコードをここに追加します。このメソッドによって、 ' サービスが正しく実行されるようになります Me.EventLog.WriteEntry("サービスを開始します。") ' タイマの開始 Me.Timer1.Enabled = True Me.EventLog.WriteEntry("サービスを開始しました。") End Sub Protected Overrides Sub OnStop() ' サービスを停止するのに必要な終了処理を実行するコードをここに追加します。 Me.EventLog.WriteEntry("サービスを停止します。") ' タイマの停止 Me.Timer1.Enabled = False Me.EventLog.WriteEntry("サービスを停止しました。") End Sub ' タイマイベント処理 Private Sub Timer1_Elapsed(sender As Object, e As Timers.ElapsedEventArgs) Handles Timer1.Elapsed Try 'この段階では特に行う処理が無いのでイベントログへの出力を行う Me.EventLog.WriteEntry("タイマイベント:" & Now.ToString("yyyy/MM/dd HH:mm:ss")) Catch ex As Exception End Try End Sub End Class
特にどうといった処理ではないので簡単な処理です。
このサービスプログラムですが、何かイベントが起こるのをこのプログラムの裏?で待っているわけで、 今回はタイマコントロールによるイベントを受け付ける様になっています。
「コントロールパネル」⇒「管理ツール」⇒「イベント ビューアー」で起動し、 実際にイベントログが登録されているかを見てみます。イベントログに「タイマイベント:YYYY/MM/DD HH:mm:ss」が正しく登録されている様です。
尚、「AutoLog」が「True」の場合には、 OnStart OnStop などではシステムがイベントログにそれぞれのメッセージを登録するため、 本当は上記の「サービスを開始しました。」「サービスを停止しました。」の登録は必要ありません。
ところで、OnStart OnStop の処理は長時間実行可能なわけでは無く、すぐに終わらせる必要があります。 その為、OnStart の中ではタイマコントロールをEnable にして、すぐに処理を終わっています。
実際のところタイマコントロールでは面白くないので、別スレッドを起動して、スレッド側でメイン処理として行う方がいいと思います。 次の記事ではメイン処理をスレッドにしたもので行います。関連する記事
⇒サービスプログラムの作り方について・その1
⇒サービスプログラムの作り方について・その3(メイン処理をスレッド化)
⇒サービスプログラムの作り方について・その4(メイン処理内で別のスレッドを呼出す)
おすすめ書籍
-
サービスプログラムの作成を行うことがあったので、作成方法の最初の部分を記します。
サービスプログラムはフォームを持つプログラムとは全く性格が異なり、作成手順も異なりますので、 手順を追いながら作成していきます。 尚、今回の作業を行うには、Windowsの管理者権限でログインしておいてください。
先ず、VisualStudioを起動し、新しいプロジェクトの作成を行います。 「新しいプロジェクト」ウインドウが表示されたら、テンプレートの中のVisualBasicを選択し、更に Windowsサービス を選択すると以下の様な表示なります。
「名前」が「WindowsServices1」となっているのを、仮に「TestService」に変更し、「OK」ボタンを押下します。
尚、画面はプロジェクト名を「TestService」と変更し、更に「TestService.vb[デザイン]」のプロパティで、「ServiceName」も「TestService」に全て変更しました。
ここをクリックするとコードビューに切り替わります。のリンクをクリックします。 すると、Service1.vb の内容が以下の様に表示されます。
ソースとしては以下の様になります。
Public Class TestService Protected Overrides Sub OnStart(ByVal args() As String) ' サービスを開始するコードをここに追加します。このメソッドによって、 ' サービスが正しく実行されるようになります End Sub Protected Overrides Sub OnStop() ' サービスを停止するのに必要な終了処理を実行するコードをここに追加します。 End Sub End Class
OnStart プロシージャと OnStop プロシージャのサービスへの機能追加を行い、再定義する他のすべてのメソッドを上書きします。
OnStart プロシージャは、「コントロールパネル」⇒「管理ツール」⇒「サービス」で起動される、画面の中で、各サービスでの「開始」操作に対応する処理を記述します。 また、OnStop プロシージャは、各サービスでの「停止」操作に対応する処理を記述します。
最初のテストなので、このままの状態でサービスを作成し、インストール行います。
「TestService.vb[デザイン]」を表示し、デザイン上で右クリックでメニューを表示させて、「インストーラーの追加」をクリックします.インストーラーのコントロールがプロジェクトに追加されて、以下の様な表示なります.
[ProjectInstaller] の [デザイン] ビューで、[ServiceInstaller1] を選択します。
[プロパティ] ウィンドウで、ServiceName プロパティが TestService に設定されていることを確認します。
「ソリューション エクスプローラー」の「ソリューション'TestService'」を選択し、VisualSutudioのメニューの「ビルド」⇒「ソリューションのビルド」を選択し、ビルドを行います.尚、ここでひとつ忘れていたのですが、「TestService」プロパティ設定で、スタートアップオブジェクトを「TestService」に設定しておきます.
では、作成されたサービスを実際に登録してみます。 登録方法は、Windows の [スタート] メニューまたは [スタート] 画面で、[Visual Studio]⇒[Visual Studio ツール]⇒[開発者コマンド プロンプト] の順に選択します。
Visual Studio コマンド プロンプトが表示されますので、インストールする場合は以下のコマンドを入力します。- installutil C:\TestService\TestService\bin\Debug\TestService.exe
アンインストールする場合は以下のコマンドを入力します。
- installutil /u C:\TestService\TestService\bin\Debug\TestService.exe
インストールコマンドを実行すると以下の様に、ログイン時のユーザー名とパスワードを聞いてきますので、 それぞれを入力して作業を進めます。
尚、ユーザー名の注意ですが、ログインしている私のマシンのAdmin属性のユーザを設定したのですが、ユーザ名の先頭に「.\」(ドット¥)が必要でした。インストールが完了すれば以下の表示になります。
これでサービスに登録が終わりましたので、「コントロールパネル」⇒「管理ツール」⇒「サービス」を起動します。 サービスの一覧の中に「TestService」があると思いますので、サービスの「開始」を行います。以下の様に、サービスの状態が「開始」になります。
サービスを止めるには「停止」を指定します。
アンインストールを実際に行った様子を以下に示します。結果、サービス一覧から「TestService」が削除されました。
今回はサービスプログラムの生成と登録までを行っただけで、実際に何かの処理を行うわけではありません。
今後は、何かの処理を行う部分を組み込んで、サービスプログラムについて考察していきたいと思います。関連する記事
⇒サービスプログラムの作り方について・その2
⇒サービスプログラムの作り方について・その3(メイン処理をスレッド化)
⇒サービスプログラムの作り方について・その4(メイン処理内で別のスレッドを呼出す)
おすすめ書籍
-
前回投稿した以下の記事ですが、プロセス間通信の方法としてRemoting の IPCを使用していました。
⇒Remoting の IPC を使ったプロセス間通信について
今回は、HTTPチャネルを介してのプロセス間通信に変更してみます。 前回同様、1個のソリューショの中に以下の3個のプロジェクトを設置します。- クライアントとサーバーで共有するクラスDLL
- サーバープロセスの実行PG(EXE)
- クライアントプロセスの実行PG(EXE)
先ず、プロセス間通信の準備として、クライアントとサーバーで共有するクラスの宣言ですがこれは前回と全く同じものです。クライアントとサーバーで共有するクラス
Public Class ServiceClass Inherits MarshalByRefObject 'クライアントから呼び出しを受け、イベントを発生させる Public Sub RaiseServerEvent(ByVal message As String) 'イベントを発生させる RaiseEvent RaiseClientEvent(message) End Sub Public Event RaiseClientEvent(ByVal messsage As String) End Class
プロジェクトの定義は以下の様になります。
次に、サーバープロセスPGのプロジェクトを生成します。
1個のフォーム frmIpcServer をプロジェクトに追加し、そのフォーム上に1個のボタンとリッチエディットBOXを設置します。
フォームの静的変数として上記で説明した共有クラス(ServiceClass)の生成を行います。 また、ServiceClass から raise されるイベントの処理として、リッチエディットBOXに受信したメッセージを表示する為に、 デリゲート処理を宣言しています。
ボタンの押下によりHTTPの受信開始を行うのですが、以下の様な手順で行います。- HttpChannelによりHTTPチャネルを用意し、ChannelServices.RegisterChannel でチャネルを登録
- RemotingServices.Marshal により共有クラスの参照許可
- httpChannel.StartListening によりHTTPの受信を開始
サーバープロセスPG
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Http Imports IpcService Public Class frmIpcServer 'IPC用クラスの生成 Private WithEvents IpcServiceClass As New IpcService.ServiceClass '受信回数 Private mintRcv As Integer = 0 '別プロセスからのメッセージを処理するためデリゲートを利用 Delegate Sub SetRichTextBox1Delegate(ByVal Value As String) Private RichTextBox1Delegate As New SetRichTextBox1Delegate(AddressOf AppendTextRichTextBox1) 'リッチテキストボックスにメッセージを表示する Private Sub AppendTextRichTextBox1(ByVal message As String) RichTextBox1.AppendText(message) End Sub '受信準備 Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click 'HTTPチャネルを用意(ポートは8080を利用) Dim httpChannel = New HttpChannel(8080) ChannelServices.RegisterChannel(httpChannel, False) '「IpcServiceClass」を「ipcsample」で参照できるように設定 Dim ref As ObjRef = RemotingServices.Marshal(IpcServiceClass, "ipcsample") 'HTTP受信準備 httpChannel.StartListening(Nothing) RichTextBox1.AppendText("HTTP channel is ready to receive." & vbNewLine) End Sub 'IPC用クラスのイベント処理 Private Sub IpcServiceClass_RaiseClientEvent(ByVal message As String) Handles IpcServiceClass.RaiseClientEvent 'このイベント処理は、別プロセスのServiceClassからRaiseされるので、 'このフォームのコントロールにアクセスするにはデリゲート処理を行う '(クライアントから受信したメッセージを処理) mintRcv += 1 Me.Invoke(RichTextBox1Delegate, New Object() {mintRcv.ToString & ":" & message & vbNewLine}) End Sub End Class
尚、このプロジェクトの参照設定で以下のものを追加します。
- System.Runtime.Remoting
- IpcService
最後に、クライアントプロセスPGのプロジェクトを生成します。
1個のフォーム frmIpcClient をプロジェクトに追加し、そのフォーム上に1個のボタンとディットBOXを設置します。
フォームロード時に ChannelServices.RegisterChannel でHTTPチャネルの用意を行い、 RemotingConfiguration.RegisterWellKnownClientType でリモートサーバへの接続準備を行います。
ボタン押下時に IPC用クラスの生成を行い、そのクラスのイベント発生メソッドを実行し、メッセージ送信処理を行います。
尚、このプロジェクトの参照設定もサーバープロセスPGと同様です。クライアントプロセスPG
Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Http Imports IpcService Public Class frmIpcClient 'IPC準備 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 'HTTPチャネルを用意する Dim channel As New HttpChannel(0) ChannelServices.RegisterChannel(channel, False) 'リモートサーバへの接続準備 RemotingConfiguration.RegisterWellKnownClientType(GetType(ServiceClass), "http://localhost:8080/ipcsample") End Sub 'メッセージを送信する Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Try 'IPC用クラスの生成 Dim IpcServiceClass As ServiceClass = New ServiceClass() 'サーバへの通知(引数には、送信したいメッセージを指定) IpcServiceClass.RaiseServerEvent("Message From Client=>" & Me.TextBox1.Text) Catch ex As Exception MsgBox(ex.Message) End Try End Sub End Class
このソリューションをデバッグ実行するために、ソリューションのプロパティを以下の図の様に設定します。
このソリューションをデバッグ実行すると以下の様になります。
サーバーPGの起動後、「Start HTTP Channel」ボタンを押下して HTTP 受信の準備を行います。 その後、クライアントPGのボタンを押下するごとに、サーバーPGにメッセージが表示されていきます。
関連する記事
⇒Remoting の IPC を使ったプロセス間通信について
⇒名前付きパイプを使ったプロセス間通信について
⇒名前付きパイプを使ったプロセス間通信についてその2(複数クライアントとの通信)
⇒名前付きパイプを使ったプロセス間通信についてその3(クライアントとの双方向通信)