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


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

背景

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

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


実装ポイント

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

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


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

```csharp
    // 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実装してメモリ解放した方が良いんだよね！？　ほんとは。。。

```csharp
    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をフックする。

```csharp
        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.メッセージ受信部の実装をする。
　　　メッセージ送信側へ任意の戻り値を返すことも可能。

```csharp
        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;
        }
```


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