最近在做一个项目,有2个进程,A进程需要定时把B进程强行kill掉,然后再启动,当时这样会有一个问题,就是强行kill掉的进程在任务栏的托盘图标不会自动消失,必须用鼠标经过那个位置才会消失。这样就导致事件久了,任务栏出现一大堆图标,非常影响形象。
后来网上看到一个方法,说是通过进程间的通讯,就是由A发送通知给B,告诉B应该要退出程序了,然后由B自己来结束进程,这样任务栏的图标才会正常的消失。废话少说,直接看代码吧:
对于A进程来说,需要先找到B进程的句柄,然后才能给它发送通知,代码如下:
/// <summary> /// 根据端口号结束某个进程 /// </summary> /// <param name="port">端口号,如:7012 </param> /// <returns></returns> private void KillPortProcess(int port) { int pid = 0; try { IntPtr hwnd = FindWindow(null, port.ToString()); int ihWnd = (int)hwnd; GetWindowThreadProcessId(hwnd, out pid); var p = Process.GetProcessById(pid); if (p != null) { if (p.ProcessName == "MySvr") { //强制杀掉进程(这种模式不能自动销毁任务栏图标) //p.Kill(); //利用发送消息方式,通知进程自杀(这种模式可以自动销毁任务栏图标) byte[] sarr = System.Text.Encoding.Default.GetBytes("SelfKill"); int len = sarr.Length; COPYDATASTRUCT cds; cds.dwData = (IntPtr)Convert.ToInt16("1");//可以是任意值 cds.cbData = len + 1;//指定lpData内存区域的字节数 cds.lpData = "SelfKill";//发送给目标窗口所在进程的数据 int iflag = SendMessage(ihWnd, WM_COPYDATA, 0, ref cds); } } } catch (Exception e) { } }
我这个函数是根据进程的窗体标题来查找的,因为我的标题都是一个固定的端口号,所以按这种方式来做,当然也可以根据进程名称来做,在这里就不举例了。
这里用到了Windows的API函数: SendMessage , 要引用这个函数,必须事先做以下的声明才能使用:
消息结构体:
public struct COPYDATASTRUCT { public IntPtr dwData; public int cbData; [MarshalAs(UnmanagedType.LPStr)] public string lpData; }
声明要引用的API函数:
[DllImport("User32.dll", EntryPoint = "FindWindow")] public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("user32", EntryPoint = "GetWindowThreadProcessId")] public static extern int GetWindowThreadProcessId(IntPtr hwnd, out int pid); [DllImport("User32.dll", EntryPoint = "SendMessage")] private static extern int SendMessage(int hWnd, int Msg, int wParam, ref COPYDATASTRUCT lParam); const int WM_COPYDATA = 0x004A;
-------------------------------------------------------------------------------------------------------------------------------------------------------
那么对于B进程来说,需要做的事情就是接收A进程发来的消息,根据消费的内容来做出对应的动作,
只要重载 DefWndProc 函数即可实现消息的接收处理,代码如下:
/// <summary> /// 消息接受处理函数 /// </summary> /// <param name="m"></param> protected override void DefWndProc(ref Message m) { switch (m.Msg) { case WM_COPYDATA: COPYDATASTRUCT cds = new COPYDATASTRUCT(); Type t = cds.GetType(); cds = (COPYDATASTRUCT)m.GetLParam(t); string strResult = cds.dwData.ToString() + ":" + cds.lpData; AppendText(strResult); //强制退出 if (strResult == "1:SelfKill") { Thread.Sleep(1000); this.mainNotifyIcon.Dispose(); Application.Exit(); } break; default: base.DefWndProc(ref m); break; } }
在B进程退出前,只要把 NotifyIcon.Dispose() , 那么任务栏托盘图标就会自动消失了。