Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

714 lines
17 KiB

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