LoginSignup
15
15

More than 5 years have passed since last update.

C#:WPF:SendMessageによるプロセス間通信まとめ。

Last updated at Posted at 2015-02-10

C#:WPF:プロセス間通信まとめ。

背景

 ①.NETリモーティングを使用して実装
  ⇒チョーカンタン!
   しかし、全てのプロセスがサーバにもクライアントにもなる場合は。。。どうすればいいんだ?
   単純に、同じAPが動いていればその全てにメッセージを投げたいんだ!!

 ②Win32API SendMessageによる実装
  ⇒C慣れないから見よう見まねだけど。。。とりあえずできた!!
   ので忘れないうちにメモしておく。

実装ポイント

 1.複数メッセージを送りたいのでメッセージNoのEnumを定義

    public enum MessageCode
    {
        MessageA = 0,
        MessageB = 1,
        MessageC = 2
    }

 2.送受信に使用する構造体を定義
   ※良くわかっていないですが、以下のようにしました。
    ・dwDataにメッセージNoを入れて
    ・lpDataに送信したい文字列を入れて
    ・cbDataにlpDataのサイズを入れる

    // SendMessageで送る構造体(Unicode文字列送信に最適化したパターン)
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct COPYDATASTRUCT
    {
        public IntPtr dwData;
        public int cbData;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpData;
    }

    // 上記構造体だと処理で扱いにくいので、扱いやすいようにクラスを定義した。
    public class DataObject
    {
        public string data { get; private set; }
        public MessageCode dataType { get; private set; }

        public DataObject(string relData MessageCode mesCode)
        {
            this.data = relData;
            this.dataType = mesCode;
        }
    }

 3.メッセージ送信用クラスを定義
   今回は、以下のように使えるようにした。
   MessageSender.sendMessageA(data,自身のウインドウハンドル);
    ※IDisposable実装してメモリ解放した方が良いんだよね!? ほんとは。。。

    public class MessageSender
    {

        // --- ●メッセージ送信用メソッド

        public static void sendMessageA(String sendData, IntPtr srcHandle)
        {
            new MessageSender(MessageCode.MessageA, sendData, srcHandle);
        }

        public static void sendMessageB(String sendData, IntPtr srcHandle)
        {
            new MessageSender(MessageCode.MessageB, sendDatasendData, srcHandle);
        }

        public static void sendMessageC(String sendData, IntPtr srcHandle)
        {
            new MessageSender(MessageCode.MessageC, sendData, srcHandle);
        }

        // ---------------------------

        private const uint WM_COPYDATA = 0x004A;

        public MessageSender(MessageCode code, String sendData, IntPtr srcHandle)
        {
            COPYDATASTRUCT sendData = new COPYDATASTRUCT();
            sendData.dwData = new IntPtr((int)code);
            sendData.cbData = sendData.Length * sizeof(char);
            sendData.lpData = sendData;

            foreach (Process targetProcess in GetPreviousProcess())
            {
                SendMessage(targetProcess.MainWindowHandle, WM_COPYDATA, srcHandle, ref sendData);
            }
        }

        /// <summary>
        /// 実行中の同じアプリケーションのプロセスを取得します。
        /// </summary>
        /// <returns></returns>
        public static List<Process> GetPreviousProcess()
        {
            Process curProcess = Process.GetCurrentProcess();
            Process[] allProcesses = Process.GetProcessesByName(curProcess.ProcessName);

            var targetProcesses = new List<Process>();

            foreach (Process checkProcess in allProcesses)
            {
                // 自分自身のプロセスIDは無視する
                if (checkProcess.Id != curProcess.Id)
                {
                    // プロセスのフルパス名を比較して同じアプリケーションか検証
                    if (String.Compare(checkProcess.MainModule.FileName, curProcess.MainModule.FileName, true) == 0)
                    {
                        // 同じフルパス名のプロセスを取得
                        targetProcesses.Add(checkProcess);
                    }
                }
            }
            return targetProcesses;
        }


        [DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
        public static extern int SendMessage(IntPtr hwnd, uint msg, IntPtr wParam, ref COPYDATASTRUCT lParam);
    }

 4.メッセージ受信のためにWndProcをフックする。

        private IntPtr myHandle;
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // WndProcを処理する。
            myHandle = new WindowInteropHelper(this).Handle;
            HwndSource source = HwndSource.FromHwnd(myHandle);
            source.AddHook(new HwndSourceHook(WndProc));
        }

 5.メッセージ受信部の実装をする。
   メッセージ送信側へ任意の戻り値を返すことも可能。

        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            switch (msg)
            {
                case 0x004A: // WM_COPYDATA
                    MessageBox.show(ReceiveString(lParam).data);
                    handled = true;
            }
            // 送信者はこの戻り値を取得することができる。(ここでは、Zero固定)
            return IntPtr.Zero;
        }

        private data ReceiveString(IntPtr lParam)
        {
            DataObject data = null;
            try
            {
                COPYDATASTRUCT cds = (COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(COPYDATASTRUCT));
                // ここで受信したデータが扱えるようになる。
                data = new DataObject(cds.lpData.Substring(0, cds.cbData / 2), (MessageCode)cds.dwData);
            }
            catch { data = null; }
            return data;
        }

以上、いったんこれで期待した動きを実現することができた。
良くわかっていないから手直しが必要だろうが。。。

15
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
15