利用API揪出抢占Win8窗口焦点的熊孩纸

起因:谁在抢镜头?!

为了开始Windows Phone 8的开发,这两天将系统升级到了Windows8.1,安装倒是又快又方便的,解压ISO镜像双击Setup一路下一步(嘛其实还输了密钥之类的)就安装好了~
不过不知道为什么去年买的正版密钥激活不了了,于是按照指示拨打了微软的客服电话,客服妹纸问了几个问题就给激活了~然后花了两三个小时进行各种配置美化和常用软件安装,看起来好极了~

01

可耻的晒桌面(其实和之前用Win7时没啥区别……已经一年多没换过主题了……)

刚开始用着感觉没啥问题啊,速度也挺快,可是打开VS写了几行代码就发现有些不对劲了,智能感应总是会莫名其妙的半路消失,然后发现聊QQ时经常打一半字输入法不见了,更糟糕的是逛B站全屏视频时会时不时跳出到窗口模式……根据十几年(啊天哪我怎么这么老了= -)的视窗操作系统(其实就是Windows~听起来逼格高那么一点?)使用经验,首先想到的是焦点被抢占了~之后用一般窗口程序观察了一下,发现的确是这样,时不时就会失去焦点半秒然后再恢复,网上查了一下,大多数说是支付宝安全控件之类的,可是我貌似不是因为这个引起的……那么究竟是谁干的呢?

04

经过:捕捉熊孩子,我有特殊的技巧

作为半个程序猿,当然就要用程序猿的方式解决咯(事实上是因为我找不到其它方法……),很久很久以前就知道了Windows有提供获得焦点窗口句柄(handle,话说为什么要翻译成“句柄”这种不明所以的东西? )的API,看来这次是可以用的上咯~
网上查了查发现不难而且有相关例子,直接打开VS,新建一个WinForm程序,拖了一个Label一个TextBox和一个Timer

00

就这么简单~

Timer设为10ms检测一次焦点窗口并输出相关信息,包括句柄号、进程PID号、进程名称、窗口名称和窗口类名,Label显示句柄号,TextBox记录相关变化
以下为代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;


namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        [DllImport("user32", EntryPoint = "GetWindowThreadProcessId")]
        private static extern int GetWindowThreadProcessId(
            IntPtr hwnd, //窗口句柄
            out int pid  //输出所在进程的PID
        );//通过句柄获取PID

        [DllImport("user32", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern IntPtr GetForegroundWindow();//获取当前激活窗口

        [DllImport("user32", SetLastError = true)]
        public static extern int GetWindowText(
            IntPtr hWnd, //窗口句柄
            StringBuilder lpString, //标题
            int nMaxCount  //最大值
        );

        [DllImport("user32")]
        private static extern int GetClassName(
            IntPtr hWnd, //句柄
            StringBuilder lpString, //类名
            int nMaxCount //最大值
        );

        public Form1()
        {
            InitializeComponent();
            timer1.Start();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            IntPtr myPtr = GetForegroundWindow();
            int pid = 0;

            // 窗口进程PID
            GetWindowThreadProcessId(myPtr, out pid);

            // 窗口标题
            StringBuilder title = new StringBuilder(512);
            GetWindowText(myPtr, title, title.Capacity);

            // 窗口进程
            Process localById = Process.GetProcessById(pid);

            // 窗口类名
            StringBuilder className = new StringBuilder(512);
            GetClassName(myPtr, className, className.Capacity);

            //如果焦点窗体发生变化则记录
            if (label1.Text != myPtr.ToString())
            {
                label1.Text = myPtr.ToString();
                textBox1.Text = DateTime.UtcNow
                    + "\r\n" + pid.ToString()
                    + "\r\n" + localById.ProcessName
                    + "\r\n" + title.ToString()
                    + "\r\n" + className.ToString()
                    + "\r\n\r\n===================================================\r\n\r\n"
                    + textBox1.Text;
            }
        }
    }
}

运行起来一切正常,就差熊孩纸出现了!

点我下载熊孩子探测器

结果:嘿嘿我捉到你了!

写完代码运行了一段时间才猛然发现,在这期间居然没有出现过被抢占焦点的问题,难道狡猾的熊孩纸发现了我的阴谋?
当然这不可能啦~该出现的迟早是要出现的,于是先让它运行着,然后继续聊QQ看视频……
又过了一个小时,熊孩纸终于按捺不止又开始捣乱啦~赶快切换到探测器,终于发现了……

QQ截图20140831000327

原来是这个名为DrUpdate.exe的进程,看名字就知道,这是我们万恶的校园网登入客户端Dr.Com的自动升级进程,

03

这哪里是熊孩子捣乱,分明是州官放火啊
没办法,又不能删掉,试着用资源监视器挂起了这个进程,没想到这货居然会自动重启,最后一怒之下直接把这个文件重命名了……然后一切都安宁了~~

后记

虽然通过重命名执行文件的方式暂时解决了问题,不过后来发现重启电脑或关闭客户端后会导致无法再次打开客户端,于是只能再重命名回来(还好留了个心眼没有直接删掉),等客户端启动后再次重命名,感觉真是麻烦,不过就暂时先这样咯,反正我一般不关电脑的~