进程间通信-字符串的传递
近来写了个简单的音乐播放器, 每次双击音乐文件, 都会再次运行一个实例, 觉得不太方便, 只需一个运行着的实例即可, 因此着手解决这个问题.
最常用的方法, 当然是在查找当前相同的进程名称, 如果有,则退出, 没有则初始化本实例. 方法比较简单,但有效.
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弄得那么多, 让人记也记不住, 查也不好查.