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

原创 2007年10月14日 21:32:00

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

Visual C#弹出窗口杀手

弹出窗口杀手是一个可以自动关闭IE弹出窗口的程序,它工作在系统的托盘中,按照一定的间隔来检测IE窗口,然后关闭弹出窗体。最后,还提供了用热键来杀掉弹出窗口的功能。    虽然已经有类似的用C++...
  • ccx_john
  • ccx_john
  • 2013年12月25日 09:20
  • 421

C#中三种弹出信息窗口的方式

本内容仅作学习复习所用,以防忘记 弹出信息框,是浏览器客户端的事件。服务器没有弹出信息框的功能。 方法一: asp.net页面如果需要弹出信息框,则需要在前台页面上注册一个jav...
  • iwaitmiracle
  • iwaitmiracle
  • 2015年10月08日 10:40
  • 1508

C# .NET弹出窗口大全

注: //关闭,父窗口弹出对话框,子窗口直接关闭 this.Response.Write("window.close();"); //关闭,父窗口和子窗口都不弹出对话框,直接关闭 ...
  • qq_27915701
  • qq_27915701
  • 2016年04月08日 17:50
  • 973

C# winForm自定义弹出页面

在C#的windows窗体应用程序中,添加弹出框效果.最后就是这样的效果.     页面Form2上有2个文本框,textBox1和textBox2.点击任意一个文本框,根据准备好的数据,弹出Form...
  • liuyanlinglanq
  • liuyanlinglanq
  • 2015年04月30日 09:33
  • 4582

【C#】C#实现嵌入式窗体(弹出的子窗体在父窗体内)

最近有些时间,就把上半年做的东西整理了一下。   C#搭配SQL Server使用,是我们在学习数据库的时候比较好的工具。       由于换了一台电脑,以前的数据库也没有备份,所以现在无法运行。...
  • qq_32353771
  • qq_32353771
  • 2016年11月15日 21:20
  • 2136

C# Winform如何弹出一个模式窗口来显示进度条

最近看了好多人问这方面的问题,以前我也写过一篇blog,里面说了如何在子线程中控制进度条。但目前大多数环境,需要弹出模式窗口,来显示进度条,那么只需要在原先的基础上稍作修改即可。   首先是进度条...
  • white__cat
  • white__cat
  • 2014年11月07日 11:37
  • 12441

C# WinForm 防止子窗口重复弹出

C# WinForm程序如何防止子窗口重复弹出
  • zshuaihua
  • zshuaihua
  • 2014年08月14日 11:29
  • 3224

VC++实现动画弹出/弹入式窗口

俗话说"佛靠金装,人靠衣装",一个好的软件如果能配上精美的界面一定会让更多的用户认同它。喜欢上网的朋友对NetAnt(网络蚂蚁)这个软件一定不会陌生,它的下载速度,断点续传的功能都给我们留下了深刻的印...
  • xiaojun111111
  • xiaojun111111
  • 2015年05月19日 09:41
  • 1022

C#实现类似QQ的隐藏浮动窗体、消息闪动

功能简介   当语音客服系统登录成功进入主界面时,本聊天工具将会自动隐藏在左下角位置,当鼠标移动到左下角时,自动弹出,当鼠标移开聊天窗体时,自动隐藏。如果想让聊天窗体固定在桌面,只要拖动一下聊天窗口...
  • zouyujie1127
  • zouyujie1127
  • 2014年07月19日 22:31
  • 4544

.NET, MVC框架下利用html, CSS,js实现弹出窗口

在前端UI交互过程中,经常会碰到需要弹出对话框的情况。通过html+css+js来自定义对话框是一个不错的方法。...
  • luanzheng_365
  • luanzheng_365
  • 2017年03月12日 14:56
  • 1117
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Visual C#弹出窗口杀手(1)
举报原因:
原因补充:

(最多只允许输入30个字)