分享:搞定IE控件烦人的脚本错误

Jinhao 2011-09-08 04:12:24
为了搞定这个问题。C&P了网上无数的代码,MSDN先查了IOleCommandTarget,然后MSDN说这个方法不靠普。又找到put_onerror的方法,结果有时不凑效。又想到往Doc插入window.onerror的js代码,但这会引发网站的兼容性问题。接着有put_Silent(true),这个更不靠普,什么都没有了。然后还有神马ControlSite神马的,介于哥对MFC/ATL不熟悉玩这些完全是自找无趣,网上又没有很好的方法,最后哥决定放弃查找MSDN的文章,另辟蹊跷(其实是很老土的方法)终于没让江东父老失望。引用爱迪生的话就是:哥花两天的时间用前面的各种方法并非失败,只是证明了它们是不错也不对的。方法很简单,伪码&思路如下:

首先,必要的数据结构

std::map<DWORD, HHOOK> hktable; //线程和HHOOK的关联表

struct extra_wndproc_t
{
WNDPROC wndproc;
bool has_close_sent;

extra_wndproc_t()
: wndproc(0), has_close_sent(false)//关键作用是初始化has_close_sent。至于什么用后面一看便知。
{}
};
std::map<HWND, extra_wndproc_t> subcls_table; //窗口和WNDPROC的关联表


然后在IE控件的DocumentComplete事件中

void DocumentComplete()
{
DWORD tid = ::GetWindowThreadProcessId(控件窗口, 0);
if(hktable.count(tid) == 0) //表示该线程没有被HOOK
{
HHOOK cbt = ::SetWindowsHookEx(WH_CBT, &_m_cbtproc, NULL, tid); //设置钩子,用来拦截窗口的创建
hktable[tid] = cbt;
}
}

LRESULT CALLBACK _m_cbtproc(int nCode, WPARAM wParam, LPARAM lParam)
{
switch(nCode)
{
case HCBT_CREATEWND: //拦截创建窗口的事件
{
HWND wd = reinterpret_cast<HWND>(wParam);
char buf[260];
if(::GetClassName(wd, buf, 260))
{
if(std::string(buf) == "Internet Explorer_TridentDlgFrame") //脚本错误窗口的类名。
{
//该新建的窗口如果与脚本错误窗口同类,那么就子类化它。因为在这里判断不出这个窗口
//具体是不是用于显示脚本错误。
WNDPROC prev = (WNDPROC)::SetWindowLong(wd, GWL_WNDPROC, reinterpret_cast<LONG>(&_m_trident_wdproc));
subcls_table[wd] = prev;
}
}
}
break;
}
return ::CallNextHookEx(hktable[::GetCurrentThreadId()], nCode, wParam, lParam);
}

LRESULT CALLBACK _m_trident_wdproc(HWND wd, UINT msg, WPARAM wpar, LPARAM lpar)
{
WNDPROC wndproc = subcls_table[wd].wndproc;

switch(msg)
{
case WM_SETTEXT:
if(lpar)
{
std::string title(reinterpret_cast<char*>(lpar));
if(title == "脚本错误" || title == "Internet Explorer 脚本错误") //由于IE版本不同,标题也不同。
{
if(false == subcls_table[wd].has_sent_close) //避免重复Post WM_CLOSE
{
subcls_table[wd].has_sent_close = true;
::PostMessage(wd, WM_CLOSE, 0, 0); //肯定是Post不能是Send,你懂的
}
}
}
break;
case WM_DESTROY:
::SetWindowLong(wd, GWL_WNDPROC, wndproc); //还原子类化过程。
subcls_table.erase(wd);
break;
}

return ::CallWindowProc(wndproc, wd, msg, wpar, lpar);
}


比MSDN上面讲的简单多了。只是这里需要注意的是:
_m_trident_wdproc() 中的 窗口标题肯定不只有那两个,遇到不同的自己用手解决。
_m_cbtproc() 同样的,类名是不是那个也不确定,遇到不同的自己还是用手解决。
由于情况各异,当关闭窗口/线程的时候必须检查并销毁hktable中的对应线程的HOOK。
最后,如果是多线程环境,自行用手保证线程安全。
打完收工!
...全文
376 6 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
kaisenx 2013-03-19
  • 打赏
  • 举报
回复
这些代码写在什么位置啊!?!?!?!?!??!
Jinhao 2011-09-09
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 oyljerry 的回复:]

可以把窗口标题,类名等加到一个配置文件,这样以后就只需要更新,添加这个文件了
[/Quote]

嘿嘿
wyx100 2011-09-08
  • 打赏
  • 举报
回复
把窗口标题,类名等加到一个配置文件
oyljerry 2011-09-08
  • 打赏
  • 举报
回复
可以把窗口标题,类名等加到一个配置文件,这样以后就只需要更新,添加这个文件了
见习学术士 2011-09-08
  • 打赏
  • 举报
回复
Eleven 2011-09-08
  • 打赏
  • 举报
回复

16,548

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • AIGC Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧