弹出窗口杀手(上)

弹出窗口杀手是一个可以自动关闭 IE 弹出窗口的程序,它工作在系统的托盘中,按照一定的间隔来检测 IE 窗口,然后关闭弹出窗体。最后,还提供了用热键来杀掉弹出窗口的功能。

虽然已经有类似的用 C++ 写的程序,但是本文讲述的是用 C# 来实现这些功能,并且本文所讲的方案在查找窗口上的方法要比更快一些。

这是一个崭新的话题,在 Internet 上我们还可以看到许多类似的程序。但是我也还是要借这个机会来讲述一些下面的技术在 C# 中如何实现:

l 系统托盘

l 程序切换

l 计时控件

l 查找窗口

l 系统热键

生成一个系统托盘程序

首先,产生一个新的 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;


    }


 </FONT
Published At
Categories with Web编程
Tagged with
comments powered by Disqus