對於存在窗體的WPF程序(或者說,起碼在任務欄上有個圖標,即ShowInTaskbar = true),互相傳遞消息是很容易的。
步驟:
1,尋找窗體的句柄
2,運用windows API: SendMessage或PostMessage
3,目標窗體收到消息
這里的代碼,展示了一個APP不希望多開所以在啟動時檢查是否存在一個已經運行的進程。如果進程已經存在,則給對方發送消息並結束自身。
1.判斷/獲取已經運行的實例
/// <summary> /// 獲取當前exe文件的是否已經在電腦上運行了一個實例,如果是,則返回那個實例的Process;如果否,就返回null; /// 如果有窗體,可以通過process.MainWindowHandle獲取窗體的句柄,如果沒窗體(ShowInTaskbar = false),則句柄為空 /// </summary> public static Process GetCurrentExeProcess() { Process targetProcess = null; Process currentProcess = Process.GetCurrentProcess(); string exeName = string.Format("{0}.exe", currentProcess.ProcessName); //PTZ.Tracer.AppLog.Info("$$$$$Get Current Process Successfully, current process exe: {0}", exeName); try { Process[] aryProcess = Process.GetProcessesByName(currentProcess.ProcessName); //PTZ.Tracer.AppLog.Info("$$$$$Get aryProcess Successfully, aryProcess count: {0}", aryProcess.Count()); foreach (Process process in aryProcess) { if (process.Id != currentProcess.Id && process.ProcessName == currentProcess.ProcessName) { targetProcess = process; //PTZ.Tracer.AppLog.Info("$$$$$Get MainWindowHandle Successfully, hWnd: {0}", hWnd); break; } } } catch (System.PlatformNotSupportedException pEx) { //PTZ.Tracer.AppLog.Error("$$$$$GetCurrentWindowsInstanceHandle exception:", pEx); } catch (System.InvalidOperationException iEx) { //PTZ.Tracer.AppLog.Error("$$$$$GetCurrentWindowsInstanceHandle exception:", iEx); } catch (System.ComponentModel.Win32Exception win32Ex) { //PTZ.Tracer.AppLog.Error("$$$$$GetCurrentWindowsInstanceHandle exception:", win32Ex); } return targetProcess; }
但是這個方法在某些奇怪權限設置的電腦上會遭遇UnauthorizedException,所以,如果只是想獲取窗體句柄的話,還可以將上述方法的Try & Catch部分換成下面的代碼:
try { string wmiQueryString = string.Format("SELECT ProcessId, Handle,ExecutablePath,Name FROM Win32_Process WHERE ProcessId != {0} AND Name = '{1}'", currentProcess.Id, exeName); using (var searcher = new ManagementObjectSearcher(wmiQueryString)) { using (var results = searcher.Get()) { ManagementObject mo = results.Cast<ManagementObject>().FirstOrDefault(); if (mo != null) { string s = (string)mo["ExecutablePath"]; string handleStr = (string)mo["Handle"]; string sss = (string)mo["Name"]; Tracing.Trace.Information("¥*¥ManagementObjectSearcher, name: {0} Handle: {1} Path: {2}", sss, handleStr, s); int handle = int.Parse(handleStr); Process p = Process.GetProcessById(handle); hWnd = p.MainWindowHandle; isRunExist = true; Tracing.Trace.Information("¥*¥ManagementObjectSearcher, hWnd: {0}", hWnd); } } } } catch (Exception e) { Tracing.Trace.Error("ManagementObjectSearcher exception:", e); }
2.注冊WindowsAPI
#region Dll Imports [DllImport("user32.dll")] static extern IntPtr PostMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam); public const uint WM_APP = 0x9112;////0x8001~0xBFFF,隨意#endregion Dll Imports
3.修改App.OnStartup方法
當程序啟動,就需要檢測實例。如已有實例運行,則發送消息;如沒有,則繼續正常運行。所以我們需要修改Appxaml.cs的OnStartup方法:
protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); Process targetProcess = SingleExeHelper.GetCurrentExeProcess(); if (targetProcess!=null)//Software has been running... {
//currentHandle通過targetProcess.MainWindowHandle得到
PostMessage(currentHandle,WM_APP, IntPtr.Zero, IntPtr.Zero);
Environment.Exit(0);//Software has been running, close this } else { //do nothing } }
4.主窗體監聽消息:
protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); ((HwndSource)PresentationSource.FromVisual(this)).AddHook(myHook); //HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle); //source.AddHook(new HwndSourceHook(myHook)); } private IntPtr myHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { int m = (int)App.WM_APP; if (msg == m) { System.Windows.MessageBox.Show("haha"); } return IntPtr.Zero; }