随着人们对客户端软件界面要求的不断提高,软件开发商面临着一个问题:如何快速廉价开发出各种丰富效果的UI界面。设计出一套丰富控件的界面库是不容易的,且产品经理丰富的想法和UED对效果的追求,往往会使程序员疲于编写这些“效果控件”。目前市面上使用的很多界面库是基于XML描述的,界面引擎解析这些XML并渲染出其对应的效果。其实我们网页也是这样的原理,只是其复杂程度往往比市面上的界面库要复杂的多,且是无窗口控件(减少内存)。于是重用IE便成为一种很好的解决软件开发商面临问题的方法。(转载请指明出处)
“拿人东西手短”,我们使用IE控件,体验着其便利,但是也往往会遇到IE默认设置对我们控件的影响。举个很简单的例子,QQ2011(其他版本没试过)的历史聊天记录部分就是通过JS加载聊天内容,如果你在IE设置中将“脚本设置”设置为“禁用”,你将看不到聊天记录。或许在用户遇到这样的问题时会询问其客服如何解决,客服可能会让他把他的“脚本设置”设置为“启用”,但是对于这样的少数用户,其一定有其将该选项设置为“禁用”的理由。我们程序员该做的就是如何设计好自己的程序,让其对用户不良的影响减少。
针对“如何在内嵌IE网页中消除IE默认设置影响”,微软其实已经给了我们例子。
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中文件
if (dwAction <= URLACTION_SCRIPT_MAX && dwAction >= URLACTION_SCRIPT_MIN) dwPolicy = URLPOLICY_ALLOW;这句就是说,不管用户设置的是“启用”、“禁用”或“提示”,本内嵌IE对活动脚本的设置都是“启用”。
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的返回值来决定返回值。