本文給出了開發關閉彈出窗口的方法。
基本原理:本系統可以自動關閉IE彈出窗口的程序,它工作在系統的托盤中,按照一定的間隔來檢測IE窗口,然后關閉彈出窗體。最后,還提供了用熱鍵來殺掉彈出窗口的功能。
具體內容如下:
系統托盤
程序切換
計時控件
查找窗口
系統熱鍵
生成一個系統托盤程序
首先,產生一個新的C# Windows Form程序,將NotifyIcon控件從工具箱中拖到窗體中:
在C# windows Form程序中添加托盤
為了保證系統托盤的圖標和應用程序的圖標一致,我們用一個共同的圖標文件a.ico來設置系統托盤的圖標和應用程序的圖標。
為了使程序不顯示在工具欄上,我們可以設置窗體的visible屬性為false. 這個可以在窗體屬性窗口中直接實現。
this.ShowInTaskbar = false;
到目前為止,系統托盤已基本好了,但是我們還沒有設置右鍵菜單,也沒有使程序顯示和隱藏的功能。
程序切換
首先,程序的主窗體可以根據不同的狀態來選擇顯示或者是隱藏,除此之外,我們可以用WindowState設置窗體的狀態:
public void HideApp() { this.WindowState = FormWindowState.Minimized; Hide(); } public void ShowApp() { Show(); this.WindowState = FormWindowState.Normal; } |
一個非常有趣的功能是讓用戶關閉窗體的時候程序并不是退出,為了實現這個功能,我們必須要重寫窗體的OnClosing事件。
protected override void OnClosing(CancelEventArgs e) { // 用最小化來代替關閉操作d e.Cancel = true; // 最小化,并且隱藏窗體 this.WindowState = FormWindowState.Minimized; Hide(); } 當然,我們必須要提供一個必須的退出方法.這個可以在托盤的右鍵菜單的exit中實現, private void menu_App_Exit(object sender, System.EventArgs e) { NativeWIN32.UnregisterHotKey(Handle, 100); //隱藏托盤 notifyIcon1.Visible = false; Application.Exit(); } |
添加右鍵菜單
添加一個右鍵菜單和添加托盤基本一樣,從工具箱中添加context menu就可以.右鍵菜單在你鼠標右鍵按下的時候是會自動彈出的。
當設置好右鍵菜單以后,我們必要要根據不同的情況來啟用或停用右鍵菜單,這個可以通過在菜單的BeforePopup設置。Enabled屬性來實現。
private void menu_App_BeforePopup(object sender, System.EventArgs e) { if ( this.WindowState == FormWindowState.Minimized ) { App_Show.Enabled = true; App_Hide.Enabled = false; } else { App_Show.Enabled = false; App_Hide.Enabled = true; } } |
計時工具
.Net Framework的 Timer能和系統的Win32 timer實現一樣的功能。我們要做的就是設置一個timer,然后合理的設置屬性。
m_Timer = new System.Timers.Timer(); // explicit namespace (Timer also in System.Threading) m_Timer.Elapsed += new ElapsedEventHandler(OnTimerKillPopup); m_Timer.Interval = m_nInterval; // for instance 3000 milliseconds m_Timer.Enabled = true; // start timer protected void OnTimerKillPopup(Object source, ElapsedEventArgs e) { m_Timer.Enabled = false; // pause the timer FindPopupToKill(); m_Timer.Enabled = true; } |
本地win32窗體查找
本程序的實現原理是這樣,先檢查所有的IE窗口標題,然后于已經有的列表來比較,如果有相同的,我們就關閉這個窗口。
按照上面的方法,我們每n妙使用KillPopup()來檢查。比較遺憾的是我們無法使用安全代碼來完成所有的工作。我們可以使用 System.Diagnostics.Proces來檢查所有的IE進程,然后得到主窗體。但是每一個IE進程可以打開好幾個窗口,雖然每一個窗體都于一個進程相關,但是還沒有辦法來使每一個窗體于進程對應起來。
一個可行的辦法使用System.Diagnostics.Process列舉出所有的運行的進程,然后System.Diagnostics.ProcessThreadCollection 來得到他們的.Threads屬性,為了得到thread Id,我們使用Win32 API EnumThreadWindows(DWORD threadId,WNDENUMPROC lpfn,LPARAM lParam) 來實現,這是一個回調(call back)函數,他可以列舉出于進程相關的窗體。當我們得到了窗體的句柄以后,我們可以使用另一個API函數 GetWindowText(HWND hwnd,/*out*/LPTSTR lpString,int nMaxCount)來得到窗體的標題,然后根據已經有的窗體,調用API函數SendMessage(HWND hWnd,int msg,int wParam,int lParam)來關閉窗口。下面使演示代碼
Process[] myProcesses = Process.GetProcessesByName("IEXPLORE"); foreach(Process myProcess in myProcesses) { FindPopupToKill(myProcess); } protected void FindPopupToKill(Process p) { // traverse all threads and enum all windows attached to the thread foreach (ProcessThread t in p.Threads) { int threadId = t.Id; NativeWIN32.EnumThreadProc callbackProc = new NativeWIN32.EnumThreadProc(MyEnumThreadWindowsProc); NativeWIN32.EnumThreadWindows(threadId, callbackProc, IntPtr.Zero /*lParam*/); } } // callback used to enumerate Windows attached to one of the threads bool MyEnumThreadWindowsProc(IntPtr hwnd, IntPtr lParam) { public const int WM_SYSCOMMAND = 0x0112; public const int SC_CLOSE = 0xF060; // get window caption NativeWIN32.STRINGBUFFER sLimitedLengthWindowTitle; NativeWIN32.GetWindowText(hwnd, out sLimitedLengthWindowTitle, 256); String sWindowTitle = sLimitedLengthWindowTitle.szText; if (sWindowTitle.Length==0) return true; // find this caption in the list of banned captions foreach (ListViewItem item in listView1.Items) { if ( sWindowTitle.StartsWith(item.Text) ) NativeWIN32.SendMessage(hwnd, NativeWIN32.WM_SYSCOMMAND, NativeWIN32.SC_CLOSE, IntPtr.Zero); // try soft kill } return true; } public class NativeWIN32 { public delegate bool EnumThreadProc(IntPtr hwnd, IntPtr lParam); [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern bool EnumThreadWindows(int threadId, EnumThreadProc pfnEnum, IntPtr lParam); // used for an output LPCTSTR parameter on a method call [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)] public struct STRINGBUFFER { [MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)] public string szText; } [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int GetWindowText(IntPtr hWnd, out STRINGBUFFER ClassName, int nMaxCount); [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam); } |
上面的方法在性能上是不錯的,因為他過濾了其他非IE的窗口.但是我們可以用一個更簡單的方法來實現,就是調用API FindWindowEx(HWND hWndParent, HWND hWndNext, /*in*/LPCTSTR szClassName, /*in*/LPCTSTR szWindowTitle)方法.比較有用的是這句,我們可以使用registered window class name來找到IE窗口(IEFrame是所有打開的IE的標識).
protected void FindPopupToKill() { IntPtr hParent = IntPtr.Zero; IntPtr hNext = IntPtr.Zero; String sClassNameFilter = "IEFrame"; // 所有IE窗口的類 do { hNext = NativeWIN32.FindWindowEx(hParent,hNext,sClassNameFilter,IntPtr.Zero); // we've got a hwnd to play with if ( !hNext.Equals(IntPtr.Zero) ) { // get window caption NativeWIN32.STRINGBUFFER sLimitedLengthWindowTitle; NativeWIN32.GetWindowText(hNext, out sLimitedLengthWindowTitle, 256); String sWindowTitle = sLimitedLengthWindowTitle.szText; if (sWindowTitle.Length>0) { // find this caption in the list of banned captions foreach (ListViewItem item in listView1.Items) { if ( sWindowTitle.StartsWith(item.Text) ) NativeWIN32.SendMessage(hNext, NativeWIN32.WM_SYSCOMMAND, NativeWIN32.SC_CLOSE, IntPtr.Zero); // try soft kill } } } } while (!hNext.Equals(IntPtr.Zero)); } public class NativeWIN32 { [DllImport("user32.dll", CharSet=CharSet.Auto)] public static extern IntPtr FindWindowEx(IntPtr parent /*HWND*/, IntPtr next /*HWND*/, string sClassName, IntPtr sWindowTitle); } |
注冊系統熱鍵
系統熱鍵用在像彈出窗口殺手這種應用程序非常有用, Ctrl+Shift+J是缺省熱鍵。
說道實現,我們繼續用RegisterHotkey(HWND hWnd, int id, UINT fsModifiers, UINT vkey)。完成,代碼如下:
public void SetHotKey(Keys c, bool bCtrl, bool bShift, bool bAlt, bool bWindows) { m_hotkey = c; m_ctrlhotkey = bCtrl; m_shifthotkey = bShift; m_althotkey = bAlt; m_winhotkey = bWindows; // update hotkey NativeWIN32.KeyModifiers modifiers = NativeWIN32.KeyModifiers.None; if (m_ctrlhotkey) modifiers |= NativeWIN32.KeyModifiers.Control; if (m_shifthotkey) modifiers |= NativeWIN32.KeyModifiers.Shift; if (m_althotkey) modifiers |= NativeWIN32.KeyModifiers.Alt; if (m_winhotkey) modifiers |= NativeWIN32.KeyModifiers.Windows; NativeWIN32.RegisterHotKey(Handle, 100, modifiers, m_hotkey); //Keys.J); } 一般的,注冊熱鍵要一下幾步 /* ------- using HOTKEYs in a C# application ------- -- code snippet by James J Thompson -- 在Form的load 中 : Ctrl+Shift+J bool success = RegisterHotKey(Handle, 100, KeyModifiers.Control | KeyModifiers.Shift, Keys.J); |
在 form的closing中:
UnregisterHotKey(Handle, 100);
如何處理熱鍵 :
protected override void WndProc( ref Message m ) { const int WM_HOTKEY = 0x0312; switch(m.Msg) { case WM_HOTKEY: MessageBox.Show("Hotkey pressed"); ProcessHotkey(); break; } base.WndProc(ref m ); } public class NativeWIN32 { [DllImport("user32.dll", SetLastError=true)] public static extern bool RegisterHotKey( IntPtr hWnd, // handle to window int id, // hot key identifier KeyModifiers fsModifiers, // key-modifier options Keys vk // virtual-key code ); [DllImport("user32.dll", SetLastError=true)] public static extern bool UnregisterHotKey( IntPtr hWnd, // handle to window int id // hot key identifier ); [Flags()] public enum KeyModifiers { None = 0, Alt = 1, Control = 2, Shift = 4, Windows = 8 } } ------- using HOTKEYs in a C# application ------- */ |
當我們按下熱鍵以后,流程是這樣:首先用HWND GetForegroundWindow()來得到窗體,然后要抓出窗體的標題, GetWindowText(HWND hwnd, /*out*/LPTSTR lpString, int nMaxCount). 具體如下:
protected void ProcessHotkey() { IntPtr hwnd = NativeWIN32.GetForegroundWindow(); if (!hwnd.Equals(IntPtr.Zero)) { NativeWIN32.STRINGBUFFER sWindowTitle; NativeWIN32.GetWindowText(hwnd, out sWindowTitle, 256); if (sWindowTitle.szText.Length>0) AddWindowTitle( sWindowTitle.szText ); // add to the ListView (Form) } } |
|
新聞熱點
疑難解答