1039 lines
32 KiB
C++
1039 lines
32 KiB
C++
#pragma once
|
|
|
|
// Extensions to ATL to enable things it doesn't natively support
|
|
|
|
template <const CLSID* pcoclsid, const IID* psrcid, class tihclass = CMarsTypeInfoHolder>
|
|
class MarsIProvideClassInfo2Impl : public IProvideClassInfo2Impl<pcoclsid, psrcid, NULL, 0, 0, tihclass>
|
|
{
|
|
|
|
};
|
|
|
|
template <class T, const IID* piid, class tihclass = CMarsTypeInfoHolder>
|
|
class MarsIDispatchImpl : public IDispatchImpl<T, piid, NULL, 0, 0, tihclass>
|
|
{
|
|
public:
|
|
STDMETHOD(Invoke)(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
|
|
DISPPARAMS *pdispparams, VARIANT *pvarResult,
|
|
EXCEPINFO *pexcepinfo, UINT *puArgErr)
|
|
{
|
|
HRESULT hr = IDispatchImpl<T, piid, NULL, 0, 0, tihclass>::Invoke(dispidMember,
|
|
riid,
|
|
lcid,
|
|
wFlags,
|
|
pdispparams,
|
|
pvarResult,
|
|
pexcepinfo,
|
|
puArgErr);
|
|
hr = SanitizeResult(hr);
|
|
|
|
if (DISP_E_EXCEPTION == hr)
|
|
{
|
|
// We're getting DISP_E_EXCEPTION returns which are not generated by Mars
|
|
// whenever invalid parameter types are passed. They're probably coming
|
|
// from oleaut itself.
|
|
// ASSERT(NULL != m_pwszException);
|
|
|
|
if ((NULL != m_pwszException) && (NULL != pexcepinfo))
|
|
{
|
|
memset(pexcepinfo, 0, sizeof(EXCEPINFO));
|
|
pexcepinfo->wCode = (WORD)dispidMember;
|
|
pexcepinfo->bstrSource = SysAllocString(L"OM Exception");
|
|
pexcepinfo->bstrDescription = SysAllocString(m_pwszException);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
protected:
|
|
LPWSTR m_pwszException;
|
|
};
|
|
|
|
// each module implements this themselves
|
|
HRESULT GetMarsTypeLib(ITypeLib **ppTypeLib);
|
|
|
|
//==================================================================
|
|
// Begin CComTypeInfoHolder override
|
|
//
|
|
// By providing our own CComTypeInfoHolder, we can load the type
|
|
// library ourselves, rather than requiring it to be loaded from
|
|
// the registry as ATL does. Only "GetTI" is changed from
|
|
// ATL source. Since GetTI isn't virtual we need to duplicate
|
|
// the entire class.
|
|
// CMarsTypeInfoHolder accepts an ITypeLib * (with reference)
|
|
// instead of a LIBID in m_plibid
|
|
//
|
|
//==================================================================
|
|
|
|
// ATL doesn't support multiple LCID's at the same time
|
|
// Whatever LCID is queried for first is the one that is used.
|
|
class CMarsTypeInfoHolder
|
|
{
|
|
// Should be 'protected' but can cause compiler to generate fat code.
|
|
public:
|
|
const GUID* m_pguid;
|
|
const GUID* m_plibid;
|
|
WORD m_wMajor;
|
|
WORD m_wMinor;
|
|
|
|
ITypeInfo* m_pInfo;
|
|
long m_dwRef;
|
|
struct stringdispid
|
|
{
|
|
CComBSTR bstr;
|
|
int nLen;
|
|
DISPID id;
|
|
};
|
|
stringdispid* m_pMap;
|
|
int m_nCount;
|
|
public:
|
|
HRESULT GetTI(LCID lcid, ITypeInfo** ppInfo)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if (m_pInfo == NULL)
|
|
hr = GetTI(lcid);
|
|
*ppInfo = m_pInfo;
|
|
if (m_pInfo != NULL)
|
|
{
|
|
m_pInfo->AddRef();
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
HRESULT GetTI(LCID lcid);
|
|
HRESULT EnsureTI(LCID lcid)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if (m_pInfo == NULL)
|
|
hr = GetTI(lcid);
|
|
return hr;
|
|
}
|
|
|
|
// This function is called by the module on exit
|
|
// It is registered through _Module.AddTermFunc()
|
|
static void __stdcall Cleanup2(DWORD_PTR dw)
|
|
{
|
|
CMarsTypeInfoHolder* p = (CMarsTypeInfoHolder*) dw;
|
|
if (p->m_pInfo != NULL)
|
|
p->m_pInfo->Release();
|
|
p->m_pInfo = NULL;
|
|
delete [] p->m_pMap;
|
|
p->m_pMap = NULL;
|
|
}
|
|
|
|
HRESULT GetTypeInfo(UINT /* itinfo */, LCID lcid, ITypeInfo** pptinfo)
|
|
{
|
|
HRESULT hRes = E_POINTER;
|
|
if (pptinfo != NULL)
|
|
hRes = GetTI(lcid, pptinfo);
|
|
return hRes;
|
|
}
|
|
HRESULT GetIDsOfNames(REFIID /* riid */, LPOLESTR* rgszNames, UINT cNames,
|
|
LCID lcid, DISPID* rgdispid)
|
|
{
|
|
HRESULT hRes = EnsureTI(lcid);
|
|
if (m_pInfo != NULL)
|
|
{
|
|
for (int i=0; i<(int)cNames; i++)
|
|
{
|
|
int n = ocslen(rgszNames[i]);
|
|
for (int j=m_nCount-1; j>=0; j--)
|
|
{
|
|
if ((n == m_pMap[j].nLen) &&
|
|
(memcmp(m_pMap[j].bstr, rgszNames[i], m_pMap[j].nLen * sizeof(OLECHAR)) == 0))
|
|
{
|
|
rgdispid[i] = m_pMap[j].id;
|
|
break;
|
|
}
|
|
|
|
// Give debug warning if we differ only in case
|
|
//DEBUG_ONLY(StrEql(m_pMap[j].bstr, rgszNames[i]));
|
|
}
|
|
if (j < 0)
|
|
{
|
|
// Not a warning. Common for behaviors as Trident passes all calls to
|
|
// our IDispatch for the first shot. We should possibly just return
|
|
// failure in this case instead of delegating to oleaut.
|
|
hRes = m_pInfo->GetIDsOfNames(rgszNames + i, 1, &rgdispid[i]);
|
|
if (FAILED(hRes))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return hRes;
|
|
}
|
|
|
|
HRESULT Invoke(IDispatch* p, DISPID dispidMember, REFIID /* riid */,
|
|
LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult,
|
|
EXCEPINFO* pexcepinfo, UINT* puArgErr)
|
|
{
|
|
HRESULT hRes = EnsureTI(lcid);
|
|
if (m_pInfo != NULL)
|
|
hRes = m_pInfo->Invoke(p, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
|
|
return hRes;
|
|
}
|
|
HRESULT LoadNameCache(ITypeInfo* pTypeInfo)
|
|
{
|
|
TYPEATTR* pta;
|
|
HRESULT hr = pTypeInfo->GetTypeAttr(&pta);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
m_nCount = pta->cFuncs;
|
|
m_pMap = m_nCount == 0 ? 0 : new stringdispid[m_nCount];
|
|
for (int i=0; i<m_nCount; i++)
|
|
{
|
|
FUNCDESC* pfd;
|
|
if (SUCCEEDED(pTypeInfo->GetFuncDesc(i, &pfd)))
|
|
{
|
|
CComBSTR bstrName;
|
|
if (SUCCEEDED(pTypeInfo->GetDocumentation(pfd->memid, &bstrName, NULL, NULL, NULL)))
|
|
{
|
|
m_pMap[i].bstr.Attach(bstrName.Detach());
|
|
m_pMap[i].nLen = SysStringLen(m_pMap[i].bstr);
|
|
m_pMap[i].id = pfd->memid;
|
|
}
|
|
pTypeInfo->ReleaseFuncDesc(pfd);
|
|
}
|
|
}
|
|
pTypeInfo->ReleaseTypeAttr(pta);
|
|
}
|
|
return S_OK;
|
|
}
|
|
};
|
|
|
|
inline HRESULT CMarsTypeInfoHolder::GetTI(LCID lcid)
|
|
{
|
|
UNREFERENCED_PARAMETER(lcid);
|
|
|
|
// Change: removed asserts
|
|
if (m_pInfo != NULL)
|
|
return S_OK;
|
|
HRESULT hRes = E_FAIL;
|
|
EnterCriticalSection(&_Module.m_csTypeInfoHolder);
|
|
if (m_pInfo == NULL)
|
|
{
|
|
ITypeLib* pTypeLib;
|
|
// Here's a change
|
|
// hRes = LoadRegTypeLib(*m_plibid, m_wMajor, m_wMinor, lcid, &pTypeLib);
|
|
|
|
hRes = GetMarsTypeLib(&pTypeLib);
|
|
// End change
|
|
|
|
if (SUCCEEDED(hRes))
|
|
{
|
|
CComPtr<ITypeInfo> spTypeInfo;
|
|
hRes = pTypeLib->GetTypeInfoOfGuid(*m_pguid, &spTypeInfo);
|
|
if (SUCCEEDED(hRes))
|
|
{
|
|
CComPtr<ITypeInfo> spInfo(spTypeInfo);
|
|
CComPtr<ITypeInfo2> spTypeInfo2;
|
|
if (SUCCEEDED(spTypeInfo->QueryInterface(&spTypeInfo2)))
|
|
spInfo = spTypeInfo2;
|
|
|
|
LoadNameCache(spInfo);
|
|
m_pInfo = spInfo.Detach();
|
|
}
|
|
pTypeLib->Release();
|
|
}
|
|
}
|
|
LeaveCriticalSection(&_Module.m_csTypeInfoHolder);
|
|
_Module.AddTermFunc(Cleanup2, (DWORD_PTR)this);
|
|
return hRes;
|
|
}
|
|
|
|
//==================================================================
|
|
// End CComTypeInfoHolder override
|
|
//==================================================================
|
|
|
|
// CComClassPtr is like CComPtr but it works with C++ classes, by not
|
|
// assuming that we can cast to IUnknown unambiguously.
|
|
|
|
// Use caution when initializing within your constructor. You can't
|
|
// AddRef an object which hasn't finished constructing yet, so you can
|
|
// only initialize smart pointers to object which don't contain you.
|
|
template <class T>
|
|
class _NoAddRefReleaseOnCComClassPtr : public T
|
|
{
|
|
public:
|
|
// If you get a compile error here, make sure that the destructors
|
|
// for any CComClassPtr<> classes are protected instead of private
|
|
~_NoAddRefReleaseOnCComClassPtr() {}
|
|
|
|
private:
|
|
STDMETHOD_(ULONG, AddRef)()=0;
|
|
STDMETHOD_(ULONG, Release)()=0;
|
|
};
|
|
|
|
|
|
template <class T>
|
|
class CComClassPtr
|
|
{
|
|
public:
|
|
typedef T _PtrClass;
|
|
CComClassPtr()
|
|
{
|
|
p=NULL;
|
|
}
|
|
CComClassPtr(T* lp)
|
|
{
|
|
if ((p = lp) != NULL)
|
|
p->AddRef();
|
|
}
|
|
CComClassPtr(const CComClassPtr<T>& lp)
|
|
{
|
|
if ((p = lp.p) != NULL)
|
|
p->AddRef();
|
|
}
|
|
~CComClassPtr()
|
|
{
|
|
if (p)
|
|
p->Release();
|
|
}
|
|
void Release()
|
|
{
|
|
T* pTemp = p;
|
|
if (pTemp)
|
|
{
|
|
p = NULL;
|
|
pTemp->Release();
|
|
}
|
|
}
|
|
operator T*() const
|
|
{
|
|
return p;
|
|
}
|
|
T& operator*() const
|
|
{
|
|
ATLASSERT(p!=NULL);
|
|
return *p;
|
|
}
|
|
//The assert on operator& usually indicates a bug. If this is really
|
|
//what is needed, however, take the address of the p member explicitly.
|
|
T** operator&()
|
|
{
|
|
ATLASSERT(p==NULL);
|
|
return &p;
|
|
}
|
|
|
|
HRESULT PassivateAndRelease()
|
|
{
|
|
if (p)
|
|
{
|
|
HRESULT hr = p->Passivate();
|
|
Release();
|
|
return hr;
|
|
}
|
|
|
|
return S_FALSE;
|
|
}
|
|
|
|
_NoAddRefReleaseOnCComClassPtr<T>* operator->() const
|
|
{
|
|
ATLASSERT(p!=NULL);
|
|
return (_NoAddRefReleaseOnCComClassPtr<T>*)p;
|
|
}
|
|
|
|
T* AtlComClassPtrAssign(T** pp, T* lp)
|
|
{
|
|
if (lp != NULL)
|
|
lp->AddRef();
|
|
if (*pp)
|
|
(*pp)->Release();
|
|
*pp = lp;
|
|
return lp;
|
|
}
|
|
|
|
T* operator=(T* lp)
|
|
{
|
|
return AtlComClassPtrAssign(&p, lp);
|
|
}
|
|
T* operator=(const CComClassPtr<T>& lp)
|
|
{
|
|
return AtlComClassPtrAssign(&p, lp.p);
|
|
}
|
|
bool operator!() const
|
|
{
|
|
return (p == NULL);
|
|
}
|
|
bool operator<(T* pT) const
|
|
{
|
|
return p < pT;
|
|
}
|
|
bool operator==(T* pT) const
|
|
{
|
|
return p == pT;
|
|
}
|
|
// Compare two objects for equivalence
|
|
bool IsEqualObject(T* pOther)
|
|
{
|
|
return (p == pOther);
|
|
}
|
|
void Attach(T* p2)
|
|
{
|
|
if (p)
|
|
p->Release();
|
|
p = p2;
|
|
}
|
|
T* Detach()
|
|
{
|
|
T* pt = p;
|
|
p = NULL;
|
|
return pt;
|
|
}
|
|
HRESULT CopyTo(T** ppT)
|
|
{
|
|
ATLASSERT(ppT != NULL);
|
|
if (ppT == NULL)
|
|
return E_POINTER;
|
|
*ppT = p;
|
|
if (p)
|
|
p->AddRef();
|
|
return S_OK;
|
|
}
|
|
template <class Q>
|
|
HRESULT QueryInterface(Q** pp) const
|
|
{
|
|
ATLASSERT(pp != NULL && *pp == NULL);
|
|
return p->QueryInterface(__uuidof(Q), (void**)pp);
|
|
}
|
|
T* p;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CMarsComDispatchDriver / Specialization of CComQIPtr<IDispatch, IID_IDispatch>
|
|
//
|
|
// This is better than CComDispatchDriver for these reasons:
|
|
// - CComDispatchDriver in atl30 doesn't define an assignment/copy constructor
|
|
// - CcomDispatchDriver doesn't use _NoAddRefReleaseOnCComPtr
|
|
// - Added "const" to methods which are const
|
|
//
|
|
class CMarsComDispatchDriver
|
|
{
|
|
public:
|
|
CMarsComDispatchDriver()
|
|
{
|
|
p = NULL;
|
|
}
|
|
CMarsComDispatchDriver(IDispatch* lp)
|
|
{
|
|
if ((p = lp) != NULL)
|
|
p->AddRef();
|
|
}
|
|
CMarsComDispatchDriver(IUnknown* lp)
|
|
{
|
|
p=NULL;
|
|
if (lp != NULL)
|
|
lp->QueryInterface(IID_IDispatch, (void **)&p);
|
|
}
|
|
CMarsComDispatchDriver(const CMarsComDispatchDriver& lp)
|
|
{
|
|
if ((p = lp.p) != NULL)
|
|
p->AddRef();
|
|
}
|
|
|
|
~CMarsComDispatchDriver() { if (p) p->Release(); }
|
|
void Release() {if (p) p->Release(); p=NULL;}
|
|
operator IDispatch*() const {return p;}
|
|
IDispatch& operator*() const {ATLASSERT(p!=NULL); return *p; }
|
|
IDispatch** operator&() {ATLASSERT(p==NULL); return &p; }
|
|
|
|
_NoAddRefReleaseOnCComPtr<IDispatch>* operator->() const
|
|
{
|
|
ATLASSERT(p!=NULL);
|
|
return (_NoAddRefReleaseOnCComPtr<IDispatch>*)p;
|
|
}
|
|
//IDispatch* operator->() {ATLASSERT(p!=NULL); return p; }
|
|
IDispatch* operator=(IDispatch* lp){return (IDispatch*)AtlComPtrAssign((IUnknown**)&p, lp);}
|
|
IDispatch* operator=(IUnknown* lp)
|
|
{
|
|
return (IDispatch*)AtlComQIPtrAssign((IUnknown**)&p, lp, IID_IDispatch);
|
|
}
|
|
IDispatch* operator=(const CMarsComDispatchDriver& lp)
|
|
{
|
|
return (IDispatch*)AtlComPtrAssign((IUnknown**)&p, lp.p);
|
|
}
|
|
BOOL operator!() const {return (p == NULL) ? TRUE : FALSE;}
|
|
|
|
HRESULT GetPropertyByName(LPCOLESTR lpsz, VARIANT* pVar)
|
|
{
|
|
ATLASSERT(p);
|
|
ATLASSERT(pVar);
|
|
DISPID dwDispID;
|
|
HRESULT hr = GetIDOfName(lpsz, &dwDispID);
|
|
if (SUCCEEDED(hr))
|
|
hr = GetProperty(p, dwDispID, pVar);
|
|
return hr;
|
|
}
|
|
HRESULT GetProperty(DISPID dwDispID, VARIANT* pVar)
|
|
{
|
|
ATLASSERT(p);
|
|
return GetProperty(p, dwDispID, pVar);
|
|
}
|
|
HRESULT PutPropertyByName(LPCOLESTR lpsz, VARIANT* pVar)
|
|
{
|
|
ATLASSERT(p);
|
|
ATLASSERT(pVar);
|
|
DISPID dwDispID;
|
|
HRESULT hr = GetIDOfName(lpsz, &dwDispID);
|
|
if (SUCCEEDED(hr))
|
|
hr = PutProperty(p, dwDispID, pVar);
|
|
return hr;
|
|
}
|
|
HRESULT PutProperty(DISPID dwDispID, VARIANT* pVar)
|
|
{
|
|
ATLASSERT(p);
|
|
return PutProperty(p, dwDispID, pVar);
|
|
}
|
|
HRESULT GetIDOfName(LPCOLESTR lpsz, DISPID* pdispid)
|
|
{
|
|
return p->GetIDsOfNames(IID_NULL, (LPOLESTR*)&lpsz, 1, LOCALE_USER_DEFAULT, pdispid);
|
|
}
|
|
// Invoke a method by DISPID with no parameters
|
|
HRESULT Invoke0(DISPID dispid, VARIANT* pvarRet = NULL)
|
|
{
|
|
DISPPARAMS dispparams = { NULL, NULL, 0, 0};
|
|
return p->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, pvarRet, NULL, NULL);
|
|
}
|
|
// Invoke a method by name with no parameters
|
|
HRESULT Invoke0(LPCOLESTR lpszName, VARIANT* pvarRet = NULL)
|
|
{
|
|
HRESULT hr;
|
|
DISPID dispid;
|
|
hr = GetIDOfName(lpszName, &dispid);
|
|
if (SUCCEEDED(hr))
|
|
hr = Invoke0(dispid, pvarRet);
|
|
return hr;
|
|
}
|
|
// Invoke a method by DISPID with a single parameter
|
|
HRESULT Invoke1(DISPID dispid, VARIANT* pvarParam1, VARIANT* pvarRet = NULL)
|
|
{
|
|
DISPPARAMS dispparams = { pvarParam1, NULL, 1, 0};
|
|
return p->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, pvarRet, NULL, NULL);
|
|
}
|
|
// Invoke a method by name with a single parameter
|
|
HRESULT Invoke1(LPCOLESTR lpszName, VARIANT* pvarParam1, VARIANT* pvarRet = NULL)
|
|
{
|
|
HRESULT hr;
|
|
DISPID dispid;
|
|
hr = GetIDOfName(lpszName, &dispid);
|
|
if (SUCCEEDED(hr))
|
|
hr = Invoke1(dispid, pvarParam1, pvarRet);
|
|
return hr;
|
|
}
|
|
// Invoke a method by DISPID with two parameters
|
|
HRESULT Invoke2(DISPID dispid, VARIANT* pvarParam1, VARIANT* pvarParam2, VARIANT* pvarRet = NULL)
|
|
{
|
|
CComVariant varArgs[2] = { *pvarParam2, *pvarParam1 };
|
|
DISPPARAMS dispparams = { &varArgs[0], NULL, 2, 0};
|
|
return p->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, pvarRet, NULL, NULL);
|
|
}
|
|
// Invoke a method by name with two parameters
|
|
HRESULT Invoke2(LPCOLESTR lpszName, VARIANT* pvarParam1, VARIANT* pvarParam2, VARIANT* pvarRet = NULL)
|
|
{
|
|
HRESULT hr;
|
|
DISPID dispid;
|
|
hr = GetIDOfName(lpszName, &dispid);
|
|
if (SUCCEEDED(hr))
|
|
hr = Invoke2(dispid, pvarParam1, pvarParam2, pvarRet);
|
|
return hr;
|
|
}
|
|
// Invoke a method by DISPID with N parameters
|
|
HRESULT InvokeN(DISPID dispid, VARIANT* pvarParams, int nParams, VARIANT* pvarRet = NULL)
|
|
{
|
|
DISPPARAMS dispparams = { pvarParams, NULL, nParams, 0};
|
|
return p->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, pvarRet, NULL, NULL);
|
|
}
|
|
// Invoke a method by name with Nparameters
|
|
HRESULT InvokeN(LPCOLESTR lpszName, VARIANT* pvarParams, int nParams, VARIANT* pvarRet = NULL)
|
|
{
|
|
HRESULT hr;
|
|
DISPID dispid;
|
|
hr = GetIDOfName(lpszName, &dispid);
|
|
if (SUCCEEDED(hr))
|
|
hr = InvokeN(dispid, pvarParams, nParams, pvarRet);
|
|
return hr;
|
|
}
|
|
static HRESULT GetProperty(IDispatch* pDisp, DISPID dwDispID,
|
|
VARIANT* pVar)
|
|
{
|
|
ATLTRACE2(atlTraceCOM, 0, _T("CPropertyHelper::GetProperty\n"));
|
|
DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
|
|
return pDisp->Invoke(dwDispID, IID_NULL,
|
|
LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET,
|
|
&dispparamsNoArgs, pVar, NULL, NULL);
|
|
}
|
|
|
|
static HRESULT PutProperty(IDispatch* pDisp, DISPID dwDispID,
|
|
VARIANT* pVar)
|
|
{
|
|
ATLTRACE2(atlTraceCOM, 0, _T("CPropertyHelper::PutProperty\n"));
|
|
DISPPARAMS dispparams = {NULL, NULL, 1, 1};
|
|
dispparams.rgvarg = pVar;
|
|
DISPID dispidPut = DISPID_PROPERTYPUT;
|
|
dispparams.rgdispidNamedArgs = &dispidPut;
|
|
|
|
if (pVar->vt == VT_UNKNOWN || pVar->vt == VT_DISPATCH ||
|
|
(pVar->vt & VT_ARRAY) || (pVar->vt & VT_BYREF))
|
|
{
|
|
HRESULT hr = pDisp->Invoke(dwDispID, IID_NULL,
|
|
LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF,
|
|
&dispparams, NULL, NULL, NULL);
|
|
if (SUCCEEDED(hr))
|
|
return hr;
|
|
}
|
|
|
|
return pDisp->Invoke(dwDispID, IID_NULL,
|
|
LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT,
|
|
&dispparams, NULL, NULL, NULL);
|
|
}
|
|
|
|
IDispatch* p;
|
|
};
|
|
|
|
#define CComDispatchDriver _DONT_USE_CComDispatchDriver_USE_CMarsComDispatchDriver
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CMarsSimpleArray / Specialization of CSimpleArray
|
|
//
|
|
// This is better than CSimpleArray for these reasons:
|
|
// overloaded the operator=() and defined a copy constructor.
|
|
// Fixed various pointer math to use + instead of & because the operator&()
|
|
// on contained smart types (like CComPtr) gets called accidentally if you
|
|
// do this: &m_aT[i] instead of (m_aT + i).
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// Collection helpers - CMarsSimpleArray
|
|
#ifdef new
|
|
#pragma push_macro("new")
|
|
#define _ATL_REDEF_NEW
|
|
#undef new
|
|
#endif
|
|
|
|
|
|
template <class T>
|
|
class CMarsSimpleArray
|
|
{
|
|
public:
|
|
T* m_aT;
|
|
int m_nSize;
|
|
int m_nAllocSize;
|
|
|
|
// Construction/destruction
|
|
CMarsSimpleArray() : m_aT(NULL), m_nSize(0), m_nAllocSize(0)
|
|
{ }
|
|
|
|
~CMarsSimpleArray()
|
|
{
|
|
RemoveAll();
|
|
}
|
|
|
|
CMarsSimpleArray(const CMarsSimpleArray<T> ¤t) : m_aT(NULL), m_nSize(0), m_nAllocSize(0)
|
|
{
|
|
*this = current;
|
|
}
|
|
|
|
CMarsSimpleArray &operator=(const CMarsSimpleArray<T> &right)
|
|
{
|
|
if (&right != this)
|
|
{
|
|
// BUGBUG (tnoonan) -- this code is going to potentially leak
|
|
// if the new size is smaller than the old.
|
|
|
|
T *aT = NULL;
|
|
aT = (T *)realloc(m_aT, right.m_nAllocSize * sizeof(T));
|
|
|
|
// Did the realloc succeed?
|
|
if (aT)
|
|
{
|
|
m_aT = aT;
|
|
m_nSize = right.m_nSize;
|
|
m_nAllocSize = right.m_nAllocSize;
|
|
|
|
// WARNING: This is not a simple mempcy() for a very specific reason!
|
|
// Each element must be copied with = in case the T class has an
|
|
// overloaded operator=(). (i.e. in the case of smart ptrs).
|
|
//
|
|
for (int idx = 0; idx < m_nSize; ++idx)
|
|
{
|
|
m_aT[idx] = right.m_aT[idx];
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
// Operations
|
|
int GetSize() const
|
|
{
|
|
return m_nSize;
|
|
}
|
|
BOOL Add(T& t)
|
|
{
|
|
if(m_nSize == m_nAllocSize)
|
|
{
|
|
T* aT;
|
|
int nNewAllocSize = (m_nAllocSize == 0) ? 1 : (m_nSize * 2);
|
|
aT = (T*)realloc(m_aT, nNewAllocSize * sizeof(T));
|
|
if(aT == NULL)
|
|
return FALSE;
|
|
m_nAllocSize = nNewAllocSize;
|
|
m_aT = aT;
|
|
}
|
|
m_nSize++;
|
|
SetAtIndex(m_nSize - 1, t);
|
|
return TRUE;
|
|
}
|
|
BOOL Remove(T& t)
|
|
{
|
|
int nIndex = Find(t);
|
|
if(nIndex == -1)
|
|
return FALSE;
|
|
return RemoveAt(nIndex);
|
|
}
|
|
BOOL RemoveAt(int nIndex)
|
|
{
|
|
ATLASSERT(nIndex >= 0 && nIndex < m_nSize);
|
|
|
|
m_aT[nIndex].~T();
|
|
|
|
if(nIndex != (m_nSize - 1))
|
|
{
|
|
//
|
|
// BUGFIX: Use m_aT + nIndex instead of &m_aT[nIndex] to avoid calling type's operator &()
|
|
//
|
|
memmove((void*)(m_aT + nIndex), (void*)(m_aT + nIndex + 1), (m_nSize - (nIndex + 1)) * sizeof(T));
|
|
|
|
ZeroMemory((void*)(m_aT + (m_nSize-1)), sizeof(m_aT[0]));
|
|
}
|
|
m_nSize--;
|
|
return TRUE;
|
|
}
|
|
BOOL InsertAt(int nIndex, T& t)
|
|
{
|
|
// Index equal to size means to insert at the end
|
|
ATLASSERT(nIndex >= 0 && nIndex <= m_nSize);
|
|
|
|
// First check if we have room...
|
|
if(m_nSize == m_nAllocSize)
|
|
{
|
|
T* aT;
|
|
int nNewAllocSize = (m_nAllocSize == 0) ? 1 : (m_nSize * 2);
|
|
aT = (T*)realloc(m_aT, nNewAllocSize * sizeof(T));
|
|
if(aT == NULL)
|
|
return FALSE;
|
|
m_nAllocSize = nNewAllocSize;
|
|
m_aT = aT;
|
|
}
|
|
|
|
// If we're not adding to the end, then we need to shift elements past the insertion point
|
|
// down one.
|
|
if (nIndex < m_nSize)
|
|
{
|
|
memmove( (void*)(m_aT + (nIndex + 1)), (void*)(m_aT + (nIndex)), sizeof(T) * (m_nSize - nIndex));
|
|
|
|
// TRICKY: This memmove is a HACK -- it doesn't call the ctors and dtors of the elements.
|
|
// However below, we're going to make an assignment, and that assignment is going to
|
|
// cause the operator=() to fire on user types, which might mess up any
|
|
// internal pointers. Ouch. We need to avoid that by wiping out the memory
|
|
// destructively first.
|
|
//
|
|
// The reason this is bad is because the memmove has caused the nIndex and nIndex + 1
|
|
// entries to both share the same data, including pointers, so we have danglers. :-(
|
|
//
|
|
ZeroMemory((void*)(m_aT + nIndex), sizeof(T));
|
|
}
|
|
|
|
m_nSize++;
|
|
SetAtIndex(nIndex, t);
|
|
return TRUE;
|
|
}
|
|
void RemoveAll()
|
|
{
|
|
if(m_aT != NULL)
|
|
{
|
|
for(int i = 0; i < m_nSize; i++)
|
|
m_aT[i].~T();
|
|
free(m_aT);
|
|
m_aT = NULL;
|
|
}
|
|
m_nSize = 0;
|
|
m_nAllocSize = 0;
|
|
}
|
|
T& operator[] (int nIndex) const
|
|
{
|
|
ATLASSERT(nIndex >= 0 && nIndex < m_nSize);
|
|
return m_aT[nIndex];
|
|
}
|
|
T* GetData() const
|
|
{
|
|
return m_aT;
|
|
}
|
|
|
|
// Implementation
|
|
class Wrapper
|
|
{
|
|
public:
|
|
Wrapper(T& _t) : t(_t)
|
|
{
|
|
}
|
|
template <class _Ty>
|
|
void *operator new(size_t, _Ty* p)
|
|
{
|
|
return p;
|
|
}
|
|
T t;
|
|
};
|
|
void SetAtIndex(int nIndex, T& t)
|
|
{
|
|
ATLASSERT(nIndex >= 0 && nIndex < m_nSize);
|
|
//
|
|
// BUGFIX: Use m_aT + nIndex instead of &m_aT[nIndex] to avoid calling type's operator &()
|
|
//
|
|
new(m_aT + nIndex) Wrapper(t);
|
|
}
|
|
int Find(T& t) const
|
|
{
|
|
for(int i = 0; i < m_nSize; i++)
|
|
{
|
|
if(m_aT[i] == t)
|
|
return i;
|
|
}
|
|
return -1; // not found
|
|
}
|
|
}; // CMarsSimpleArray
|
|
|
|
|
|
// for arrays of simple types
|
|
template <class T>
|
|
class CMarsSimpleValArray : public CMarsSimpleArray< T >
|
|
{
|
|
public:
|
|
BOOL Add(T t)
|
|
{
|
|
return CMarsSimpleArray< T >::Add(t);
|
|
}
|
|
BOOL Remove(T t)
|
|
{
|
|
return CMarsSimpleArray< T >::Remove(t);
|
|
}
|
|
T operator[] (int nIndex) const
|
|
{
|
|
return CMarsSimpleArray< T >::operator[](nIndex);
|
|
}
|
|
|
|
CMarsSimpleValArray &operator=(const CMarsSimpleValArray<T> &right)
|
|
{
|
|
if (&right != this)
|
|
{
|
|
T *aT = NULL;
|
|
aT = (T *)realloc(m_aT, right.m_nAllocSize * sizeof(T));
|
|
|
|
// Did the realloc succeed?
|
|
if (aT)
|
|
{
|
|
m_aT = aT;
|
|
m_nSize = right.m_nSize;
|
|
m_nAllocSize = right.m_nAllocSize;
|
|
|
|
CopyMemory(m_aT, right.m_aT, sizeof(T) * m_nSize);
|
|
}
|
|
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
};
|
|
|
|
|
|
#ifdef _ATL_REDEF_NEW
|
|
#pragma pop_macro("new")
|
|
#undef _ATL_REDEF_NEW
|
|
#endif
|
|
|
|
|
|
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
// class CComTableMarshalPtr
|
|
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
template <class I>
|
|
class CComTableMarshalPtr : public CComPtr<I>
|
|
{
|
|
public:
|
|
CComTableMarshalPtr(DWORD dwGITKey);
|
|
virtual ~CComTableMarshalPtr();
|
|
|
|
static HRESULT RegisterInterface(IUnknown *pUnk, DWORD *pdwKey);
|
|
static HRESULT RevokeInterface(DWORD dwKey);
|
|
|
|
private:
|
|
CComTableMarshalPtr(); // Protect access to default ctor
|
|
}; // CComTableMarshalPtr
|
|
|
|
|
|
//============================================================================
|
|
// class CComTableMarshalPtr
|
|
//============================================================================
|
|
template <class I>
|
|
CComTableMarshalPtr<I>::CComTableMarshalPtr(DWORD dwKey)
|
|
{
|
|
CComPtr<IGlobalInterfaceTable> spGIT(CMarsGlobalsManager::GIT());
|
|
|
|
if (spGIT)
|
|
{
|
|
I *pInt = NULL;
|
|
|
|
HRESULT hr = spGIT->GetInterfaceFromGlobal(dwKey, __uuidof(I), (void **)&pInt);
|
|
|
|
if (SUCCEEDED(hr))
|
|
Attach(pInt);
|
|
}
|
|
} // CComTableMarshalPtr
|
|
|
|
template <class I>
|
|
CComTableMarshalPtr<I>::~CComTableMarshalPtr()
|
|
{
|
|
} // ~CComTableMarshalPtr
|
|
|
|
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
//
|
|
//
|
|
//
|
|
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
template <class I>
|
|
HRESULT CComTableMarshalPtr<I>::RegisterInterface(IUnknown *pInt, DWORD *pdwKey)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (IsValidInterfacePtr(pInt) && IsValidWritePtr(pdwKey))
|
|
{
|
|
CComPtr<IGlobalInterfaceTable> spGIT(CMarsGlobalsManager::GIT());
|
|
|
|
ATLASSERT(spGIT);
|
|
|
|
if (spGIT)
|
|
{
|
|
hr = spGIT->RegisterInterfaceInGlobal(pInt, __uuidof(I), pdwKey);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
} // RegisterInterface
|
|
|
|
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
//
|
|
// CComTableMarshalPtr::RevokeInterface()
|
|
//
|
|
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
|
template <class I>
|
|
HRESULT CComTableMarshalPtr<I>::RevokeInterface(DWORD dwKey)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
CComPtr<IGlobalInterfaceTable> spGIT(CMarsGlobalsManager::GIT());
|
|
|
|
ATLASSERT(spGIT);
|
|
|
|
if (spGIT)
|
|
{
|
|
hr = spGIT->RevokeInterfaceFromGlobal(dwKey);
|
|
}
|
|
|
|
return hr;
|
|
} // RevokeInterface
|
|
|
|
|
|
typedef CComEnum<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT, _Copy<VARIANT> > CComVariantEnum;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// CMarsComEnumVariant - Simple derivation for convenience. This is the standard
|
|
// type of a CComEnum for use in a collection that returns IEnumVARIANT.
|
|
//
|
|
// Our helper class has some luxury features that completely handle the common
|
|
// case of take a CMarsSimpleArray full of "stuff," stuffing those things into
|
|
// an array of CComVariant's (so we can be type-agnostic about what the
|
|
// "things" are), and then creates a CComEnum and puts it in an out-param.
|
|
//
|
|
template <class I>
|
|
class CMarsComEnumVariant :
|
|
public CComEnum<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT, _Copy<VARIANT> >
|
|
{
|
|
public:
|
|
static HRESULT CreateFromMarsSimpleArray(CMarsSimpleArray<CComClassPtr< I > > &arr, IUnknown **ppUnk)
|
|
{
|
|
// Internal API, so rip if the out param is bad
|
|
ATLASSERT(NULL != ppUnk);
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
// Create one of ourselves...
|
|
CComObject<CMarsComEnumVariant<CMarsSimpleArray< CComClassPtr< I > > > > *pEnum = NULL;
|
|
|
|
hr = CComObject<CMarsComEnumVariant<CMarsSimpleArray< CComClassPtr< I > > > >::CreateInstance(&pEnum);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Allocation must have succeeded if no error HRESULT
|
|
ATLASSERT(pEnum);
|
|
pEnum->AddRef();
|
|
|
|
VARIANT *rgVar = new VARIANT[arr.GetSize()];
|
|
LONG idxEntry;
|
|
|
|
if (rgVar)
|
|
{
|
|
HRESULT hrTestForDispatch = E_FAIL;
|
|
|
|
for (idxEntry = 0; idxEntry < arr.GetSize(); idxEntry++)
|
|
{
|
|
CComClassPtr<I> spElt;
|
|
|
|
spElt = arr.operator[](idxEntry);
|
|
|
|
hrTestForDispatch = spElt->QueryInterface(IID_IDispatch, (void **)&V_DISPATCH(&rgVar[idxEntry]));
|
|
|
|
//
|
|
// This had better succeed: the type I must be a dispatch interface, because otherwise,
|
|
// you can't pass this object to script anyway, so what's the point of the collection???
|
|
// If you really want an IEnumXXXX for your non-automation interfaces, use ATL's CComEnum
|
|
// directly with appropriate template parameters.
|
|
//
|
|
ATLASSERT(SUCCEEDED(hrTestForDispatch));
|
|
|
|
V_VT(&rgVar[idxEntry]) = VT_DISPATCH;
|
|
}
|
|
|
|
// If this succeeds, then ATL will have taken care of freeing our array for
|
|
// us. How nice of it.
|
|
//
|
|
hr = pEnum->Init(&rgVar[0], &rgVar[arr.GetSize()], NULL, AtlFlagTakeOwnership);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pEnum->QueryInterface(IID_IUnknown, (void **)ppUnk);
|
|
}
|
|
else
|
|
{
|
|
for(idxEntry = 0; idxEntry < arr.GetSize(); idxEntry++)
|
|
{
|
|
VariantClear(&rgVar[idxEntry]);
|
|
}
|
|
delete[] rgVar;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
pEnum->Release();
|
|
} // If CreateInstance SUCCEEDED
|
|
|
|
return hr;
|
|
}
|
|
};
|
|
|
|
|
|
|