使用Hook(钩子)阻止Flash启动浏览器打开URL
贺星河 2005-11-7
【文章目的】
在.net程序中嵌入flash文件,为了防止有人通过flash右键菜单中的“关于Macromedia”打开ie访问internet,或者flash中通过GetURL()方法打开浏览器直接访问internet
【解决方案】
使用Hook技术,截获flash在启动外部浏览器访问internet时向系统发送的消息,然后用线程将外部浏览器Kill掉
【编程手记】
开发工具:VS2005 Beta2,Flash版本为flash 8
为了达到禁止Flash打开浏览器的目的,从Hook(Hook不知道?天极里面资料很多的)入手,截获到Flash的相关事件信息,提前启动一个专杀浏览器的线程,使得Flash打开浏览器的瞬间关闭。我们先要找到Flash在播放电子杂志时候的一些相关数据,比如系统消息,发送给外部的消息结构为:
[StructLayout(LayoutKind.Sequential)]
public struct CWPSTRUCT
{
public IntPtr lparam;
public IntPtr wparam;
public int message;
public IntPtr hwnd;
}
//在C#中使用钩子
public delegate int HookProc(int code, IntPtr wparam, ref CWPSTRUCT cwp);
public static int WH_CALLWNDPROC = 0x004;
//安装钩子的函数
[DllImport("user32.dll", CharSet = CharSet.Auto,CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr SetWindowsHookEx(int type, HookProc hook, IntPtr instance, int threadID);
//调用下一个钩子的函数
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(IntPtr hookHandle, int code, IntPtr wparam, ref CWPSTRUCT cwp);
//卸载钩子
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool UnhookWindowsHookEx(IntPtr hookHandle);
//获取窗体线程ID
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowThreadProcessId(IntPtr hwnd, int ID);
private HookProc hookProc;
private IntPtr hookHandle = IntPtr.Zero;
这是打开Flash中一个含链接的事件时候截获的系统消息:
cwp.message =528 && cwp.wparam=513 //鼠标事件消息,在Flash上点击左右键盘都会发生
cwp.message =33cwp.lparam =33619969
cwp.message =33cwp.lparam =33619969cwp.wparam=658836cwp.hwnd=724410
cwp.message =33cwp.lparam =33619969cwp.wparam=658836cwp.hwnd=658836
cwp.message =32cwp.lparam =33619969cwp.wparam=724410cwp.hwnd=724410
cwp.message =8cwp.lparam =0cwp.wparam=724410cwp.hwnd=527784
cwp.message =8cwp.lparam =0cwp.wparam=724410cwp.hwnd=527784
cwp.message =647cwp.lparam =527784cwp.wparam=24cwp.hwnd=658866 //当前窗口的得到或失去焦点消息
cwp.message =641cwp.lparam =-1073741809cwp.wparam=0cwp.hwnd=527784
cwp.message =641cwp.lparam =-1073741809cwp.wparam=0cwp.hwnd=658866
cwp.message =641cwp.lparam =-1073741809cwp.wparam=0cwp.hwnd=724374
cwp.message =647cwp.lparam =724410cwp.wparam=23cwp.hwnd=658866 //当前窗口得到或失去焦点信息
cwp.message =641cwp.lparam =-1073741809cwp.wparam=1cwp.hwnd=724410
cwp.message =641cwp.lparam =-1073741809cwp.wparam=1cwp.hwnd=658866
cwp.message =641cwp.lparam =-1073741809cwp.wparam=1cwp.hwnd=724374
cwp.message =992cwp.lparam =-1070940117cwp.wparam=855452cwp.hwnd=855452 //通知外部浏览器打开URL消息
cwp.message =992cwp.lparam =-1070940117cwp.wparam=855452cwp.hwnd=724374
cwp.message =992cwp.lparam =-1070940117cwp.wparam=855452cwp.hwnd=658866
cwp.message =992cwp.lparam =-1070940117cwp.wparam=855452cwp.hwnd=658836
cwp.message =996cwp.lparam =-1070940117cwp.wparam=462274cwp.hwnd=855452
从以上消息队列中可以找到Flash中相关事件的消息 message 的内容,查阅API的相关资料,经过测试可以得知,message=992 是想要截获的消息,截获到这个消息后,可以使用thread来kill IE的process(有人可能说这里可以直接将截获到的消息丢掉,实际上这是行不通的,有些钩子只能查看消息,不能修改消息,也不能通过返回非0值阻止消息继续传递。很不幸,我们用到的WH_CALLWNDPROC就是其中之一。MSDN里说得很清楚:After the hook procedure returns control to the system, the message is passed to the window procedure. 不过WH_GETMESSAGE钩子可以修改消息,大家可以试试看)。
#region 阻止Flash打开URL
private Thread killThread;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == 992)
{
if (killThread != null)
{
killThread.Abort();
}
killThread = new System.Threading.Thread(new System.Threading.ThreadStart(KillIE));
killThread.IsBackground = true;
killThread.Start();
}
}
private void KillIE()
{
bool isKilled = false;
while (!isKilled)
{
System.Threading.Thread.Sleep(10);
//这里当然也可以是关闭firefox/maxthon 等其他浏览器
Process[] processes = System.Diagnostics.Process.GetProcessesByName("iexplore");
foreach (Process ps in processes)
{
ps.Kill();
isKilled = true;
break;
}
}
}
#endregion
【小结】
本文只是在做项目的过程中碰到一个小的需求而进行的一个测验,解决方案有很多,目前为止这是我找到的一个性能比较高的方案,供大家参考,谢谢!
全文完
附件:源程序FlashUrlTest.rar,我的.net2.0版本为 2.0.50727,请注意