715 lines
18 KiB
C++
715 lines
18 KiB
C++
// 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; // <20><> 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;
|
||
}
|