|
|
// Copyright (c) 1999 Microsoft Corporation. All rights reserved.
//
// Helper utilities for implementing automation interfaces.
//
#include "stdinc.h"
#include "authelper.h"
#include "oleaut.h"
//////////////////////////////////////////////////////////////////////
// CAutUnknown
CAutUnknown::CAutUnknown() : m_cRef(0), m_pParent(NULL), m_pDispatch(NULL) { }
void CAutUnknown::Init(CAutUnknownParent *pParent, IDispatch *pDispatch) { m_pParent = pParent; m_pDispatch = pDispatch;
struct LocalFn { static HRESULT CheckParams(CAutUnknownParent *pParent, IDispatch *pDispatch) { V_INAME(CAutUnknown::CAutUnknown); V_PTR_READ(pParent, CAutUnknown::CAutUnknownParent); V_INTERFACE(pDispatch); return S_OK; } }; assert(S_OK == LocalFn::CheckParams(m_pParent, m_pDispatch)); }
STDMETHODIMP CAutUnknown::QueryInterface(const IID &iid, void **ppv) { V_INAME(CAutUnknown::QueryInterface); V_PTRPTR_WRITE(ppv); V_REFGUID(iid);
*ppv = NULL; if (iid == IID_IUnknown) { *ppv = this; } else if (iid == IID_IDispatch) { if (!m_pDispatch) return E_UNEXPECTED; *ppv = m_pDispatch; }
if (*ppv == NULL) return E_NOINTERFACE; reinterpret_cast<IUnknown*>(*ppv)->AddRef(); return S_OK; }
STDMETHODIMP_(ULONG) CAutUnknown::AddRef() { return InterlockedIncrement(&m_cRef); }
STDMETHODIMP_(ULONG) CAutUnknown::Release() { if (!InterlockedDecrement(&m_cRef) && m_pParent) { m_pParent->Destroy(); return 0; }
return m_cRef; }
//////////////////////////////////////////////////////////////////////
// IDispatch implemented from type table
HRESULT AutDispatchGetIDsOfNames( const AutDispatchMethod *pMethods, REFIID riid, LPOLESTR __RPC_FAR *rgszNames, UINT cNames, LCID lcid, DISPID __RPC_FAR *rgDispId) { V_INAME(AutDispatchGetIDsOfNames); V_PTR_READ(pMethods, AutDispatchMethod); // only 1 -- assume the rest are OK
V_BUFPTR_READ(rgszNames, sizeof(LPOLESTR) * cNames); V_BUFPTR_WRITE(rgDispId, sizeof(DISPID) * cNames);
if (riid != IID_NULL) return DISP_E_UNKNOWNINTERFACE;
if (cNames == 0) return S_OK;
// Clear out dispid's
for (UINT c = 0; c < cNames; ++c) { rgDispId[c] = DISPID_UNKNOWN; }
// See if we have a method with the first name
for (c = 0; pMethods[c].dispid != DISPID_UNKNOWN; ++c) { if (0 == _wcsicmp(rgszNames[0], pMethods[c].pwszName)) { rgDispId[0] = pMethods[c].dispid; break; } }
// Additional names requested (cNames > 1) are named parameters to the method,
// which isn't something we support.
// Return DISP_E_UNKNOWNNAME in this case, and in the case that we didn't match
// the first name.
if (rgDispId[0] == DISPID_UNKNOWN || cNames > 1) return DISP_E_UNKNOWNNAME;
return S_OK; }
inline HRESULT ConvertParameter( bool fUseOleAut, VARIANTARG *pvarActualParam, // pass null if param omitted
const AutDispatchParam *pExpectedParam, AutDispatchDecodedParam *pparam) { HRESULT hr = S_OK;
if (!pvarActualParam) { // parameter omitted
if (!pExpectedParam->fOptional) return DISP_E_PARAMNOTOPTIONAL;
// set to default value
switch (pExpectedParam->adt) { case ADT_Long: pparam->lVal = 0; break; case ADT_Interface: pparam->iVal = NULL; break; case ADT_Bstr: pparam->bstrVal = NULL; break; default: assert(false); return E_FAIL; } } else { // convert to expected type
VARIANT varConvert; DMS_VariantInit(fUseOleAut, &varConvert);
VARTYPE vtExpected; switch (pExpectedParam->adt) { case ADT_Long: vtExpected = VT_I4; break; case ADT_Interface: vtExpected = VT_UNKNOWN; break; case ADT_Bstr: vtExpected = VT_BSTR; break; default: assert(false); return E_FAIL; }
hr = DMS_VariantChangeType( fUseOleAut, &varConvert, pvarActualParam, 0, vtExpected); if (FAILED(hr) && !(hr == DISP_E_OVERFLOW || hr == DISP_E_TYPEMISMATCH)) { assert(false); // something weird happened -- according to the OLE specs these are the only two conversion results we should get if we called VariantChangeType properly
hr = DISP_E_TYPEMISMATCH; // the problem happened during type conversion problem, so call it a type mismatch
} if (SUCCEEDED(hr)) { // set the decoded pointer
switch (vtExpected) { case VT_I4: pparam->lVal = varConvert.lVal; break; case VT_UNKNOWN: if (varConvert.punkVal) hr = varConvert.punkVal->QueryInterface(*pExpectedParam->piid, &pparam->iVal); else pparam->iVal = 0; if (FAILED(hr)) hr = DISP_E_TYPEMISMATCH; break; case VT_BSTR: pparam->bstrVal = DMS_SysAllocString(fUseOleAut, varConvert.bstrVal); break; default: assert(false); return E_FAIL; } } DMS_VariantClear(fUseOleAut, &varConvert); // free possible resources allocated in conversion
}
return hr; }
inline void FreeParameters( bool fUseOleAut, const AutDispatchMethod *pMethod, AutDispatchDecodedParams *pDecodedParams, const AutDispatchParam *pParamStopBefore = NULL) { for (const AutDispatchParam *pParam = pMethod->rgadpParams; pParam != pParamStopBefore; ++pParam) { switch (pParam->adt) { case ADT_None: return; case ADT_Long: break; case ADT_Interface: { IUnknown *pUnknown = reinterpret_cast<IUnknown *>(pDecodedParams->params[pParam - pMethod->rgadpParams].iVal); SafeRelease(pUnknown); pDecodedParams->params[pParam - pMethod->rgadpParams].iVal = NULL; break; } case ADT_Bstr: { DMS_SysFreeString(fUseOleAut, pDecodedParams->params[pParam - pMethod->rgadpParams].bstrVal); pDecodedParams->params[pParam - pMethod->rgadpParams].bstrVal = NULL; break; } default: assert(false); return; } } }
HRESULT AutDispatchInvokeDecode( const AutDispatchMethod *pMethods, AutDispatchDecodedParams *pDecodedParams, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, UINT __RPC_FAR *puArgErr, const WCHAR *pwszTraceTargetType, IUnknown *punkTraceTargetObject) { V_INAME(AutDispatchInvokeDecode); V_PTR_READ(pMethods, AutDispatchMethod); // only 1 -- assume the rest are OK
V_PTR_WRITE(pDecodedParams, AutDispatchDecodedParams); V_PTR_READ(pDispParams, DISPPARAMS); V_PTR_WRITE_OPT(pVarResult, VARIANT); V_PTR_WRITE_OPT(puArgErr, UINT);
bool fUseOleAut = !!(riid == IID_NULL);
// Additional parameter validation
if (!fUseOleAut && riid != g_guidInvokeWithoutOleaut) return DISP_E_UNKNOWNINTERFACE;
if (!(wFlags & DISPATCH_METHOD)) return DISP_E_MEMBERNOTFOUND;
if (pDispParams->cNamedArgs > 0) return DISP_E_NONAMEDARGS;
// Zero the out params
if (puArgErr) *puArgErr = 0;
ZeroMemory(pDecodedParams, sizeof(AutDispatchDecodedParams));
if (pVarResult) { DMS_VariantInit(fUseOleAut, pVarResult); }
// Find the method
for (const AutDispatchMethod *pMethodCalled = pMethods; pMethodCalled->dispid != DISPID_UNKNOWN && pMethodCalled->dispid != dispIdMember; ++pMethodCalled) { }
if (pMethodCalled->dispid == DISPID_UNKNOWN) return DISP_E_MEMBERNOTFOUND;
#ifdef DBG
// Build a trace string for the method call
struct LocalTraceFunc { static void CatTill(WCHAR *&rpwszWrite, const WCHAR *pwszCopy, const WCHAR *pwszUntil) { while (*pwszCopy != L'\0' && rpwszWrite < pwszUntil) { *rpwszWrite++ = *pwszCopy++; } } };
WCHAR wszBuf[512]; WCHAR *pwszWrite = wszBuf; const WCHAR *pwszUntil = wszBuf + ARRAY_SIZE(wszBuf) - 2; // leave space for CR and \0
LocalTraceFunc::CatTill(pwszWrite, L"Call to ", pwszUntil); LocalTraceFunc::CatTill(pwszWrite, pwszTraceTargetType, pwszUntil);
IDirectMusicObject *pIDMO = NULL; HRESULT hrTrace = punkTraceTargetObject->QueryInterface(IID_IDirectMusicObject, reinterpret_cast<void**>(&pIDMO)); if (SUCCEEDED(hrTrace)) { DMUS_OBJECTDESC objdesc; ZeroMemory(&objdesc, sizeof(objdesc)); hrTrace = pIDMO->GetDescriptor(&objdesc); pIDMO->Release(); if (SUCCEEDED(hrTrace) && (objdesc.dwValidData & DMUS_OBJ_NAME)) { LocalTraceFunc::CatTill(pwszWrite, L" \"", pwszUntil); LocalTraceFunc::CatTill(pwszWrite, objdesc.wszName, pwszUntil); LocalTraceFunc::CatTill(pwszWrite, L"\"", pwszUntil); } }
LocalTraceFunc::CatTill(pwszWrite, L" ", pwszUntil); LocalTraceFunc::CatTill(pwszWrite, pMethodCalled->pwszName, pwszUntil); LocalTraceFunc::CatTill(pwszWrite, L"(", pwszUntil); #endif
// Count the expected parameters
UINT cParamMin = 0; for (UINT cParamMax = 0; pMethodCalled->rgadpParams[cParamMax].adt != ADT_None; ++cParamMax) { if (!pMethodCalled->rgadpParams[cParamMax].fOptional) { cParamMin = cParamMax + 1; // add one because max is currently zero-based
} }
if (pDispParams->cArgs < cParamMin || pDispParams->cArgs > cParamMax) return DISP_E_BADPARAMCOUNT;
// Verify and prepare each parameter
HRESULT hr = S_OK; for (UINT iParam = 0; iParam < cParamMax; ++iParam) { const int iParamActual = pDispParams->cArgs - iParam - 1; // dispparams are passed last to first
const AutDispatchParam *pExpectedParam = &pMethodCalled->rgadpParams[iParam]; VARIANTARG *pvarActualParam = (iParamActual >= 0) ? &pDispParams->rgvarg[iParamActual] : NULL; // VT_ERROR with DISP_E_PARAMNOTFOUND is passed as placeholder for optional params
if (pvarActualParam && pvarActualParam->vt == VT_ERROR && pvarActualParam->scode == DISP_E_PARAMNOTFOUND) pvarActualParam = NULL;
hr = ConvertParameter(fUseOleAut, pvarActualParam, pExpectedParam, &pDecodedParams->params[iParam]);
if (FAILED(hr)) { if (puArgErr) *puArgErr = iParamActual; FreeParameters(fUseOleAut, pMethodCalled, pDecodedParams, pExpectedParam); return hr; } }
// Prepare the return value
if (pVarResult) { switch (pMethodCalled->adpReturn.adt) { case ADT_None: break;
case ADT_Long: pVarResult->vt = VT_I4; pVarResult->lVal = 0; pDecodedParams->pvReturn = &pVarResult->lVal; break;
case ADT_Interface: pVarResult->vt = VT_UNKNOWN; pVarResult->punkVal = NULL; pDecodedParams->pvReturn = &pVarResult->punkVal; break;
case ADT_Bstr: pVarResult->vt = VT_BSTR; pVarResult->bstrVal = NULL; pDecodedParams->pvReturn = &pVarResult->bstrVal;
default: assert(false); return E_FAIL; } }
#ifdef DBG
LocalTraceFunc::CatTill(pwszWrite, L")", pwszUntil); pwszWrite[0] = L'\n'; pwszWrite[1] = L'\0'; DebugTrace(g_ScriptCallTraceLevel, "%S", wszBuf); #endif
return S_OK; }
void AutDispatchInvokeFree( const AutDispatchMethod *pMethods, AutDispatchDecodedParams *pDecodedParams, DISPID dispIdMember, REFIID riid) { bool fUseOleAut = !!(riid == IID_NULL); if (!fUseOleAut && riid != g_guidInvokeWithoutOleaut) { assert(false); return; }
// Find the method
for (const AutDispatchMethod *pMethodCalled = pMethods; pMethodCalled->dispid != DISPID_UNKNOWN && pMethodCalled->dispid != dispIdMember; ++pMethodCalled) { }
if (pMethodCalled->dispid != DISPID_UNKNOWN) { FreeParameters(fUseOleAut, pMethodCalled, pDecodedParams); } }
HRESULT AutDispatchHrToException( const AutDispatchMethod *pMethods, DISPID dispIdMember, REFIID riid, HRESULT hr, EXCEPINFO __RPC_FAR *pExcepInfo) { V_INAME(AutDispatchHrToException); V_PTR_WRITE_OPT(pExcepInfo, EXCEPINFO);
bool fUseOleAut = !!(riid == IID_NULL);
if (!fUseOleAut && riid != g_guidInvokeWithoutOleaut) return DISP_E_UNKNOWNINTERFACE;
if (SUCCEEDED(hr)) return hr;
if (!pExcepInfo) return DISP_E_EXCEPTION;
// Find the method
for (const AutDispatchMethod *pMethodCalled = pMethods; pMethodCalled->dispid != DISPID_UNKNOWN && pMethodCalled->dispid != dispIdMember; ++pMethodCalled) { }
if (pMethodCalled->dispid == DISPID_UNKNOWN) { assert(false); return hr; }
pExcepInfo->wCode = 0; pExcepInfo->wReserved = 0; pExcepInfo->bstrSource = DMS_SysAllocString(fUseOleAut, L"Microsoft DirectMusic Runtime Error"); static const WCHAR wszError[] = L"An error occurred in a call to "; static const UINT cchError = wcslen(wszError); WCHAR *pwszDescription = new WCHAR[cchError + wcslen(pMethodCalled->pwszName) + 1]; if (!pwszDescription) { pExcepInfo->bstrDescription = NULL; } else { wcscpy(pwszDescription, wszError); wcscat(pwszDescription, pMethodCalled->pwszName); pExcepInfo->bstrDescription = DMS_SysAllocString(fUseOleAut, pwszDescription); delete[] pwszDescription; } pExcepInfo->bstrHelpFile = NULL; pExcepInfo->pvReserved = NULL; pExcepInfo->pfnDeferredFillIn = NULL; pExcepInfo->scode = hr;
return DISP_E_EXCEPTION; }
//////////////////////////////////////////////////////////////////////
// Implementation of IDispatch for the standard Load method on objects.
HRESULT AutLoadDispatchGetIDsOfNames( REFIID riid, LPOLESTR __RPC_FAR *rgszNames, UINT cNames, LCID lcid, DISPID __RPC_FAR *rgDispId) { V_INAME(AutLoadDispatchGetIDsOfNames); V_BUFPTR_READ(rgszNames, sizeof(LPOLESTR) * cNames); V_BUFPTR_WRITE(rgDispId, sizeof(DISPID) * cNames);
if (riid != IID_NULL) return DISP_E_UNKNOWNINTERFACE;
if (cNames == 0) return S_OK;
// Clear out dispid's
for (UINT c = 0; c < cNames; ++c) { rgDispId[c] = DISPID_UNKNOWN; }
// See if we have a method with the first name
if (0 == _wcsicmp(rgszNames[0], L"Load")) rgDispId[0] = g_dispidLoad;
// Additional names requested (cNames > 1) are named parameters to the method,
// which isn't something we support.
// Return DISP_E_UNKNOWNNAME in this case, and in the case that we didn't match
// the first name.
if (rgDispId[0] == DISPID_UNKNOWN || cNames > 1) return DISP_E_UNKNOWNNAME;
return S_OK; }
HRESULT AutLoadDispatchInvoke( bool *pfUseOleAut, DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr) { V_INAME(AutLoadDispatchInvoke); V_PTR_READ(pDispParams, DISPPARAMS); V_PTR_WRITE_OPT(pVarResult, VARIANT); V_PTR_WRITE_OPT(puArgErr, UINT); V_PTR_WRITE_OPT(pExcepInfo, EXCEPINFO);
bool fUseOleAut = !!(riid == IID_NULL); if (pfUseOleAut) *pfUseOleAut = fUseOleAut;
// Additional parameter validation
if (!fUseOleAut && riid != g_guidInvokeWithoutOleaut) return DISP_E_UNKNOWNINTERFACE;
if (!(wFlags & DISPATCH_METHOD)) return DISP_E_MEMBERNOTFOUND;
if (pDispParams->cNamedArgs > 0) return DISP_E_NONAMEDARGS;
// Zero the out params
if (puArgErr) *puArgErr = 0;
if (pVarResult) { DMS_VariantInit(fUseOleAut, pVarResult); }
// Find the method
if (dispIdMember != g_dispidLoad) return DISP_E_MEMBERNOTFOUND;
if (pDispParams->cArgs > 0) return DISP_E_BADPARAMCOUNT;
return S_OK; }
//////////////////////////////////////////////////////////////////////
// Miscellaneous little things
DWORD MapFlags(LONG lFlags, const FlagMapEntry *pfm) { assert(pfm); DWORD dw = 0; for ( ; pfm->lSrc; ++pfm) { if (lFlags & pfm->lSrc) dw |= pfm->dwDest; } return dw; }
BYTE VolumeToMidi(LONG lVolume) { assert(lVolume >= -9600 && lVolume <= 0); static LONG s_lDBToMIDI[97] = { 0 }; if (s_lDBToMIDI[0] == 0) { s_lDBToMIDI[0] = 127; for (int nIndex = 1; nIndex < 97; nIndex++) { double flTemp = 0.0 - nIndex; flTemp /= 10.0; flTemp = pow(10,flTemp); flTemp = sqrt(flTemp); flTemp = sqrt(flTemp); flTemp *= 127.0; s_lDBToMIDI[nIndex] = flTemp; } }
lVolume = -lVolume; long lFraction = lVolume % 100; lVolume = lVolume / 100; long lResult = s_lDBToMIDI[lVolume]; lResult += ((s_lDBToMIDI[lVolume + 1] - lResult) * lFraction) / 100; assert(lResult >= std::numeric_limits<BYTE>::min() && lResult <= std::numeric_limits<BYTE>::max()); return lResult; }
HRESULT SendVolumePMsg(LONG lVolume, LONG lDuration, DWORD dwPChannel, IDirectMusicGraph *pGraph, IDirectMusicPerformance *pPerf, short *pnNewVolume) { assert(pGraph && pPerf && pnNewVolume); lVolume = ClipLongRange(lVolume, -9600, 0); BYTE bMIDIVol = VolumeToMidi(lVolume);
SmartRef::PMsg<DMUS_CURVE_PMSG> pmsgCurve(pPerf); HRESULT hr = pmsgCurve.hr(); if (FAILED(hr)) return hr;
// generic PMsg fields
REFERENCE_TIME rtTimeNow = 0; hr = pPerf->GetLatencyTime(&rtTimeNow); if (FAILED(hr)) return hr; pmsgCurve.p->rtTime = rtTimeNow; pmsgCurve.p->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME | DMUS_PMSGF_DX8; pmsgCurve.p->dwPChannel = dwPChannel; // dwVirtualTrackID: this isn't a track so leave as 0
pmsgCurve.p->dwType = DMUS_PMSGT_CURVE; pmsgCurve.p->dwGroupID = -1; // this isn't a track so just say all groups
// curve PMsg fields
pmsgCurve.p->mtDuration = lDuration; // setting the DMUS_PMSGF_LOCKTOREFTIME is interpreted by the performance that mtDuration is milliseconds
// mtResetDuration: no reset so leave as 0
// nStartValue: will be ignored
pmsgCurve.p->nEndValue = bMIDIVol; // nResetValue: no reset so leave as 0
pmsgCurve.p->bType = DMUS_CURVET_CCCURVE; pmsgCurve.p->bCurveShape = lDuration ? DMUS_CURVES_LINEAR : DMUS_CURVES_INSTANT; pmsgCurve.p->bCCData = 7; // MIDI volume controller number
pmsgCurve.p->bFlags = DMUS_CURVE_START_FROM_CURRENT; // wParamType: leave as zero since this isn't a NRPN/RPN curve
pmsgCurve.p->wMergeIndex = 0xFFFF; // �� special merge index so this won't get stepped on. is a big number OK? define a constant for this value?
// send it
pmsgCurve.StampAndSend(pGraph); hr = pmsgCurve.hr(); if (FAILED(hr)) return hr;
*pnNewVolume = lVolume; return S_OK; }
|