Permission denied on frame

允我心安 提交于 2019-12-11 04:07:17

问题


I have a window based on the CAxWindow. In this window I create WebBrowser control. When the DISPID_DOCUMENTCOMPLETE happens I do:

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

    CComPtr< IDispatch > doc;

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

    ...
}

When the page is loaded I call for each document in _docs the script (IActiveScript and IActiveScriptSite):

function main( doc )
{
    try
    {
        return doc.URL;
    }
    catch( e )
    {
        return "Error: " + e.description;
    }
}

On some documents I get error: "Permission denied". With native code I haven't any problems:

for( auto disp : _docs )
{
    CComQIPtr< IHTMLDocument2 > doc( disp );

    _bstr_t url;

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

How can I avoid the error?

It turned out that the scripts were not the cause:

for( auto disp : _docs )
{
    CComQIPtr< IDispatchEx > doc( disp );

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

    // hr == E_ACCESSDENIED
}

回答1:


As far as I understand, you have a custom scripting host hosting JavaScript through Active Scripting interfaces. JavaScript engine uses IDispatchEx::Invoke to call COM objects (whenever IDispatchEx is available, as with MSHTML doc object) and passes its own IServiceProvider to the call. I suppose, that's how doc implementation is aware it's being called from a scripting environment different from its own scripts, and restrict its methods for security reasons (unlike with native code calls).

I'm not aware of a documented way to turn this behavior off, but you could try these options:

  • Implement IServiceProvider on your IActiveScriptSite object and forward all service requests to the IServiceProvider you would get from the IHTMLDocument2 object (doc). This may or may not work.
  • Wrap doc with a native C++ object which implements only IDispatch and forward all its calls to IHTMLDocument2. Pass the wrapper object to your script instead of the original doc. Chances are high this should work.

Let us know if you have any luck with the above.

[EDITED] Try this:

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;
    }
};

Usage:

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


来源:https://stackoverflow.com/questions/18718366/permission-denied-on-frame

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!