Leaked source code of windows server 2003
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
18 KiB

  1. // Copyright (c) 1999 Microsoft Corporation. All rights reserved.
  2. //
  3. // Helper utilities for implementing automation interfaces.
  4. //
  5. #include "stdinc.h"
  6. #include "authelper.h"
  7. #include "oleaut.h"
  8. //////////////////////////////////////////////////////////////////////
  9. // CAutUnknown
  10. CAutUnknown::CAutUnknown()
  11. : m_cRef(0),
  12. m_pParent(NULL),
  13. m_pDispatch(NULL)
  14. {
  15. }
  16. void
  17. CAutUnknown::Init(CAutUnknownParent *pParent, IDispatch *pDispatch)
  18. {
  19. m_pParent = pParent;
  20. m_pDispatch = pDispatch;
  21. struct LocalFn
  22. {
  23. static HRESULT CheckParams(CAutUnknownParent *pParent, IDispatch *pDispatch)
  24. {
  25. V_INAME(CAutUnknown::CAutUnknown);
  26. V_PTR_READ(pParent, CAutUnknown::CAutUnknownParent);
  27. V_INTERFACE(pDispatch);
  28. return S_OK;
  29. }
  30. };
  31. assert(S_OK == LocalFn::CheckParams(m_pParent, m_pDispatch));
  32. }
  33. STDMETHODIMP
  34. CAutUnknown::QueryInterface(const IID &iid, void **ppv)
  35. {
  36. V_INAME(CAutUnknown::QueryInterface);
  37. V_PTRPTR_WRITE(ppv);
  38. V_REFGUID(iid);
  39. *ppv = NULL;
  40. if (iid == IID_IUnknown)
  41. {
  42. *ppv = this;
  43. }
  44. else if (iid == IID_IDispatch)
  45. {
  46. if (!m_pDispatch)
  47. return E_UNEXPECTED;
  48. *ppv = m_pDispatch;
  49. }
  50. if (*ppv == NULL)
  51. return E_NOINTERFACE;
  52. reinterpret_cast<IUnknown*>(*ppv)->AddRef();
  53. return S_OK;
  54. }
  55. STDMETHODIMP_(ULONG)
  56. CAutUnknown::AddRef()
  57. {
  58. return InterlockedIncrement(&m_cRef);
  59. }
  60. STDMETHODIMP_(ULONG)
  61. CAutUnknown::Release()
  62. {
  63. if (!InterlockedDecrement(&m_cRef) && m_pParent)
  64. {
  65. m_pParent->Destroy();
  66. return 0;
  67. }
  68. return m_cRef;
  69. }
  70. //////////////////////////////////////////////////////////////////////
  71. // IDispatch implemented from type table
  72. HRESULT
  73. AutDispatchGetIDsOfNames(
  74. const AutDispatchMethod *pMethods,
  75. REFIID riid,
  76. LPOLESTR __RPC_FAR *rgszNames,
  77. UINT cNames,
  78. LCID lcid,
  79. DISPID __RPC_FAR *rgDispId)
  80. {
  81. V_INAME(AutDispatchGetIDsOfNames);
  82. V_PTR_READ(pMethods, AutDispatchMethod); // only 1 -- assume the rest are OK
  83. V_BUFPTR_READ(rgszNames, sizeof(LPOLESTR) * cNames);
  84. V_BUFPTR_WRITE(rgDispId, sizeof(DISPID) * cNames);
  85. if (riid != IID_NULL)
  86. return DISP_E_UNKNOWNINTERFACE;
  87. if (cNames == 0)
  88. return S_OK;
  89. // Clear out dispid's
  90. for (UINT c = 0; c < cNames; ++c)
  91. {
  92. rgDispId[c] = DISPID_UNKNOWN;
  93. }
  94. // See if we have a method with the first name
  95. for (c = 0; pMethods[c].dispid != DISPID_UNKNOWN; ++c)
  96. {
  97. if (0 == _wcsicmp(rgszNames[0], pMethods[c].pwszName))
  98. {
  99. rgDispId[0] = pMethods[c].dispid;
  100. break;
  101. }
  102. }
  103. // Additional names requested (cNames > 1) are named parameters to the method,
  104. // which isn't something we support.
  105. // Return DISP_E_UNKNOWNNAME in this case, and in the case that we didn't match
  106. // the first name.
  107. if (rgDispId[0] == DISPID_UNKNOWN || cNames > 1)
  108. return DISP_E_UNKNOWNNAME;
  109. return S_OK;
  110. }
  111. inline HRESULT
  112. ConvertParameter(
  113. bool fUseOleAut,
  114. VARIANTARG *pvarActualParam, // pass null if param omitted
  115. const AutDispatchParam *pExpectedParam,
  116. AutDispatchDecodedParam *pparam)
  117. {
  118. HRESULT hr = S_OK;
  119. if (!pvarActualParam)
  120. {
  121. // parameter omitted
  122. if (!pExpectedParam->fOptional)
  123. return DISP_E_PARAMNOTOPTIONAL;
  124. // set to default value
  125. switch (pExpectedParam->adt)
  126. {
  127. case ADT_Long:
  128. pparam->lVal = 0;
  129. break;
  130. case ADT_Interface:
  131. pparam->iVal = NULL;
  132. break;
  133. case ADT_Bstr:
  134. pparam->bstrVal = NULL;
  135. break;
  136. default:
  137. assert(false);
  138. return E_FAIL;
  139. }
  140. }
  141. else
  142. {
  143. // convert to expected type
  144. VARIANT varConvert;
  145. DMS_VariantInit(fUseOleAut, &varConvert);
  146. VARTYPE vtExpected;
  147. switch (pExpectedParam->adt)
  148. {
  149. case ADT_Long:
  150. vtExpected = VT_I4;
  151. break;
  152. case ADT_Interface:
  153. vtExpected = VT_UNKNOWN;
  154. break;
  155. case ADT_Bstr:
  156. vtExpected = VT_BSTR;
  157. break;
  158. default:
  159. assert(false);
  160. return E_FAIL;
  161. }
  162. hr = DMS_VariantChangeType(
  163. fUseOleAut,
  164. &varConvert,
  165. pvarActualParam,
  166. 0,
  167. vtExpected);
  168. if (FAILED(hr) && !(hr == DISP_E_OVERFLOW || hr == DISP_E_TYPEMISMATCH))
  169. {
  170. 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
  171. hr = DISP_E_TYPEMISMATCH; // the problem happened during type conversion problem, so call it a type mismatch
  172. }
  173. if (SUCCEEDED(hr))
  174. {
  175. // set the decoded pointer
  176. switch (vtExpected)
  177. {
  178. case VT_I4:
  179. pparam->lVal = varConvert.lVal;
  180. break;
  181. case VT_UNKNOWN:
  182. if (varConvert.punkVal)
  183. hr = varConvert.punkVal->QueryInterface(*pExpectedParam->piid, &pparam->iVal);
  184. else
  185. pparam->iVal = 0;
  186. if (FAILED(hr))
  187. hr = DISP_E_TYPEMISMATCH;
  188. break;
  189. case VT_BSTR:
  190. pparam->bstrVal = DMS_SysAllocString(fUseOleAut, varConvert.bstrVal);
  191. break;
  192. default:
  193. assert(false);
  194. return E_FAIL;
  195. }
  196. }
  197. DMS_VariantClear(fUseOleAut, &varConvert); // free possible resources allocated in conversion
  198. }
  199. return hr;
  200. }
  201. inline void
  202. FreeParameters(
  203. bool fUseOleAut,
  204. const AutDispatchMethod *pMethod,
  205. AutDispatchDecodedParams *pDecodedParams,
  206. const AutDispatchParam *pParamStopBefore = NULL)
  207. {
  208. for (const AutDispatchParam *pParam = pMethod->rgadpParams;
  209. pParam != pParamStopBefore;
  210. ++pParam)
  211. {
  212. switch (pParam->adt)
  213. {
  214. case ADT_None:
  215. return;
  216. case ADT_Long:
  217. break;
  218. case ADT_Interface:
  219. {
  220. IUnknown *pUnknown = reinterpret_cast<IUnknown *>(pDecodedParams->params[pParam - pMethod->rgadpParams].iVal);
  221. SafeRelease(pUnknown);
  222. pDecodedParams->params[pParam - pMethod->rgadpParams].iVal = NULL;
  223. break;
  224. }
  225. case ADT_Bstr:
  226. {
  227. DMS_SysFreeString(fUseOleAut, pDecodedParams->params[pParam - pMethod->rgadpParams].bstrVal);
  228. pDecodedParams->params[pParam - pMethod->rgadpParams].bstrVal = NULL;
  229. break;
  230. }
  231. default:
  232. assert(false);
  233. return;
  234. }
  235. }
  236. }
  237. HRESULT
  238. AutDispatchInvokeDecode(
  239. const AutDispatchMethod *pMethods,
  240. AutDispatchDecodedParams *pDecodedParams,
  241. DISPID dispIdMember,
  242. REFIID riid,
  243. LCID lcid,
  244. WORD wFlags,
  245. DISPPARAMS __RPC_FAR *pDispParams,
  246. VARIANT __RPC_FAR *pVarResult,
  247. UINT __RPC_FAR *puArgErr,
  248. const WCHAR *pwszTraceTargetType,
  249. IUnknown *punkTraceTargetObject)
  250. {
  251. V_INAME(AutDispatchInvokeDecode);
  252. V_PTR_READ(pMethods, AutDispatchMethod); // only 1 -- assume the rest are OK
  253. V_PTR_WRITE(pDecodedParams, AutDispatchDecodedParams);
  254. V_PTR_READ(pDispParams, DISPPARAMS);
  255. V_PTR_WRITE_OPT(pVarResult, VARIANT);
  256. V_PTR_WRITE_OPT(puArgErr, UINT);
  257. bool fUseOleAut = !!(riid == IID_NULL);
  258. // Additional parameter validation
  259. if (!fUseOleAut && riid != g_guidInvokeWithoutOleaut)
  260. return DISP_E_UNKNOWNINTERFACE;
  261. if (!(wFlags & DISPATCH_METHOD))
  262. return DISP_E_MEMBERNOTFOUND;
  263. if (pDispParams->cNamedArgs > 0)
  264. return DISP_E_NONAMEDARGS;
  265. // Zero the out params
  266. if (puArgErr)
  267. *puArgErr = 0;
  268. ZeroMemory(pDecodedParams, sizeof(AutDispatchDecodedParams));
  269. if (pVarResult)
  270. {
  271. DMS_VariantInit(fUseOleAut, pVarResult);
  272. }
  273. // Find the method
  274. for (const AutDispatchMethod *pMethodCalled = pMethods;
  275. pMethodCalled->dispid != DISPID_UNKNOWN && pMethodCalled->dispid != dispIdMember;
  276. ++pMethodCalled)
  277. {
  278. }
  279. if (pMethodCalled->dispid == DISPID_UNKNOWN)
  280. return DISP_E_MEMBERNOTFOUND;
  281. #ifdef DBG
  282. // Build a trace string for the method call
  283. struct LocalTraceFunc
  284. {
  285. static void CatTill(WCHAR *&rpwszWrite, const WCHAR *pwszCopy, const WCHAR *pwszUntil)
  286. {
  287. while (*pwszCopy != L'\0' && rpwszWrite < pwszUntil)
  288. {
  289. *rpwszWrite++ = *pwszCopy++;
  290. }
  291. }
  292. };
  293. WCHAR wszBuf[512];
  294. WCHAR *pwszWrite = wszBuf;
  295. const WCHAR *pwszUntil = wszBuf + ARRAY_SIZE(wszBuf) - 2; // leave space for CR and \0
  296. LocalTraceFunc::CatTill(pwszWrite, L"Call to ", pwszUntil);
  297. LocalTraceFunc::CatTill(pwszWrite, pwszTraceTargetType, pwszUntil);
  298. IDirectMusicObject *pIDMO = NULL;
  299. HRESULT hrTrace = punkTraceTargetObject->QueryInterface(IID_IDirectMusicObject, reinterpret_cast<void**>(&pIDMO));
  300. if (SUCCEEDED(hrTrace))
  301. {
  302. DMUS_OBJECTDESC objdesc;
  303. ZeroMemory(&objdesc, sizeof(objdesc));
  304. hrTrace = pIDMO->GetDescriptor(&objdesc);
  305. pIDMO->Release();
  306. if (SUCCEEDED(hrTrace) && (objdesc.dwValidData & DMUS_OBJ_NAME))
  307. {
  308. LocalTraceFunc::CatTill(pwszWrite, L" \"", pwszUntil);
  309. LocalTraceFunc::CatTill(pwszWrite, objdesc.wszName, pwszUntil);
  310. LocalTraceFunc::CatTill(pwszWrite, L"\"", pwszUntil);
  311. }
  312. }
  313. LocalTraceFunc::CatTill(pwszWrite, L" ", pwszUntil);
  314. LocalTraceFunc::CatTill(pwszWrite, pMethodCalled->pwszName, pwszUntil);
  315. LocalTraceFunc::CatTill(pwszWrite, L"(", pwszUntil);
  316. #endif
  317. // Count the expected parameters
  318. UINT cParamMin = 0;
  319. for (UINT cParamMax = 0;
  320. pMethodCalled->rgadpParams[cParamMax].adt != ADT_None;
  321. ++cParamMax)
  322. {
  323. if (!pMethodCalled->rgadpParams[cParamMax].fOptional)
  324. {
  325. cParamMin = cParamMax + 1; // add one because max is currently zero-based
  326. }
  327. }
  328. if (pDispParams->cArgs < cParamMin || pDispParams->cArgs > cParamMax)
  329. return DISP_E_BADPARAMCOUNT;
  330. // Verify and prepare each parameter
  331. HRESULT hr = S_OK;
  332. for (UINT iParam = 0; iParam < cParamMax; ++iParam)
  333. {
  334. const int iParamActual = pDispParams->cArgs - iParam - 1; // dispparams are passed last to first
  335. const AutDispatchParam *pExpectedParam = &pMethodCalled->rgadpParams[iParam];
  336. VARIANTARG *pvarActualParam = (iParamActual >= 0)
  337. ? &pDispParams->rgvarg[iParamActual]
  338. : NULL;
  339. // VT_ERROR with DISP_E_PARAMNOTFOUND is passed as placeholder for optional params
  340. if (pvarActualParam && pvarActualParam->vt == VT_ERROR && pvarActualParam->scode == DISP_E_PARAMNOTFOUND)
  341. pvarActualParam = NULL;
  342. hr = ConvertParameter(fUseOleAut, pvarActualParam, pExpectedParam, &pDecodedParams->params[iParam]);
  343. if (FAILED(hr))
  344. {
  345. if (puArgErr)
  346. *puArgErr = iParamActual;
  347. FreeParameters(fUseOleAut, pMethodCalled, pDecodedParams, pExpectedParam);
  348. return hr;
  349. }
  350. }
  351. // Prepare the return value
  352. if (pVarResult)
  353. {
  354. switch (pMethodCalled->adpReturn.adt)
  355. {
  356. case ADT_None:
  357. break;
  358. case ADT_Long:
  359. pVarResult->vt = VT_I4;
  360. pVarResult->lVal = 0;
  361. pDecodedParams->pvReturn = &pVarResult->lVal;
  362. break;
  363. case ADT_Interface:
  364. pVarResult->vt = VT_UNKNOWN;
  365. pVarResult->punkVal = NULL;
  366. pDecodedParams->pvReturn = &pVarResult->punkVal;
  367. break;
  368. case ADT_Bstr:
  369. pVarResult->vt = VT_BSTR;
  370. pVarResult->bstrVal = NULL;
  371. pDecodedParams->pvReturn = &pVarResult->bstrVal;
  372. default:
  373. assert(false);
  374. return E_FAIL;
  375. }
  376. }
  377. #ifdef DBG
  378. LocalTraceFunc::CatTill(pwszWrite, L")", pwszUntil);
  379. pwszWrite[0] = L'\n';
  380. pwszWrite[1] = L'\0';
  381. DebugTrace(g_ScriptCallTraceLevel, "%S", wszBuf);
  382. #endif
  383. return S_OK;
  384. }
  385. void
  386. AutDispatchInvokeFree(
  387. const AutDispatchMethod *pMethods,
  388. AutDispatchDecodedParams *pDecodedParams,
  389. DISPID dispIdMember,
  390. REFIID riid)
  391. {
  392. bool fUseOleAut = !!(riid == IID_NULL);
  393. if (!fUseOleAut && riid != g_guidInvokeWithoutOleaut)
  394. {
  395. assert(false);
  396. return;
  397. }
  398. // Find the method
  399. for (const AutDispatchMethod *pMethodCalled = pMethods;
  400. pMethodCalled->dispid != DISPID_UNKNOWN && pMethodCalled->dispid != dispIdMember;
  401. ++pMethodCalled)
  402. {
  403. }
  404. if (pMethodCalled->dispid != DISPID_UNKNOWN)
  405. {
  406. FreeParameters(fUseOleAut, pMethodCalled, pDecodedParams);
  407. }
  408. }
  409. HRESULT AutDispatchHrToException(
  410. const AutDispatchMethod *pMethods,
  411. DISPID dispIdMember,
  412. REFIID riid,
  413. HRESULT hr,
  414. EXCEPINFO __RPC_FAR *pExcepInfo)
  415. {
  416. V_INAME(AutDispatchHrToException);
  417. V_PTR_WRITE_OPT(pExcepInfo, EXCEPINFO);
  418. bool fUseOleAut = !!(riid == IID_NULL);
  419. if (!fUseOleAut && riid != g_guidInvokeWithoutOleaut)
  420. return DISP_E_UNKNOWNINTERFACE;
  421. if (SUCCEEDED(hr))
  422. return hr;
  423. if (!pExcepInfo)
  424. return DISP_E_EXCEPTION;
  425. // Find the method
  426. for (const AutDispatchMethod *pMethodCalled = pMethods;
  427. pMethodCalled->dispid != DISPID_UNKNOWN && pMethodCalled->dispid != dispIdMember;
  428. ++pMethodCalled)
  429. {
  430. }
  431. if (pMethodCalled->dispid == DISPID_UNKNOWN)
  432. {
  433. assert(false);
  434. return hr;
  435. }
  436. pExcepInfo->wCode = 0;
  437. pExcepInfo->wReserved = 0;
  438. pExcepInfo->bstrSource = DMS_SysAllocString(fUseOleAut, L"Microsoft DirectMusic Runtime Error");
  439. static const WCHAR wszError[] = L"An error occurred in a call to ";
  440. static const UINT cchError = wcslen(wszError);
  441. WCHAR *pwszDescription = new WCHAR[cchError + wcslen(pMethodCalled->pwszName) + 1];
  442. if (!pwszDescription)
  443. {
  444. pExcepInfo->bstrDescription = NULL;
  445. }
  446. else
  447. {
  448. wcscpy(pwszDescription, wszError);
  449. wcscat(pwszDescription, pMethodCalled->pwszName);
  450. pExcepInfo->bstrDescription = DMS_SysAllocString(fUseOleAut, pwszDescription);
  451. delete[] pwszDescription;
  452. }
  453. pExcepInfo->bstrHelpFile = NULL;
  454. pExcepInfo->pvReserved = NULL;
  455. pExcepInfo->pfnDeferredFillIn = NULL;
  456. pExcepInfo->scode = hr;
  457. return DISP_E_EXCEPTION;
  458. }
  459. //////////////////////////////////////////////////////////////////////
  460. // Implementation of IDispatch for the standard Load method on objects.
  461. HRESULT AutLoadDispatchGetIDsOfNames(
  462. REFIID riid,
  463. LPOLESTR __RPC_FAR *rgszNames,
  464. UINT cNames,
  465. LCID lcid,
  466. DISPID __RPC_FAR *rgDispId)
  467. {
  468. V_INAME(AutLoadDispatchGetIDsOfNames);
  469. V_BUFPTR_READ(rgszNames, sizeof(LPOLESTR) * cNames);
  470. V_BUFPTR_WRITE(rgDispId, sizeof(DISPID) * cNames);
  471. if (riid != IID_NULL)
  472. return DISP_E_UNKNOWNINTERFACE;
  473. if (cNames == 0)
  474. return S_OK;
  475. // Clear out dispid's
  476. for (UINT c = 0; c < cNames; ++c)
  477. {
  478. rgDispId[c] = DISPID_UNKNOWN;
  479. }
  480. // See if we have a method with the first name
  481. if (0 == _wcsicmp(rgszNames[0], L"Load"))
  482. rgDispId[0] = g_dispidLoad;
  483. // Additional names requested (cNames > 1) are named parameters to the method,
  484. // which isn't something we support.
  485. // Return DISP_E_UNKNOWNNAME in this case, and in the case that we didn't match
  486. // the first name.
  487. if (rgDispId[0] == DISPID_UNKNOWN || cNames > 1)
  488. return DISP_E_UNKNOWNNAME;
  489. return S_OK;
  490. }
  491. HRESULT AutLoadDispatchInvoke(
  492. bool *pfUseOleAut,
  493. DISPID dispIdMember,
  494. REFIID riid,
  495. LCID lcid,
  496. WORD wFlags,
  497. DISPPARAMS __RPC_FAR *pDispParams,
  498. VARIANT __RPC_FAR *pVarResult,
  499. EXCEPINFO __RPC_FAR *pExcepInfo,
  500. UINT __RPC_FAR *puArgErr)
  501. {
  502. V_INAME(AutLoadDispatchInvoke);
  503. V_PTR_READ(pDispParams, DISPPARAMS);
  504. V_PTR_WRITE_OPT(pVarResult, VARIANT);
  505. V_PTR_WRITE_OPT(puArgErr, UINT);
  506. V_PTR_WRITE_OPT(pExcepInfo, EXCEPINFO);
  507. bool fUseOleAut = !!(riid == IID_NULL);
  508. if (pfUseOleAut)
  509. *pfUseOleAut = fUseOleAut;
  510. // Additional parameter validation
  511. if (!fUseOleAut && riid != g_guidInvokeWithoutOleaut)
  512. return DISP_E_UNKNOWNINTERFACE;
  513. if (!(wFlags & DISPATCH_METHOD))
  514. return DISP_E_MEMBERNOTFOUND;
  515. if (pDispParams->cNamedArgs > 0)
  516. return DISP_E_NONAMEDARGS;
  517. // Zero the out params
  518. if (puArgErr)
  519. *puArgErr = 0;
  520. if (pVarResult)
  521. {
  522. DMS_VariantInit(fUseOleAut, pVarResult);
  523. }
  524. // Find the method
  525. if (dispIdMember != g_dispidLoad)
  526. return DISP_E_MEMBERNOTFOUND;
  527. if (pDispParams->cArgs > 0)
  528. return DISP_E_BADPARAMCOUNT;
  529. return S_OK;
  530. }
  531. //////////////////////////////////////////////////////////////////////
  532. // Miscellaneous little things
  533. DWORD MapFlags(LONG lFlags, const FlagMapEntry *pfm)
  534. {
  535. assert(pfm);
  536. DWORD dw = 0;
  537. for ( ; pfm->lSrc; ++pfm)
  538. {
  539. if (lFlags & pfm->lSrc)
  540. dw |= pfm->dwDest;
  541. }
  542. return dw;
  543. }
  544. BYTE VolumeToMidi(LONG lVolume)
  545. {
  546. assert(lVolume >= -9600 && lVolume <= 0);
  547. static LONG s_lDBToMIDI[97] = { 0 };
  548. if (s_lDBToMIDI[0] == 0)
  549. {
  550. s_lDBToMIDI[0] = 127;
  551. for (int nIndex = 1; nIndex < 97; nIndex++)
  552. {
  553. double flTemp = 0.0 - nIndex;
  554. flTemp /= 10.0;
  555. flTemp = pow(10,flTemp);
  556. flTemp = sqrt(flTemp);
  557. flTemp = sqrt(flTemp);
  558. flTemp *= 127.0;
  559. s_lDBToMIDI[nIndex] = flTemp;
  560. }
  561. }
  562. lVolume = -lVolume;
  563. long lFraction = lVolume % 100;
  564. lVolume = lVolume / 100;
  565. long lResult = s_lDBToMIDI[lVolume];
  566. lResult += ((s_lDBToMIDI[lVolume + 1] - lResult) * lFraction) / 100;
  567. assert(lResult >= std::numeric_limits<BYTE>::min() && lResult <= std::numeric_limits<BYTE>::max());
  568. return lResult;
  569. }
  570. HRESULT SendVolumePMsg(LONG lVolume, LONG lDuration, DWORD dwPChannel, IDirectMusicGraph *pGraph, IDirectMusicPerformance *pPerf, short *pnNewVolume)
  571. {
  572. assert(pGraph && pPerf && pnNewVolume);
  573. lVolume = ClipLongRange(lVolume, -9600, 0);
  574. BYTE bMIDIVol = VolumeToMidi(lVolume);
  575. SmartRef::PMsg<DMUS_CURVE_PMSG> pmsgCurve(pPerf);
  576. HRESULT hr = pmsgCurve.hr();
  577. if (FAILED(hr))
  578. return hr;
  579. // generic PMsg fields
  580. REFERENCE_TIME rtTimeNow = 0;
  581. hr = pPerf->GetLatencyTime(&rtTimeNow);
  582. if (FAILED(hr))
  583. return hr;
  584. pmsgCurve.p->rtTime = rtTimeNow;
  585. pmsgCurve.p->dwFlags = DMUS_PMSGF_REFTIME | DMUS_PMSGF_LOCKTOREFTIME | DMUS_PMSGF_DX8;
  586. pmsgCurve.p->dwPChannel = dwPChannel;
  587. // dwVirtualTrackID: this isn't a track so leave as 0
  588. pmsgCurve.p->dwType = DMUS_PMSGT_CURVE;
  589. pmsgCurve.p->dwGroupID = -1; // this isn't a track so just say all groups
  590. // curve PMsg fields
  591. pmsgCurve.p->mtDuration = lDuration; // setting the DMUS_PMSGF_LOCKTOREFTIME is interpreted by the performance that mtDuration is milliseconds
  592. // mtResetDuration: no reset so leave as 0
  593. // nStartValue: will be ignored
  594. pmsgCurve.p->nEndValue = bMIDIVol;
  595. // nResetValue: no reset so leave as 0
  596. pmsgCurve.p->bType = DMUS_CURVET_CCCURVE;
  597. pmsgCurve.p->bCurveShape = lDuration ? DMUS_CURVES_LINEAR : DMUS_CURVES_INSTANT;
  598. pmsgCurve.p->bCCData = 7; // MIDI volume controller number
  599. pmsgCurve.p->bFlags = DMUS_CURVE_START_FROM_CURRENT;
  600. // wParamType: leave as zero since this isn't a NRPN/RPN curve
  601. 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?
  602. // send it
  603. pmsgCurve.StampAndSend(pGraph);
  604. hr = pmsgCurve.hr();
  605. if (FAILED(hr))
  606. return hr;
  607. *pnNewVolume = lVolume;
  608. return S_OK;
  609. }