内嵌IE网页窗口中消除IE默认脚本设置影响的方法

        随着人们对客户端软件界面要求的不断提高,软件开发商面临着一个问题:如何快速廉价开发出各种丰富效果的UI界面。设计出一套丰富控件的界面库是不容易的,且产品经理丰富的想法和UED对效果的追求,往往会使程序员疲于编写这些“效果控件”。目前市面上使用的很多界面库是基于XML描述的,界面引擎解析这些XML并渲染出其对应的效果。其实我们网页也是这样的原理,只是其复杂程度往往比市面上的界面库要复杂的多,且是无窗口控件(减少内存)。于是重用IE便成为一种很好的解决软件开发商面临问题的方法。(转载请指明出处)

        “拿人东西手短”,我们使用IE控件,体验着其便利,但是也往往会遇到IE默认设置对我们控件的影响。举个很简单的例子,QQ2011(其他版本没试过)的历史聊天记录部分就是通过JS加载聊天内容,如果你在IE设置中将“脚本设置”设置为“禁用”,你将看不到聊天记录。或许在用户遇到这样的问题时会询问其客服如何解决,客服可能会让他把他的“脚本设置”设置为“启用”,但是对于这样的少数用户,其一定有其将该选项设置为“禁用”的理由。我们程序员该做的就是如何设计好自己的程序,让其对用户不良的影响减少。

        针对“如何在内嵌IE网页中消除IE默认设置影响”,微软其实已经给了我们例子。

Secumgr.exe Overrides Security Manager for WebBrowser Host

        这是个MFC的例子,对于如果界面库是基于MFC的来说,完全可以参考这个例子。

        我主要来谈谈WTL的界面库中的解决方案。

        我在codeproject上找到了一个WTL的IE内嵌窗口的demo,其中已经加好了我要入的内容,只是有些内容写法“存在”问题。我把一些关键点罗列下:       

class ATL_NO_VTABLE CBrowserHost :
    public CComObjectRoot,
	public CComCoClass<CBrowserHost, &CLSID_NULL>,
    public CBrowserHostWindowImpl,
    public CWindowIcons<CBrowserHost, MAKEINTRESOURCE(IDI_WEBAPP)>,
    public DWebBrowserEvents2Impl,
    public IBrowserHost,
    public IOleCommandTarget,
//////////////////////////////////////////////////////////////////////////
    // 要加入的
    public IServiceProvider,
    public IInternetSecurityManager
//////////////////////////////////////////////////////////////////////////
BEGIN_SERVICE_MAP(CBrowserHost)
    //////////////////////////////////////////////////////////////////////////
    // 要加入的
	SERVICE_ENTRY(__uuidof(IInternetSecurityManager))
    //////////////////////////////////////////////////////////////////////////
END_SERVICE_MAP()
protected:
    //////////////////////////////////////////////////////////////////////////
    // 要加入的
    IInternetSecurityManager* m_pSecurityMgr;
    //////////////////////////////////////////////////////////////////////////

        以上是头文件

HRESULT CBrowserHost::FinalConstruct()
{
    HRESULT hr = CAxHostWindow::_CreatorClass::CreateInstance(
    static_cast<IBrowserHost*>(this), IID_PPV_ARGS(&m_ptrUnkInner));
    ATLASSERT(SUCCEEDED(hr));

	::OleInitialize(NULL);

    //////////////////////////////////////////////////////////////////////////
    // 要加入的
    /* Create Internet Security Manager Object */
    if ( NULL != m_pSecurityMgr )
    {
        ::CoCreateInstance(CLSID_InternetSecurityManager, NULL,
            CLSCTX_INPROC_SERVER, IID_IInternetSecurityManager,
            (void**)&m_pSecurityMgr);
    }
    //////////////////////////////////////////////////////////////////////////
    return hr;
}
void CBrowserHost::FinalRelease()
{
    m_ptrUnkInner = NULL;

    //////////////////////////////////////////////////////////////////////////
    // 要加入的
	if ( NULL != m_pSecurityMgr )
    {
        m_pSecurityMgr->Release();
    }
    //////////////////////////////////////////////////////////////////////////

    ::OleUninitialize();
}
STDMETHODIMP CBrowserHost::ProcessUrlAction( 
    /* [in] */ LPCWSTR pwszUrl,
    /* [in] */ DWORD dwAction,
    /* [size_is][out] */ BYTE *pPolicy,
    /* [in] */ DWORD cbPolicy,
    /* [in] */ BYTE *pContext,
    /* [in] */ DWORD cbContext,
    /* [in] */ DWORD dwFlags,
    /* [in] */ DWORD dwReserved)
{
    // 脚本禁用的关键

    DWORD dwPolicy = URLPOLICY_ALLOW;

    // !! If the compiler can't find URLACTION_CROSS_DOMAIN_DATA, make sure you are building with
    // !! the latest version of the IE headers -- URLMON.H specifically -- from MSDN Downloads for the 
    // !! Web Workshop or the Platform SDK
    if (dwAction <= URLACTION_SCRIPT_MAX && dwAction >= URLACTION_SCRIPT_MIN)
        dwPolicy = URLPOLICY_ALLOW;
    else
        return INET_E_DEFAULT_ACTION;

    if ( cbPolicy >= sizeof (DWORD))
    {
        *(DWORD*) pPolicy = dwPolicy;
        return S_OK;
    } 
    else 
    {
        return S_FALSE;
    }
}
STDMETHODIMP CBrowserHost::QueryService( 
                                        REFGUID guidService,
                                        REFIID riid,
                                        void __RPC_FAR *__RPC_FAR *ppvObject)
{
    if (guidService == SID_SInternetSecurityManager && 
        riid == IID_IInternetSecurityManager)
    {
        *ppvObject = dynamic_cast<IInternetSecurityManager*>(this);
        HRESULT hr = ((IInternetSecurityManager*)*ppvObject)->AddRef();
        ATLTRACE( L"%d\n", hr );
        return hr;
        //////////////////////////////////////////////////////////////////////////
        // 很多地方是这么写的
        // 但是这么写会出现个问题,就是HR会报一系列错误(15~55,58)
        // HRESULT hr = ((IInternetSecurityManager*)*ppvObject)->AddRef();
        // return hr;
        //////////////////////////////////////////////////////////////////////////
        ((IInternetSecurityManager*)*ppvObject)->AddRef();
        return S_OK;
    } 
    else 
    {
        *ppvObject = NULL;

    }
    return E_NOINTERFACE;
}

        以上是CPP中文件

        以上只是主要列出主要的改动点。其中主要说下ProcessUrlAction和QueryService两个函数。ProcessUrlAction是消除IE默认脚本设置的关键。其中

if (dwAction <= URLACTION_SCRIPT_MAX && dwAction >= URLACTION_SCRIPT_MIN)
        dwPolicy = URLPOLICY_ALLOW;

        这句就是说,不管用户设置的是“启用”、“禁用”或“提示”,本内嵌IE对活动脚本的设置都是“启用”。

        (这里面的很多设置都可以在这个函数中进行修改)          还有个要注意的地方就是QueryService中的实现(非常重要),很多网上的方法中都是如此写的

 if (guidService == SID_SInternetSecurityManager && 
        riid == IID_IInternetSecurityManager)
    {
        *ppvObject = dynamic_cast<IInternetSecurityManager*>(this);
        HRESULT hr = ((IInternetSecurityManager*)*ppvObject)->AddRef();
        return hr;
    } 

        我想所有曾期望解决此问题的同学都遇到一个问题:如此写的话,那么ProcessUrlAction永远都进不去。当初我也纠结于这个问题,后来我注意了下QueryService,发现此处的hr一直不会是S_OK。在没有办法的情况下,我就将代码改为:

    if (guidService == SID_SInternetSecurityManager && 
        riid == IID_IInternetSecurityManager)
    {
        *ppvObject = dynamic_cast<IInternetSecurityManager*>(this);
        ((IInternetSecurityManager*)*ppvObject)->AddRef();
        return S_OK;
    } 

        如此改完后,问题就解决了,也没引入其他问题。至于为什么,可能只有微软知道了,或许该处就应该返回S_OK,而不是根据AddRef的返回值来决定返回值。

        希望所有使用IE控件的界面库设计同学都能很好的解决这个问题。

        以下是微软提供的MFC修改版和WTL修改版的工程,其中MFC是VC6的,需要include最低vs2003的库。WTL是VC9的。

        链接:https://pan.baidu.com/s/1N_wOSfqNZL4vc1fDTXNhFw 密码:yli6

(转载请指明出处)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏WebDeveloper

golang-protobuf使用

Protocol buffers是一个灵活的、高效的、自动化的用于对结构化数据进行序列化的协议,与XML、json相比,Protocol buffers序列化后...

55230
来自专栏Bug生活2048

工作中的坑——dom4j解析含有命名空间的XML的坑

网上大多数分析的帖子都说dom4j解析xml性能最好,所以在碰到实际业务场景中就着手使用dom4j来解析xml了。

23520
来自专栏Spring相关

运行时权限

我们在夸数据共享的时候,遇到危险的权限时候系统会让我们进行是否授权,只有我们授权了才能使用这些权限.比如拨号(可能引起收费的敏感权限)等,这里我们来演示一下调用...

14820
来自专栏一个会写诗的程序员的博客

在浏览器扩展程序中进行: 跨域 XMLHttpRequest 请求

跨域 XMLHttpRequest 请求 https://crxdoc-zh.appspot.com/extensions/xhr

20730
来自专栏做全栈攻城狮

程序员带你学习安卓开发-两种显示互联网上的图片的方式 及动画

本系列教程致力于可以快速的进行学习安卓开发,按照项目式的方法,通常一篇文章会做一个小程序。提高学习的兴趣。

7110
来自专栏Spark学习技巧

调试flink源码

本文主要是讲讲flink的源码编译,案例运行,flink源码调试过程。调试flink的源码及案例,需要先clone工程,编一下源码,去掉规范检查,修改工程,最后...

82450
来自专栏散尽浮华

Nginx性能优化功能- Gzip压缩(大幅度提高页面加载速度)

Nginx开启Gzip压缩功能, 可以使网站的css、js 、xml、html 文件在传输时进行压缩,提高访问速度, 进而优化Nginx性能!  Web网站上的...

40920
来自专栏做全栈攻城狮

程序员带你学习安卓开发-两种显示互联网上的图片的方式 及动画

本系列教程致力于可以快速的进行学习安卓开发,按照项目式的方法,通常一篇文章会做一个小程序。提高学习的兴趣。

10250
来自专栏Spring相关

LitePal操作数据库

15120
来自专栏Jed的技术阶梯

HBase Region 自动拆分策略

其中BusyRegionSplitPolicy是HBase-2.x新增的策略,其他6种在HBase-1.2.x中也可以使用。

88610

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励