忍者ブログ

VB.NET-TIPS などプログラミングについて

VB.NETのTIPS(小技集)を中心に、その他のプログラミングについて少し役に立つ情報を発信します。いわゆる個人的な忘備録ですが、みなさんのお役に立てれば幸いです。

Remoting の IPC を使ったプロセス間通信について

同一PC上でプロセス(EXE)間で通信を行いたいことはよくあります。 例えば、1個のPGを何か受信専用として動作させておいて、別のPGではメインの処理を行いながら、たまに受信データの処理を行うなどの要求が出てきたりします。 別EXE同士でやり取りを行う場合には以下の様な方法があると思います。

  • WindowsAPIのメッセージ通信
  • WindowsAPIのPIPEでの通信
  • WindowsAPIのファイルマッピングでの通信
  • 通常のファイルを使った通信
  • 遅くてOKならば、データベースを介した通信

尚、今回は勉強もかねて RemotingIPC を使って、クライアントプロセス(EXE)からサーバープロセス(EXE)にメッセージを送信することをやってみます。
今回の例では、1個のソリューションの中に以下の3個のプロジェクトを生成します。

  • クライアントとサーバーで共有するクラスDLL
  • サーバープロセスの実行PG(EXE)
  • クライアントプロセスの実行PG(EXE)

先ず、プロセス間通信の準備として、クライアントとサーバーで共有するクラスの生成を行います。
以下のソースを見て下さい。MarshalByRefObject をクラスを継承したクラス ServiceClass を宣言しています。
内容的には多くのことをするわけでは無く、クライアントから呼出されるであろうメソッドで、 文字列データを引数としてイベントを raise しています。
このプロジェクトは IpcService と命名し、クラスライブラリ(DLL)としてビルドしておきます。
(後からこのDLLを、サーバープロセス及びクライアントプロセスのプロジェクトで参照設定します。)

クライアントとサーバーで共有するクラス

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に受信したメッセージを表示する為に、 デリゲート処理を宣言しています。
ボタンの押下によりIPCの受信開始を行うのですが、以下の様な手順で行います。

  • IpcServerChannel によりIPCチャネルを用意し、ChannelServices.RegisterChannel でチャネルを登録
  • RemotingConfiguration.RegisterWellKnownServiceType でサービスのタイプを宣言
  • RemotingServices.Marshal により共有クラスの参照許可
  • IpcChannel.StartListening によりIPCの受信を開始

サーバープロセスPG

Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Ipc
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

    'IPC受信開始
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'IPCチャネルを用意
        Dim IpcChannel As New IpcServerChannel("ipcPort")
        ChannelServices.RegisterChannel(IpcChannel, False)
        Dim strSClassName As String = GetType(ServiceClass).Name
        RemotingConfiguration.RegisterWellKnownServiceType(GetType(ServiceClass), strSClassName, WellKnownObjectMode.SingleCall)
        '「ServiceClass」を参照できるように設定
        Dim ref As ObjRef = RemotingServices.Marshal(IpcServiceClass, strSClassName)

        'IPC受信準備
        IpcChannel.StartListening(Nothing)
        RichTextBox1.AppendText("IPC 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 でIPCチャネルの用意を行います。
ボタン押下時にメッセージ送信処理を行うのですが以下の様な手順で行います。

  • リモートIPCオブジェクトの URL を定義して Activator.GetObject 参照を取得
  • その参照を共有クラスのオブジェクト変数に代入
  • 共有参照クラスのイベント生成メソッド呼び出し

このプロジェクトの参照設定もサーバープロセスPGと同様です。

クライアントプロセスPG

Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Ipc
Imports IpcService

Public Class frmIpcClient

    'IPC準備
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        'IPCチャネルを用意する
        Dim ipcChannel As New IpcClientChannel
        ChannelServices.RegisterChannel(ipcChannel, False)
    End Sub

    'メッセージを送信する
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Try
            '既存のリモートIPCオブジェクトへの参照を取得する
            Dim strURL As String = "ipc://ipcPort/" + GetType(ServiceClass).Name
            Dim objRemote As Object = Activator.GetObject(GetType(ServiceClass), strURL)

            'サーバとの共通クラスの参照(ServiceClass)
            Dim objServiceClass As ServiceClass = CType(objRemote, ServiceClass)
            '共有参照クラスのイベント生成メソッド呼び出し
            objServiceClass.RaiseServerEvent("Message From Client=>" & Me.TextBox1.Text)

        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub

End Class

このソリューションをデバッグ実行するために、ソリューションのプロパティを以下の図の様に設定します。

このソリューションをデバッグ実行すると以下の様になります。
サーバーPGの起動後、「Start IPC Channel」ボタンを押下して IPC 受信の準備を行います。 その後、クライアントPGのボタンを押下するごとに、サーバーPGにメッセージが表示されていきます。

今回のPGはまだまだ不備な点が多いので実用には供しないですが、ご参考になればと思います。

PR

コメント

コメントを書く