Всем привет.
Хелпер для реализации чистого 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.
Отличия:
Наконец-то поддерживаются retval'ы
Поддерживаются свойства
Поддерживаются свойства с параметрами
Пример использования.
Предположим имеется такое описание интерфейса
[
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 будет мусор.