Free Programming

我的生活在一行又一行的代码中前行........

 

Visual C#弹出窗口杀手(转)

Visual C#弹出窗口杀手

作者:出处:ASPCool.com责任编辑: [ 2002-11-19 11:44 ]

弹出窗口杀手是一个可以自动关闭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==0return 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) 
}
 
}
  

  代码下载:

  演示程序:

posted on 2007-01-14 21:44  sharewind  阅读(372)  评论(0编辑  收藏  举报

导航