帧上的权限被拒绝

Permission denied on frame

本文关键字:拒绝 权限      更新时间:2023-10-16

我有一个基于CAxWindow的窗口。在此窗口中,我创建了 Web 浏览器控件。当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
}

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

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

  • IActiveScriptSite对象上实现IServiceProvider,并将所有服务请求转发到从IHTMLDocument2对象获得的IServiceProviderdoc)。这可能有效,也可能无效。
  • 使用本机 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