Visual C#快顯視窗殺手

來源:互聯網
上載者:User
快顯視窗殺手是一個可以自動關閉IE快顯視窗的程式,它工作在系統的托盤中,按照一定的間隔來檢測IE視窗,然後關閉彈出表單。最後,還提供了用熱鍵來殺掉快顯視窗的功能。

   雖然已經有類似的用C++寫的程式,但是本文講述的是用C#來實現這些功能,並且本文所講的方案在尋找視窗上的方法要比更快一些。

   這是一個嶄新的話題,在Internet上我們還可以看到許多類似的程式。但是我也還是要借這個機會來講述一些下面的技術在C#中如何?:

   系統托盤
   程式切換
   計時控制項
   尋找視窗
   系統熱鍵

產生一個系統托盤程式

   首先,產生一個新的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)
}
}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.