进程间通信-字符串的传递

    近来写了个简单的音乐播放器, 每次双击音乐文件, 都会再次运行一个实例, 觉得不太方便, 只需一个运行着的实例即可, 因此着手解决这个问题.
    最常用的方法, 当然是在查找当前相同的进程名称, 如果有,则退出, 没有则初始化本实例. 方法比较简单,但有效.
private static bool FoundRunningInstance()
{
 Process currentProcess = Process.GetCurrentProcess();
 Process[] procList = Process.GetProcessesByName(currentProcess.ProcessName);

 foreach (Process proc in procList)
 {
  if (proc.Id != currentProcess.Id)
  {
   return true;
  }
 }
 return false;
}

public static void Main(string[] args)
{
 if (args.Length == 0)
  Application.Run(new MainForm());
 if (args.Length == 1)
 {
  if (!FoundRunningInstance())
   Application.Run(new MainForm(args[0]));
  else
   Environment.Exit(0);
 }
}
    这样运行起来是OK了, 但问题也随之而来, 如果已经运行了一个实例, 再双击一个音乐文件, 虽然不再运行新的实例, 但双击的音乐, 也没有预想的那样想起来. 后来想到微软的媒体播放器作为默认的播放器时, 每次双击新的音乐文件, 已经运行的实例会播放选定的音乐.
    如果实现这个效果呢?
    要让已经运行的实例接受新的音乐文件路径, 只有在双击后初始化新的实例前给running instance一个参数. 由此, 涉及到了进程间的通信. 实现进程间通信最常用的是使用windows API SendMessage函数. SendMessage函数的定义:
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
在四个参数中,全是整型的, 但这里需要传送的是一个字符串. 使用SendMessage是无法直接传送字符串的, 但可以通过发送WM_COPYDATA消息, 发送自定义只读数据, 这个自定义的数据, 在C#中使用struct实现.
public struct ProcessCopyDataStruct
{
 public int dwData;  // 或许自己需要的四字节标识
 public int cbData;
 [MarshalAs(UnmanagedType.LPStr)]  // lpData字符串的长度
        public string lpData;  // 需要传送的字符串
}
到这里, SendMessage函数定义也需要重新改一下了.
private static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, ref ProcessCopyDataStruct lParam);
Msg要传送的消息WM_COPYDATA的定义数值: public const int WM_COPYDATA = 0x004A;
    写到这里就要说说很多文章的不负责任了, 像WM_COPYDATA这类的变量, 虽说是API中使用, 可以查得到, 但对新手来说却是一头雾水, 把代码一套, 报错变量未定义, 就那么一个复制粘贴, 很多文章就是不把实际的值贴出来, 太不负责任了.
    定义好了结构, 就可以实现消息的发送了. 现在回头再修改一下FoundRunningInstance方法和Main方法.
    其中结构中dwData, 自己定义为:WM_QINGMUSIC = 0x8888;
private static bool FoundRunningInstance(string musicFile)
{
 Process currentProcess = Process.GetCurrentProcess();
 Process[] procList = Process.GetProcessesByName(currentProcess.ProcessName);

 foreach (Process proc in procList)
 {
  if (proc.Id != currentProcess.Id)
  {
   if (musicFile == null) return true;
   ProcessCopyDataStruct copydata;
   copydata.dwData = WM_QINGMUSIC;
   copydata.lpData = musicFile;
   copydata.cbData = System.Text.Encoding.Default.GetBytes(musicFile).Length + 1;
   SendMessage(proc.MainWindowHandle, WM_COPYDATA, currentProcess.Handle, ref copydata);
   return true;
  }
 }
 return false;
}

public static void Main(string[] args)
{
 if (args.Length == 0)
 {
  if (!FoundRunningInstance(null))
   Application.Run(new MainForm());
  else
   Environment.Exit(0);
 }
 if (args.Length == 1)
 {
  if (!FoundRunningInstance(args[0]))
   Application.Run(new MainForm(args[0]));
  else
   Environment.Exit(0);
 }
}
    到这里发送消息就做好了, 剩下的就是要Running Instance接收发过来的消息. 这里要重载一个方法WndProc.
protected override void WndProc(ref Message m)
{
 if (m.Msg == WM_COPYDATA)
 {
  ProcessCopyDataStruct copydata = (ProcessCopyDataStruct) m.GetLParam(typeof(ProcessCopyDataStruct));
  PlayMusic(copydata.lpData); // 此处为播放传过来的音乐文件路径, 可自由处理.
 }
 base.WndProc(ref m); // 这一句还是不能忘了.
}
    进程间传送自定义结构数据, 就完成了. 其实本不复杂, 麻烦呢只能怪微软把个API弄得那么多, 让人记也记不住, 查也不好查.

原文地址:https://www.cnblogs.com/fanyf/p/4499889.html