WPF如果存在窗体(或至少,在任务栏有图标显示),互相传递消息是很容易的。
寻找目标窗体句柄->WindowsAPI SendMessage/PostMessage->目标窗体AddHook。参见:C# WPF 窗体传递消息
但是,如果窗体不存在,比如说,最小化的时候仅显示一个tray icon,连任务栏的图标都不能用,这个流程就不管用了。因为此时无法获取到目标窗体的句柄。
到达到这个要求,还有几种不同的方法:
1.管道。因为就在本机传递消息,所以匿名管道/命名管道都可以满足要求;
2.TCP/UDP 如果仅仅是传递一些特定的约定好的少数信息的话,这有点杀鸡用牛刀的感觉;
3.Windows API: PostThreadMessage
本文介绍第三种方法:
示例目标:程序只能运行一个实例,当程序再次被启动,就通知已经运行的实例并且关闭自身进程
1.判断/获取已经运行的实例
跟上一篇博文一样的代码一样……
2.注册WindowsAPI
[return: MarshalAs(UnmanagedType.Bool)] [DllImport("user32.dll", SetLastError = true)] public static extern bool PostThreadMessage(int threadId, uint msg, IntPtr wParam, IntPtr lParam);
3.修改App.OnStartup方法
public const uint WM_APP = 0x9112;//0x8001~0xBFFF; protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); Process targetProcess = GetCurrentExeProcess(); if (targetProcess!=null)//Software has been running... { if (targetProcess.Threads.Count > 0) { int targetMainThreadID=targetProcess.Threads[0].Id; bool result=SingleExeHelper.PostThreadMessage(targetMainThreadID, WM_APP, IntPtr.Zero, IntPtr.Zero); } Environment.Exit(0);//Software has been running, close this } else { ComponentDispatcher.ThreadPreprocessMessage += ComponentDispatcher_ThreadPreprocessMessage; } } private void ComponentDispatcher_ThreadPreprocessMessage(ref MSG msg, ref bool handled) { int m = (int)App.WM_APP; if (msg.message == m) { System.Windows.MessageBox.Show("收到信息啦!!!!"); } }
因为没有窗体了,所以在App.OnStartup方法注册收到消息的处理方法。
参考文章:
How can I register a global hot key to say CTRL+SHIFT+(LETTER) using WPF and .NET 3.5?