【C#】SendMessage & PostMessage

スポンサーリンク

プロセス間通信(IPC)の方法の1つとして、

SendMessage(センドメッセージ)とPostMessage(ポストメッセージ)があります。

この2つについて、話していきましょう。

プロセス間通信(IPC)とは

プロセス間通信(Interprocess Communication) 略して、IPCです。

複数のプロセスがデータをやりとりする仕組みの事をプロセス間通信と言います。

パソコン内でアプリ間が会話する為の方法だと思ってください。

※Wikipediaより

プロセス間通信の技法は上記のような方法があります。

先日行ったソケット(Socket)もプロセス間通信の1つです。

今回は、メッセージを使いたいと思います。

SendMessageとPostMessageについて

今回使うSendMessageとPostMessageは、フォーム間でやり取りをします。

フォームとフォームが送受信を行う仕組みです。

どんな時に使うかというと、アプリAとアプリBがあったとします。

アプリAのボタンが押された時に、押された事をアプリBに教える時に使います。

これだと、使い道無くない??って思う人がいるかもしれませんが、

確かにそんなに使うことはありませんwww

ただ、会社内で部署が分かれており、各々の部署でアプリを作られている時、

メッセージを貰えたら、処理できる仕組みを作っていれば、

各々の部署のアプリの変更は、メッセージを送ってもらう事のみで済みます。

SendMessageとPostMessageの違い

大きな違いは、非同期処理か同期処理かの違いです。

SendMessageは、送信後、送信相手の処理が終わるまで、次の処理にいきません。

PostMessageは、送信後、次の処理に行きます。その為、受信相手と同期処理になります。

メリット/デメリットはありますが、用途に合わせて使いましょう。

SendMessageとPostMessage ソフト作成(GUI)

【Send】ボタンではSendMessageを使い、

テキストボックスに入力されている文字列を送信します。

【Post】ボタンではPostMessageを使い、

テキストボックスに入力されている文字列を送信します。

SendMessageとPostMessage ソフト作成(DLLImport)

SendMessage/PostMessageでは、『User32.dll』の中の関数を使います。

using System.Runtime.InteropServices; //DLLImportを使うために
namespace ProcessConnect
{
  public partial class frmSend : Form
  {
    //ウィンドウを探す用のメソッド
    [DllImport("User32.dll", EntryPoint = "FindWindow")]
    public static extern Int32 FindWindow(String lpClassName, String lpWindowName);

    //送信するためのメソッド(数値)
    [DllImport("User32.dll", EntryPoint = "SendMessage")]
    public static extern Int32 SendMessage(Int32 hWnd, Int32 Msg, Int32 wParam, ref COPYDATASTRUCT lParam);

    //送信するためのメソッド(文字も可能)
    [DllImport("User32.dll", EntryPoint = "SendMessage")]
    public static extern Int32 SendMessage(Int32 hWnd, Int32 Msg, Int32 wParam, Int32 lParam);

    //送信するためのメソッド(数値)
    [DllImport("User32.dll", EntryPoint = "PostMessage")]
    public static extern Int32 PostMessage(Int32 hWnd, Int32 Msg, Int32 wParam, ref COPYDATASTRUCT lParam);

    //送信するためのメソッド(文字も可能)
    [DllImport("User32.dll", EntryPoint = "PostMessage")]
    public static extern Int32 PostMessage(Int32 hWnd, Int32 Msg, Int32 wParam, Int32 lParam);

    //文字列を送信する時に使う構造体
    public struct COPYDATASTRUCT
    {
      public IntPtr dwData; //送信する32ビット値
      public int cbData; //lpDataのバイト数
      public string lpData; //送信するデータへのポインタ(0も可能)
    }

構造体は送信側、受信側両方に必要になります。

DLLImportは送信側だけに必要です。

SendMessageとPostMessage ソフト作成(送信部)

流れとしては、フォームのウィンドウハンドルを取得します。

次に、テキストボックスの入力値が【数値】であるか確認して、

数値の場合は、数値を送信し、文字列の場合は、構造体を送信します。

【Send】ボタンの処理です。

    private void btnSend_Click(object sender, EventArgs e)
    {
        int hWnd = FindWindow(null, "受信フォーム");
        if (hWnd == 0)
        {
            //ハンドルが取得できなかった
            MessageBox.Show("相手Windowのハンドルが取得できません");
            return;
        }
        if (int.TryParse(txtMessage.Text,out int mes))
        {
            //数値を送信
            int result = SendMessage(hWnd, SMSG_NUM, mes, mes * 2);
        }
        else
        {
            //文字として送信
            COPYDATASTRUCT cds = new COPYDATASTRUCT();
            cds.dwData = (IntPtr)0;        
            cds.lpData = txtMessage.Text;
            cds.cbData = cds.lpData.Length + 1; //長さをセット

            //文字列を送る
            int result = SendMessage(hWnd, SMSG_STR, 0, ref cds);
        }
    }

【Post】ボタンの処理です。

    private void btnPost_Click(object sender, EventArgs e)
    {
        int hWnd = FindWindow(null, "受信フォーム");
        if (hWnd == 0)
        {
            //ハンドルが取得できなかった
            MessageBox.Show("相手Windowのハンドルが取得できません");
            return;
        }
        if (int.TryParse(txtMessage.Text, out int mes))
        {
            //数値を送信
            int result = PostMessage(hWnd, PMSG_NUM, mes, mes * 2);
        }
        else
        {
            //文字として送信
            PstMes_T = new COPYDATASTRUCT(); ;
            PstMes_T.dwData = (IntPtr)0; 
            PstMes_T.lpData = txtMessage.Text;
            PstMes_T.cbData = PstMes_T.lpData.Length + 1; //長さをセット

            //文字列を送る
            int result = PostMessage(hWnd, PMSG_STR, 0, ref PstMes_T);
        }
    }

これで送信部の完成です。

SendMessageとPostMessage ソフト作成(受信部)

次に受信部を作っていきます。

    protected override void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case SMSG_NUM:
            case PMSG_NUM:
                //数値が送信されて来た
                string typ = (m.Msg == SMSG_NUM) ? "SendMesNum" : "PostMesNum";
                txtRecieve.Text = "["+ typ + "] " + (m.WParam + "-" + m.LParam);
                break;
            case SMSG_STR:
            case PMSG_STR:
                //文字が送信されて来た
                typ = (m.Msg == SMSG_STR) ? "SendMesStr" : "PostMesStr";
                COPYDATASTRUCT mystr = new COPYDATASTRUCT();
                Type mytype = mystr.GetType();
                mystr = (COPYDATASTRUCT)m.GetLParam(mytype);
                txtRecieve.Text = "["+ typ + "] " + (mystr.lpData);
                break;
        }
        base.WndProc(ref m);
    }

ウィンドウプロシージャをオーバーライドします。

メッセージの番号に応じて、処理を分けます。

    public const Int32 SMSG_NUM = 9999;
    public const Int32 SMSG_STR = 9990;
    public const Int32 PMSG_NUM = 8888;
    public const Int32 PMSG_STR = 8880;

上記定義したメッセージ番号を受信したときに、受信内容を表示するように作りました。

まとめ

各パタンで実行してみました。

文字列をポストメッセージで送る事ができませんでした。

原因が分からず、できる方法を調べてみたいと思います。

ちなみに1度メッセージを送信で切れていれば、例外は発生しませんが、

最初から文字列のPostMessageを送信した場合は、例外が発生します。

構造体の定義を変更すれば、解決するような気がします。

解決した時にまたアップしたいと思います。

コメント

タイトルとURLをコピーしました