IDispinterfaceImpl
От: Alexey Shirshov Россия http://wise-orm.com
Дата: 15.06.03 14:49
Оценка:
Всем привет.

Хелпер для реализации чистого dispinterface'а.
template <class T>
struct _ATL_EVENT_ENTRY_EX
{
    DISPID dispid;                //DISPID of method/property
    void (__stdcall T::*pfn)();    //method to invoke
    _ATL_FUNC_INFO* pInfo;
    INVOKEKIND kind;            //property or func
};

#define BEGIN_DISP_MAP(_class)\
    static const _ATL_EVENT_ENTRY_EX<_class>* _GetSinkMap()\
    {\
        typedef _class _atl_event_classtype;\
        static const _ATL_EVENT_ENTRY_EX<_class> map[] = {

#define DISP_ENTRY_INFO(dispid, fn, info, kind) \
    {dispid, (void (__stdcall _atl_event_classtype::*)())fn, info, kind},

#define END_DISP_MAP() {0, NULL, NULL, (INVOKEKIND)0} }; return map;}

template<class T>
class ATL_NO_VTABLE IDispinterfaceImpl : public IDispatch
{
public:
    IDispinterfaceImpl()
    {
        _tih.m_wMajor = 0;
        _tih.m_wMinor = 0;
        _tih.m_pguid = NULL;
        _tih.m_plibid = NULL;
        _tih.m_pInfo = NULL;
    }

    void SetTypeInfo(WORD wMajor,WORD wMinor,REFIID riid,REFIID libid)
    {
        _tih.m_wMajor = wMajor;
        _tih.m_wMinor = wMinor;
        _tih.m_pguid = &riid;
        _tih.m_plibid = &libid;
    }

protected:
    WORD KindToFlag(INVOKEKIND k)
    {
        return (WORD)k;
    }

    STDMETHOD(GetTypeInfoCount)(UINT* pctinfo)
    {
        *pctinfo = 1;
        return S_OK;
    }

    STDMETHOD(GetTypeInfo)(UINT itinfo, LCID lcid, ITypeInfo** pptinfo)
    {
        return _tih.GetTypeInfo(itinfo, lcid, pptinfo);
    }

    STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
        LCID lcid, DISPID* rgdispid)
    {
        return _tih.GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
    }

    STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid,
        LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
        EXCEPINFO* /*pexcepinfo*/, UINT* /*puArgErr*/)
    {
        T* pT = static_cast<T*>(this);
        const _ATL_EVENT_ENTRY_EX<T>* pMap = T::_GetSinkMap();
        const _ATL_EVENT_ENTRY_EX<T>* pFound = NULL;
//        void (__stdcall T::*pEvent)() = NULL;
        while (pMap->dispid != 0)
        {
            if (pMap->dispid == dispidMember &&
                (KindToFlag(pMap->kind) & wFlags) != 0){
                pFound = pMap;
                break;
            }
            pMap++;
        }
        if (pFound == NULL)
            return S_OK;
        
        //DISPATCH_METHOD
        _ATL_FUNC_INFO info;
        _ATL_FUNC_INFO* pInfo;
        if (pFound->pInfo != NULL)
            pInfo = pFound->pInfo;
        else{
            pInfo = &info;
            if ((pFound->kind & INVOKE_FUNC) == INVOKE_FUNC){
                HRESULT hr = GetFuncInfoFromId(*_tih.m_pguid, dispidMember,
                    lcid, info,pFound->kind);
                if (FAILED(hr))
                    return S_OK;
            }
            else{
                UINT i=0;
                for (;i < pdispparams->cArgs; i++)
                {
                    info.pVarTypes[i] = pdispparams[i].rgvarg->vt;
                }

                if ((wFlags & DISPATCH_PROPERTYGET) != DISPATCH_PROPERTYGET){
                    info.vtReturn = VT_ERROR;
                    info.cc = CC_STDCALL;
                    info.nParams = pdispparams->cArgs;
                }
                else{
                    CComPtr<ITypeInfo> spTypeInfo;
                    HRESULT hr = _tih.GetTI(lcid, &spTypeInfo);
                    if (FAILED(hr))
                        return hr;
                    
                    CComQIPtr<ITypeInfo2, &IID_ITypeInfo2> spTypeInfo2(spTypeInfo);

                    VARDESC* pVarDesc = NULL;
                    if (spTypeInfo2 != NULL){
                        UINT nIndex;
                        hr = spTypeInfo2->GetVarIndexOfMemId(dispidMember, &nIndex);
                        if (FAILED(hr) && (wFlags & DISPATCH_METHOD) == DISPATCH_METHOD){
                            FUNCDESC* pFuncDesc = NULL;
                            UINT nIndex;
                            hr = spTypeInfo2->GetFuncIndexOfMemId(
                                dispidMember, INVOKE_PROPERTYGET, &nIndex);
                            if (FAILED(hr))
                                return hr;

                            hr = spTypeInfo->GetFuncDesc(
                                nIndex, &pFuncDesc);
                            if (FAILED(hr))
                                return hr;

                            info.pVarTypes[i] = pFuncDesc->lprgelemdescParam[pFuncDesc->cParams - i].tdesc.vt;
                            if (info.pVarTypes[i] == VT_PTR)
                                info.pVarTypes[i] = pFuncDesc->lprgelemdescParam[pFuncDesc->cParams - i].tdesc.lptdesc->vt | VT_BYREF;
                            if (info.pVarTypes[i] == VT_USERDEFINED)
                                info.pVarTypes[i] = GetUserDefinedType(spTypeInfo,pFuncDesc->lprgelemdescParam[pFuncDesc->cParams-i].tdesc.hreftype);
                        }
                        else if (SUCCEEDED(hr)){
                            hr = spTypeInfo->GetVarDesc(nIndex, &pVarDesc);
                            if (FAILED(hr))
                                return hr;
                            
                            info.pVarTypes[i] = pVarDesc->elemdescVar.tdesc.vt|VT_BYREF;
                        }
                        else
                            return hr;
                    }
                    
                    //if (info.pVarTypes[i] == VT_PTR)
                    //    info.pVarTypes[i] = pFuncDesc->lprgelemdescParam[pFuncDesc->cParams - i - 1].tdesc.lptdesc->vt | VT_BYREF;
                    //if (info.pVarTypes[i] == VT_USERDEFINED)
                    //    info.pVarTypes[i] = GetUserDefinedType(spTypeInfo,pFuncDesc->lprgelemdescParam[pFuncDesc->cParams-i-1].tdesc.hreftype);

                    //info.pVarTypes[i] = VT_BSTR|VT_BYREF;
                    info.vtReturn = VT_ERROR;
                    info.cc = CC_STDCALL;
                    info.nParams = pdispparams->cArgs+1;
                    
                    VARIANTARG** pVarArgs = 
                        info.nParams ? (VARIANTARG**)alloca(sizeof(VARIANTARG*)*info.nParams) : 0;
                    
                    int i=0;
                    for (; i<info.nParams-1; i++)
                        pVarArgs[i] = &pdispparams->rgvarg[info.nParams - i - 2];

                    CComVariant v;
                    v.vt = VT_VARIANT|VT_BYREF;
                    v.pvarVal = pvarResult;
                    pVarArgs[i] = &v;
                    
                    CComStdCallThunk<T> thunk;
                    thunk.Init(pFound->pfn, pT);
                    CComVariant tmpResult;

                    hr = DispCallFunc(
                        &thunk,
                        0,
                        info.cc,
                        info.vtReturn,
                        info.nParams,
                        info.pVarTypes,
                        pVarArgs,
                        &tmpResult);
                    ATLASSERT(SUCCEEDED(hr));
                    return hr;
                }
            }
        }
        InvokeFromFuncInfo(pFound->pfn, info, pdispparams, pvarResult);
        
        return S_OK;
    }

    //Helper for invoking the event
    HRESULT InvokeFromFuncInfo(void (__stdcall T::*pEvent)(),
        _ATL_FUNC_INFO& info, DISPPARAMS* pdispparams,
        VARIANT* pvarResult)
    {
        T* pT = static_cast<T*>(this);
        VARIANTARG** pVarArgs = 
            info.nParams ? (VARIANTARG**)alloca(sizeof(VARIANTARG*)*info.nParams) : 0;
        
        UINT i=0;
        bool f = false;
        bool d = false;
        for (; i<pdispparams->cArgs; i++)
            pVarArgs[i] = &pdispparams->rgvarg[pdispparams->cArgs - i - 1];

        if (pdispparams->cArgs < (UINT)info.nParams){
            CComVariant v;
            v.vt = info.pVarTypes[0];
            v.pvarVal = pvarResult;
            pVarArgs[i] = &v;
            f = true;
            VARTYPE vt = info.pVarTypes[0];
            for(int h = 1;h < info.nParams;h++)
                info.pVarTypes[h-1] = info.pVarTypes[h];
            info.pVarTypes[h-1] = vt;
        }

        CComStdCallThunk<T> thunk;
        thunk.Init(pEvent, pT);
        CComVariant tmpResult;

        HRESULT hr = DispCallFunc(
            &thunk,
            0,
            info.cc,
            info.vtReturn,
            info.nParams,
            info.pVarTypes,
            pVarArgs,
            &tmpResult);
        ATLASSERT(SUCCEEDED(hr));
        
        if (!f && pvarResult)
            VariantCopy(pvarResult,&tmpResult);

        return hr;
    }

    //Helper for finding the function index for a DISPID
    virtual HRESULT GetFuncInfoFromId(const IID& iid, DISPID dispidMember,
        LCID lcid, _ATL_FUNC_INFO& info, INVOKEKIND k)
    {
        CComPtr<ITypeInfo> spTypeInfo;
        HRESULT hr = _tih.GetTI(lcid, &spTypeInfo);
        if (FAILED(hr))
            return hr;
        CComQIPtr<ITypeInfo2, &IID_ITypeInfo2> spTypeInfo2(spTypeInfo);

        FUNCDESC* pFuncDesc = NULL;
        if (spTypeInfo2 != NULL)
        {
            UINT nIndex;
            hr = spTypeInfo2->GetFuncIndexOfMemId(dispidMember, k, &nIndex);
            if (FAILED(hr))
                return hr;

            hr = spTypeInfo->GetFuncDesc(nIndex, &pFuncDesc);
            if (FAILED(hr))
                return hr;
        }
        else // search for funcdesc
        {
            TYPEATTR* pAttr;
            hr = spTypeInfo->GetTypeAttr(&pAttr);
            if (FAILED(hr))
                return hr;
            for (int i=0;i<pAttr->cFuncs;i++)
            {
                hr = spTypeInfo->GetFuncDesc(i, &pFuncDesc);
                if (FAILED(hr))
                    return hr;
                if (pFuncDesc->memid == dispidMember)
                    break;
                spTypeInfo->ReleaseFuncDesc(pFuncDesc);
                pFuncDesc = NULL;
            }
            spTypeInfo->ReleaseTypeAttr(pAttr);
            if (pFuncDesc == NULL)
                return E_FAIL;
        }

        // If this assert occurs, then add a #define _ATL_MAX_VARTYPES nnnn
        // before including atlcom.h
        ATLASSERT(pFuncDesc->cParams <= _ATL_MAX_VARTYPES);
        if (pFuncDesc->cParams > _ATL_MAX_VARTYPES)
            return E_FAIL;

        for (int i=0; i<pFuncDesc->cParams; i++)
        {
            info.pVarTypes[i] = pFuncDesc->lprgelemdescParam[pFuncDesc->cParams - i - 1].tdesc.vt;
            if (info.pVarTypes[i] == VT_PTR)
                info.pVarTypes[i] = pFuncDesc->lprgelemdescParam[pFuncDesc->cParams - i - 1].tdesc.lptdesc->vt | VT_BYREF;
            if (info.pVarTypes[i] == VT_USERDEFINED)
                info.pVarTypes[i] = GetUserDefinedType(spTypeInfo,pFuncDesc->lprgelemdescParam[pFuncDesc->cParams-i-1].tdesc.hreftype);
        }

        VARTYPE vtReturn = pFuncDesc->elemdescFunc.tdesc.vt;
        switch(vtReturn)
        {
        case VT_INT:
            vtReturn = VT_I4;
            break;
        case VT_UINT:
            vtReturn = VT_UI4;
            break;
        case VT_VOID:
            vtReturn = VT_EMPTY; // this is how DispCallFunc() represents void
            break;
        case VT_HRESULT:
            vtReturn = VT_ERROR;
            break;
        }
        info.vtReturn = vtReturn;
        info.cc = pFuncDesc->callconv;
        info.nParams = pFuncDesc->cParams;
        spTypeInfo->ReleaseFuncDesc(pFuncDesc);
        return S_OK;
    }

    VARTYPE GetUserDefinedType(ITypeInfo *pTI, HREFTYPE hrt)
    {
        CComPtr<ITypeInfo> spTypeInfo;
        VARTYPE vt = VT_USERDEFINED;
        HRESULT hr = E_FAIL;
        hr = pTI->GetRefTypeInfo(hrt, &spTypeInfo);
        if(FAILED(hr))
            return vt;
        TYPEATTR *pta=NULL;

        spTypeInfo->GetTypeAttr(&pta);
        if(pta && pta->typekind == TKIND_ALIAS)
        {
            if (pta->tdescAlias.vt == VT_USERDEFINED)
                GetUserDefinedType(spTypeInfo,pta->tdescAlias.hreftype);
            else
                vt = pta->tdescAlias.vt;
        }
    
        if(pta)
            spTypeInfo->ReleaseTypeAttr(pta);
        return vt;

    }

private:
    CComTypeInfoHolder _tih;
};


Как видите, очень много кода бессовестно содрано с IDispEventImpl.
Отличия:

Пример использования.

Предположим имеется такое описание интерфейса
    [
        uuid(D784923A-281D-4c4f-BC2D-704A479C19B4),
        helpstring("Ib Interface")
    ]
    dispinterface Ib
    {
    properties:
        [id(10), helpstring("property Hello")]
        BSTR HelloStr;
    methods:
        [id(1), helpstring("method Hello")]
        HRESULT Hello([out,retval] BSTR* str);

        [id(2), propget, helpstring("index property")]
        HRESULT Hellos([in] long Idx, [out,retval] BSTR* str);
    };


Тогда вот так будет выглядет кокласс.

class ATL_NO_VTABLE Ca : 
    public CComObjectRootEx<CComSingleThreadModel>,
    public CComCoClass<Ca, &CLSID_a1>,
    public IDispinterfaceImpl<Ca>
{
public:
    Ca()
    {
    }

    CComBSTR str;
    
    HRESULT FinalConstruct()
    {
        SetTypeInfo(1,0,DIID_Ib,LIBID_DISPLib);
        return S_OK;
    }

    void FinalRelease()
    {
        int h = 0;
    }
    
    DECLARE_REGISTRY_RESOURCEID(IDR_A)

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    BEGIN_COM_MAP(Ca)
        COM_INTERFACE_ENTRY_IID(DIID_Ib,IDispatch)
        COM_INTERFACE_ENTRY(IDispatch)
    END_COM_MAP()

    BEGIN_DISP_MAP(Ca)
       DISP_ENTRY_INFO(1, OnHello,NULL,INVOKE_FUNC)
       DISP_ENTRY_INFO(2, OnGetHellosStr,NULL,INVOKE_PROPERTYGET)
       DISP_ENTRY_INFO(10, OnSetHelloStr,NULL,INVOKE_PROPERTYPUT)
       DISP_ENTRY_INFO(10, OnGetHelloStr,NULL,INVOKE_PROPERTYGET)
    END_DISP_MAP()

public:
    HRESULT __stdcall OnHello(VARIANT* v)
    {
        (*v).vt = VT_BSTR;
        (*v).bstrVal = str.Copy();
        return S_OK;
    }

    HRESULT __stdcall OnSetHelloStr(BSTR b)
    {
        str = b;
        return S_OK;
    }

    HRESULT __stdcall OnGetHelloStr(VARIANT* v)
    {
        (*v).vt = VT_BSTR;
        (*v).bstrVal = str.Copy();
        return S_OK;
    }

    HRESULT __stdcall OnGetHellosStr(long i,VARIANT* v)
    {
        (*v).vt = VT_BSTR;
        (*v).bstrVal = ::SysAllocString(L"dima");
        int h = 0;
        return S_OK;
    }
};


Замечания.
1. Особо сильно не тестировал, найдете баги — дайте знать.
2. Для всех возвращаемых значений тип в реализации должен быть VARIANT*.
3. VBScript совсем глупый язык, так что если вы вызовете свойство Hellos напрмер, так
Dim i
i = 8456894
c.Hellos(i)

AV вам гарантирован. Будте осторожны также с таким кодом
c.Hellos(45)

vb передаст параметр как short , т.е. в старшей половине i будет мусор.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.