我有一个基于CAxWindow的窗口。在此窗口中,我创建WebBrowser控件。当发生DISPID_DOCUMENTCOMPLETE时,我会执行以下操作:

void __stdcall document_complete( LPDISPATCH pBrowser, VARIANT* )
{
    CComQIPtr< IWebBrowser2 > wb( pBrowser );

    CComPtr< IDispatch > doc;

    if( SUCCEEDED( wb->get_Document( &doc ) ) )
    {
        _docs.push_back( doc );
    }

    ...
}

加载页面后,我会调用_docs中的每个文档脚本(IActiveScript和IActiveScriptSite):
function main( doc )
{
    try
    {
        return doc.URL;
    }
    catch( e )
    {
        return "Error: " + e.description;
    }
}

在某些文档上,我收到错误消息:“权限被拒绝”。
使用本地代码,我没有任何问题:
for( auto disp : _docs )
{
    CComQIPtr< IHTMLDocument2 > doc( disp );

    _bstr_t url;

    ATLVERIFY( SUCCEEDED( doc->get_URL( url.GetAddress() ) ) );
}

如何避免该错误?

原来,脚本不是原因:
for( auto disp : _docs )
{
    CComQIPtr< IDispatchEx > doc( disp );

    DISPID id = 0;
    auto hr = doc->GetDispID( _bstr_t( L"URL" ), 0, &id );

    // hr == E_ACCESSDENIED
}

最佳答案

据我了解,您有一个自定义脚本托管主机,该主机通过Active Scripting接口(interface)托管JavaScript。 JavaScript引擎使用IDispatchEx::Invoke调用COM对象(只要IDispatchEx可用,如MSHTML doc对象一样),然后将其自己的IServiceProvider传递给调用。我想这就是doc实现如何知道它是从不同于其自身脚本的脚本环境中调用的,并且出于安全原因(与本机代码调用不同)限制了其方法。

我不知道有记录的方式可以关闭此行为,但是您可以尝试以下选项:

  • IServiceProvider对象上实现IActiveScriptSite,并将所有服务请求转发到您将从IServiceProvider对象(IHTMLDocument2)获得的doc。这可能会或可能不会。
  • 用本机C++对象包装doc,该对象仅实现IDispatch并将其所有调用转发到IHTMLDocument2。将包装器对象而不是原始的doc传递给您的脚本。很有可能这样做。

  • 让我们知道您是否对上述情况有任何希望。

    [编辑] 尝试以下操作:
    class CDispatchWrapper:
        public CComObjectRoot,
        public IDispatch
    {
    // http://stackoverflow.com/questions/18718366/permission-denied-on-frame/
    protected:
        CDispatchWrapper() {}
    
        struct MEMBER
        {
            CComPtr<IUnknown> unk;
            CComPtr<ITypeInfo> ti;
        };
    
        CComPtr<ITypeLib> m_typeLib;
        CComPtr<IDispatch> m_dispatch;
        CSimpleMap<CComBSTR, DISPID> m_dispids;
        CSimpleMap<DISPID, MEMBER> m_members;
    
    public:
        BEGIN_COM_MAP(CDispatchWrapper)
            COM_INTERFACE_ENTRY(IDispatch)
        END_COM_MAP()
    
        // create and initialize
        static HRESULT Create(IDispatch* dispatch, const GUID& libid, CDispatchWrapper** pp)
        {
            CComObject<CDispatchWrapper>* pThis = NULL;
            CComObject<CDispatchWrapper>::CreateInstance(&pThis);
            if (!pThis)
                return E_OUTOFMEMORY;
    
            if ( FAILED(LoadRegTypeLib(libid, 0xFFFF, 0xFFFF, 0, &pThis->m_typeLib)) )
                return E_FAIL;
    
            pThis->m_dispatch = dispatch;
    
            (*pp = pThis)->AddRef();
            return S_OK;
        }
    
        // IDispatch
        STDMETHODIMP GetTypeInfoCount(UINT* pctinfo)
        {
            return E_NOTIMPL;
        }
    
        STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
        {
            return E_NOTIMPL;
        }
    
        STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid)
        {
            if ( cNames != 1 || !rgszNames || !rgszNames[0] || !*rgszNames[0] || !rgdispid )
                return E_INVALIDARG;
    
            CComBSTR name(rgszNames[0]);
            if ( !name )
                return E_OUTOFMEMORY;
    
            int n = m_dispids.FindKey(name);
            if ( n >= 0 )
            {
                DISPID dispid = m_dispids.GetValueAt(n);
                if ( dispid == DISPID_UNKNOWN )
                    return DISP_E_UNKNOWNNAME;
                rgdispid[0] = dispid;
                return S_OK;
            }
    
            // find the name(s) in the typelib
            UINT cMax = m_typeLib->GetTypeInfoCount();
            ITypeInfo** ppTypeInfo = (ITypeInfo**)_alloca(sizeof(ITypeInfo*) * cMax);
            MEMBERID* pMemberid = (MEMBERID*)_alloca(sizeof(MEMBERID*) * cMax);
            USHORT cTypes = cMax;
            if ( FAILED(m_typeLib->FindName(name, 0, ppTypeInfo, pMemberid, &cTypes)) || !cTypes )
                return DISP_E_UNKNOWNNAME;
    
            bool found = false;
            MEMBER member;
            DISPID dispid = DISPID_UNKNOWN;
    
            for ( int i = 0; i < cTypes && !found; i++ )
            {
                TYPEATTR* pTypeAttr = NULL;
                member.ti.Release();
                member.unk.Release();
    
                member.ti = ppTypeInfo[i];
                member.ti->GetTypeAttr(&pTypeAttr);
                if (pTypeAttr)
                {
                    // check to see if m_dispatch object also implements pTypeAttr->guid interface
                    m_dispatch->QueryInterface(pTypeAttr->guid, (void**)&member.unk);
                    if (member.unk)
                    {
                        // could use pMemberid[i], but let's make sure
                        dispid = DISPID_UNKNOWN;
                        if ( SUCCEEDED(member.ti->GetIDsOfNames(rgszNames, 1, &dispid)) )
                            found = true;
                    }
                    member.ti->ReleaseTypeAttr(pTypeAttr);
                }
            }
    
            for ( int i = 0; i < cTypes; i++ )
                ppTypeInfo[i]->Release();
    
            if (found)
            {
                if ( !m_dispids.Add(name, dispid) )
                    return E_OUTOFMEMORY;
                if ( !m_members.Add(dispid, member) )
                    return E_OUTOFMEMORY;
    
                rgdispid[0] = dispid;
                return S_OK;
            }
    
            if ( !m_dispids.Add(name, DISPID_UNKNOWN) )
                return E_OUTOFMEMORY;
    
            return DISP_E_UNKNOWNNAME;
        }
    
        STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
        {
            int n = m_members.FindKey(dispidMember);
            if ( n >= 0 )
            {
                const MEMBER& member = m_members.GetValueAt(n);
                return member.ti->Invoke(member.unk, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
            }
    
            return DISP_E_MEMBERNOTFOUND;
        }
    };
    

    用法:
    CComPtr<IHTMLDocument2> doc;
    // ...
    // once doc != NULL, wrap it
    CComPtr<CDispatchWrapper> wrapper;
    CDispatchWrapper::Create(doc, LIBID_MSHTML, &wrapper);
    // now pass the wrapper to the script, instead of doc
    

    10-04 15:08