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.

540 lines
13 KiB

  1. // Copyright (c) 2000 Microsoft Corporation. All rights reserved.
  2. //
  3. // Implementation of CContainerDispatch.
  4. //
  5. #include "stdinc.h"
  6. #include "containerdisp.h"
  7. #include "oleaut.h"
  8. #include "dmusicf.h"
  9. #include "activescript.h"
  10. #include "authelper.h"
  11. //#include "..\shared\dmusicp.h"
  12. //////////////////////////////////////////////////////////////////////
  13. // CContainerItemDispatch
  14. CContainerItemDispatch::CContainerItemDispatch(
  15. IDirectMusicLoader *pLoader,
  16. const WCHAR *wszAlias,
  17. const DMUS_OBJECTDESC &desc,
  18. bool fPreload,
  19. bool fAutodownload,
  20. HRESULT *phr)
  21. : m_pLoader(pLoader),
  22. m_pLoader8P(NULL),
  23. m_wstrAlias(wszAlias),
  24. m_desc(desc),
  25. m_fLoaded(false),
  26. m_pDispLoadedItem(NULL),
  27. m_fAutodownload(fAutodownload),
  28. m_pPerfForUnload(NULL)
  29. {
  30. assert(pLoader && phr);
  31. *phr = S_OK;
  32. HRESULT hr = m_pLoader->QueryInterface(IID_IDirectMusicLoader8P, reinterpret_cast<void**>(&m_pLoader8P));
  33. if (SUCCEEDED(hr))
  34. {
  35. // Hold only a private ref on the loader. See IDirectMusicLoader8P::AddRefP for more info.
  36. m_pLoader8P->AddRefP();
  37. m_pLoader->Release(); // offset the QI
  38. }
  39. else
  40. {
  41. // It's OK if there's no private interface. We just won't tell the garbage collector about stuff we load.
  42. // And we hold a normal reference.
  43. m_pLoader->AddRef();
  44. }
  45. if (fPreload)
  46. *phr = this->Load(false);
  47. }
  48. CContainerItemDispatch::~CContainerItemDispatch()
  49. {
  50. if (m_pPerfForUnload)
  51. {
  52. // We need to unload to correspond with the automatic download done when we were loaded.
  53. this->DownloadOrUnload(false, m_pPerfForUnload);
  54. }
  55. SafeRelease(m_pPerfForUnload);
  56. ReleaseLoader();
  57. SafeRelease(m_pDispLoadedItem);
  58. }
  59. STDMETHODIMP
  60. CContainerItemDispatch::GetIDsOfNames(
  61. REFIID riid,
  62. LPOLESTR __RPC_FAR *rgszNames,
  63. UINT cNames,
  64. LCID lcid,
  65. DISPID __RPC_FAR *rgDispId)
  66. {
  67. // If we're loaded and have a dispatch interface, defer to the real object.
  68. if (m_pDispLoadedItem)
  69. return m_pDispLoadedItem->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
  70. // Otherwise implement just the Load method.
  71. return AutLoadDispatchGetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
  72. }
  73. STDMETHODIMP
  74. CContainerItemDispatch::Invoke(
  75. DISPID dispIdMember,
  76. REFIID riid,
  77. LCID lcid,
  78. WORD wFlags,
  79. DISPPARAMS __RPC_FAR *pDispParams,
  80. VARIANT __RPC_FAR *pVarResult,
  81. EXCEPINFO __RPC_FAR *pExcepInfo,
  82. UINT __RPC_FAR *puArgErr)
  83. {
  84. // If we're loaded and have a dispatch interface, defer to the real object.
  85. if (m_pDispLoadedItem)
  86. return m_pDispLoadedItem->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
  87. // Otherwise implement just the Load method.
  88. bool fUseOleAut = false;
  89. HRESULT hr = AutLoadDispatchInvoke(&fUseOleAut, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
  90. if (FAILED(hr))
  91. return hr;
  92. InitWithPerfomanceFailureType eFailureType = IWP_Success;
  93. hr = m_fLoaded
  94. ? S_OK // if we've already been loaded, load can be called again and is a no-op
  95. : this->Load(true); // otherwise, actually load the object
  96. if (SUCCEEDED(hr))
  97. {
  98. IDirectMusicPerformance *pPerf = CActiveScriptManager::GetCurrentPerformanceWEAK();
  99. if (pPerf)
  100. {
  101. hr = this->InitWithPerformance(pPerf, &eFailureType);
  102. }
  103. else
  104. {
  105. assert(false);
  106. hr = E_FAIL;
  107. }
  108. }
  109. if (SUCCEEDED(hr))
  110. return hr; // everything worked--we're done
  111. // From here on, we've failed and need to return an exception.
  112. if (!pExcepInfo)
  113. return DISP_E_EXCEPTION;
  114. pExcepInfo->wCode = 0;
  115. pExcepInfo->wReserved = 0;
  116. pExcepInfo->bstrSource = DMS_SysAllocString(fUseOleAut, L"Microsoft DirectMusic Runtime Error");
  117. const WCHAR *pwszErrorBeg = NULL;
  118. if (eFailureType == IWP_DownloadFailed)
  119. {
  120. pwszErrorBeg = L"Unable to download the requested content (";
  121. }
  122. else if (eFailureType == IWP_ScriptInitFailed && hr == DMUS_E_SCRIPT_ERROR_IN_SCRIPT)
  123. {
  124. pwszErrorBeg = L"Syntax error loading the requested script (";
  125. }
  126. else
  127. {
  128. // Must have been a problem before InitWithPerformance or a problem with the script
  129. // that wasn't a syntax error.
  130. pwszErrorBeg = L"Unable to load the requested content (";
  131. }
  132. static const WCHAR wszErrorEnd[] = L".Load)";
  133. WCHAR *pwszDescription = new WCHAR[wcslen(pwszErrorBeg) + wcslen(m_wstrAlias) + wcslen(wszErrorEnd) + 1];
  134. if (!pwszDescription)
  135. {
  136. pExcepInfo->bstrDescription = NULL;
  137. }
  138. else
  139. {
  140. wcscpy(pwszDescription, pwszErrorBeg);
  141. wcscat(pwszDescription, m_wstrAlias);
  142. wcscat(pwszDescription, wszErrorEnd);
  143. pExcepInfo->bstrDescription = DMS_SysAllocString(fUseOleAut, pwszDescription);
  144. delete[] pwszDescription;
  145. }
  146. pExcepInfo->bstrHelpFile = NULL;
  147. pExcepInfo->pvReserved = NULL;
  148. pExcepInfo->pfnDeferredFillIn = NULL;
  149. pExcepInfo->scode = hr;
  150. return DISP_E_EXCEPTION;
  151. }
  152. HRESULT
  153. CContainerItemDispatch::InitWithPerformance(IDirectMusicPerformance *pPerf, InitWithPerfomanceFailureType *peFailureType)
  154. {
  155. if (!m_fLoaded || !pPerf || !peFailureType)
  156. {
  157. assert(false);
  158. return E_FAIL;
  159. }
  160. *peFailureType = IWP_Success;
  161. if (!m_pDispLoadedItem)
  162. return S_OK; // don't have an active item so no initialization is necessary
  163. HRESULT hr = S_OK;
  164. if (m_fAutodownload)
  165. {
  166. hr = this->DownloadOrUnload(true, pPerf);
  167. if (hr == S_OK)
  168. {
  169. m_pPerfForUnload = pPerf;
  170. m_pPerfForUnload->AddRef();
  171. }
  172. if (FAILED(hr))
  173. {
  174. *peFailureType = IWP_DownloadFailed;
  175. return hr;
  176. }
  177. }
  178. if (m_desc.guidClass == CLSID_DirectMusicScript)
  179. {
  180. IDirectMusicScript *pScript = NULL;
  181. hr = m_pDispLoadedItem->QueryInterface(IID_IDirectMusicScript, reinterpret_cast<void**>(&pScript));
  182. if (SUCCEEDED(hr))
  183. {
  184. hr = pScript->Init(pPerf, NULL);
  185. pScript->Release();
  186. }
  187. if (FAILED(hr))
  188. {
  189. *peFailureType = IWP_ScriptInitFailed;
  190. return hr;
  191. }
  192. }
  193. else if (m_desc.guidClass == CLSID_DirectMusicSong)
  194. {
  195. IDirectMusicSong *pSong = NULL;
  196. hr = m_pDispLoadedItem->QueryInterface(IID_IDirectMusicSong, reinterpret_cast<void**>(&pSong));
  197. if (SUCCEEDED(hr))
  198. {
  199. hr = pSong->Compose();
  200. pSong->Release();
  201. }
  202. if (FAILED(hr))
  203. {
  204. *peFailureType = IWP_DownloadFailed;
  205. return hr;
  206. }
  207. }
  208. return S_OK;
  209. }
  210. void CContainerItemDispatch::ReleaseLoader()
  211. {
  212. if (m_pLoader8P)
  213. {
  214. // If we had the private interface, we just need to do a private release.
  215. m_pLoader8P->ReleaseP();
  216. m_pLoader8P = NULL;
  217. m_pLoader = NULL;
  218. }
  219. else
  220. {
  221. // We just have the public interface, so do a normal release.
  222. SafeRelease(m_pLoader);
  223. }
  224. }
  225. HRESULT
  226. CContainerItemDispatch::Load(bool fDynamicLoad)
  227. {
  228. HRESULT hr = S_OK;
  229. assert(m_pLoader);
  230. IUnknown *punkLoadedItem = NULL;
  231. if (fDynamicLoad && m_pLoader8P)
  232. {
  233. IDirectMusicObject *pScriptObject = CActiveScriptManager::GetCurrentScriptObjectWEAK();
  234. hr = m_pLoader8P->GetDynamicallyReferencedObject(pScriptObject, &m_desc, IID_IUnknown, reinterpret_cast<void**>(&punkLoadedItem));
  235. }
  236. else
  237. {
  238. // It's OK if there's no private interface. We just won't tell the garbage collector about this load.
  239. hr = m_pLoader->GetObject(&m_desc, IID_IUnknown, reinterpret_cast<void**>(&punkLoadedItem));
  240. }
  241. if (SUCCEEDED(hr))
  242. {
  243. assert(punkLoadedItem);
  244. ReleaseLoader();
  245. m_fLoaded = true;
  246. // save the object's IDispatch interface, if it has one
  247. punkLoadedItem->QueryInterface(IID_IDispatch, reinterpret_cast<void**>(&m_pDispLoadedItem));
  248. punkLoadedItem->Release();
  249. }
  250. return hr;
  251. }
  252. HRESULT
  253. CContainerItemDispatch::DownloadOrUnload(bool fDownload, IDirectMusicPerformance *pPerf)
  254. {
  255. HRESULT hr = S_OK;
  256. if (m_desc.guidClass == CLSID_DirectMusicSegment)
  257. {
  258. assert(pPerf);
  259. IDirectMusicSegment8 *pSegment = NULL;
  260. hr = m_pDispLoadedItem->QueryInterface(IID_IDirectMusicSegment8, reinterpret_cast<void**>(&pSegment));
  261. if (FAILED(hr))
  262. return hr;
  263. hr = fDownload
  264. ? pSegment->Download(pPerf)
  265. : pSegment->Unload(pPerf);
  266. pSegment->Release();
  267. }
  268. else if (m_desc.guidClass == CLSID_DirectMusicSong)
  269. {
  270. assert(pPerf);
  271. IDirectMusicSong8 *pSong = NULL;
  272. hr = m_pDispLoadedItem->QueryInterface(IID_IDirectMusicSong8, reinterpret_cast<void**>(&pSong));
  273. if (FAILED(hr))
  274. return hr;
  275. hr = fDownload
  276. ? pSong->Download(pPerf)
  277. : pSong->Unload(pPerf);
  278. pSong->Release();
  279. }
  280. else
  281. {
  282. hr = S_FALSE; // this type of object doesn't need to be downloaded
  283. }
  284. return hr;
  285. }
  286. //////////////////////////////////////////////////////////////////////
  287. // CContainerDispatch
  288. CContainerDispatch::CContainerDispatch(IDirectMusicContainer *pContainer, IDirectMusicLoader *pLoader, DWORD dwScriptFlags, HRESULT *phr)
  289. {
  290. assert(pContainer && pLoader && phr);
  291. *phr = S_OK;
  292. DMUS_OBJECTDESC desc;
  293. ZeroAndSize(&desc);
  294. WCHAR wszAlias[MAX_PATH] = L"";
  295. // we need to download all the segments when the script is initialized if both loading and downloading are on
  296. bool fLoad = !!(dwScriptFlags & DMUS_SCRIPTIOF_LOAD_ALL_CONTENT);
  297. bool fDownload = !!(dwScriptFlags & DMUS_SCRIPTIOF_DOWNLOAD_ALL_SEGMENTS);
  298. m_fDownloadOnInit = fLoad && fDownload;
  299. DWORD i = 0;
  300. for (;;)
  301. {
  302. // Read an item out of the container
  303. *phr = pContainer->EnumObject(GUID_DirectMusicAllTypes, i, &desc, wszAlias);
  304. if (FAILED(*phr))
  305. return;
  306. if (*phr == S_FALSE)
  307. {
  308. // we've read all the items
  309. *phr = S_OK;
  310. return;
  311. }
  312. // Make an object to represent the item
  313. CContainerItemDispatch *pNewItem = new CContainerItemDispatch(
  314. pLoader,
  315. wszAlias,
  316. desc,
  317. fLoad,
  318. fDownload,
  319. phr);
  320. if (!pNewItem)
  321. *phr = E_OUTOFMEMORY;
  322. if (FAILED(*phr))
  323. {
  324. if(pNewItem)
  325. {
  326. pNewItem->Release();
  327. }
  328. return;
  329. }
  330. // Add an entry to the table
  331. UINT iSlot = m_vecItems.size();
  332. if (!m_vecItems.AccessTo(iSlot))
  333. {
  334. pNewItem->Release();
  335. *phr = E_OUTOFMEMORY;
  336. return;
  337. }
  338. m_vecItems[iSlot] = pNewItem;
  339. // Set up for next iteration
  340. ZeroAndSize(&desc);
  341. wszAlias[0] = L'\0';
  342. ++i;
  343. }
  344. }
  345. CContainerDispatch::~CContainerDispatch()
  346. {
  347. UINT iEnd = m_vecItems.size();
  348. for (UINT i = 0; i < m_vecItems.size(); ++i)
  349. {
  350. m_vecItems[i]->Release();
  351. }
  352. }
  353. HRESULT
  354. CContainerDispatch::OnScriptInit(IDirectMusicPerformance *pPerf)
  355. {
  356. if (m_fDownloadOnInit)
  357. {
  358. UINT iEnd = m_vecItems.size();
  359. for (UINT i = 0; i < m_vecItems.size(); ++i)
  360. {
  361. CContainerItemDispatch::InitWithPerfomanceFailureType eFailureType;
  362. m_vecItems[i]->InitWithPerformance(pPerf, &eFailureType);
  363. }
  364. }
  365. return S_OK;
  366. }
  367. HRESULT
  368. CContainerDispatch::GetIDsOfNames(
  369. REFIID riid,
  370. LPOLESTR __RPC_FAR *rgszNames,
  371. UINT cNames,
  372. LCID lcid,
  373. DISPID __RPC_FAR *rgDispId)
  374. {
  375. // Otherwise implement just the Load method.
  376. V_INAME(CContainerDispatch::GetIDsOfNames);
  377. V_BUFPTR_READ(rgszNames, sizeof(LPOLESTR) * cNames);
  378. V_BUFPTR_WRITE(rgDispId, sizeof(DISPID) * cNames);
  379. if (riid != IID_NULL)
  380. return DISP_E_UNKNOWNINTERFACE;
  381. if (cNames == 0)
  382. return S_OK;
  383. // Clear out dispid's
  384. for (UINT c = 0; c < cNames; ++c)
  385. {
  386. rgDispId[c] = DISPID_UNKNOWN;
  387. }
  388. // See if we have a method with the first name
  389. UINT cEnd = m_vecItems.size();
  390. for (c = 0; c < cEnd; ++c)
  391. {
  392. if (0 == _wcsicmp(rgszNames[0], m_vecItems[c]->Alias()))
  393. {
  394. rgDispId[0] = c + 1;
  395. break;
  396. }
  397. }
  398. // Additional names requested (cNames > 1) are named parameters to the method,
  399. // which isn't something we support.
  400. // Return DISP_E_UNKNOWNNAME in this case, and in the case that we didn't match
  401. // the first name.
  402. if (rgDispId[0] == DISPID_UNKNOWN || cNames > 1)
  403. return DISP_E_UNKNOWNNAME;
  404. return S_OK;
  405. }
  406. HRESULT
  407. CContainerDispatch::Invoke(
  408. DISPID dispIdMember,
  409. REFIID riid,
  410. LCID lcid,
  411. WORD wFlags,
  412. DISPPARAMS __RPC_FAR *pDispParams,
  413. VARIANT __RPC_FAR *pVarResult,
  414. EXCEPINFO __RPC_FAR *pExcepInfo,
  415. UINT __RPC_FAR *puArgErr)
  416. {
  417. V_INAME(CContainerDispatch::Invoke);
  418. V_PTR_READ(pDispParams, DISPPARAMS);
  419. V_PTR_WRITE_OPT(pVarResult, VARIANT);
  420. V_PTR_WRITE_OPT(pExcepInfo, EXCEPINFO);
  421. bool fUseOleAut = !!(riid == IID_NULL);
  422. // Additional parameter validation
  423. if (!fUseOleAut && riid != g_guidInvokeWithoutOleaut)
  424. return DISP_E_UNKNOWNINTERFACE;
  425. if (!(wFlags & DISPATCH_PROPERTYGET))
  426. return DISP_E_MEMBERNOTFOUND;
  427. if (pDispParams->cArgs > 0)
  428. return DISP_E_BADPARAMCOUNT;
  429. if (pDispParams->cNamedArgs > 0)
  430. return DISP_E_NONAMEDARGS;
  431. // Zero the out params
  432. if (puArgErr)
  433. *puArgErr = 0;
  434. if (pVarResult)
  435. {
  436. DMS_VariantInit(fUseOleAut, pVarResult);
  437. }
  438. if (dispIdMember > m_vecItems.size())
  439. return DISP_E_MEMBERNOTFOUND;
  440. // Return the value
  441. if (pVarResult)
  442. {
  443. pVarResult->vt = VT_DISPATCH;
  444. pVarResult->pdispVal = m_vecItems[dispIdMember - 1]->Item();
  445. pVarResult->pdispVal->AddRef();
  446. }
  447. return S_OK;
  448. }
  449. HRESULT
  450. CContainerDispatch::EnumItem(DWORD dwIndex, WCHAR *pwszName)
  451. {
  452. if (dwIndex >= m_vecItems.size())
  453. return S_FALSE;
  454. CContainerItemDispatch *pItem = m_vecItems[dwIndex];
  455. return wcsTruncatedCopy(pwszName, pItem->Alias(), MAX_PATH);
  456. }
  457. HRESULT
  458. CContainerDispatch::GetVariableObject(WCHAR *pwszVariableName, IUnknown **ppunkValue)
  459. {
  460. assert(pwszVariableName && ppunkValue);
  461. UINT cEnd = m_vecItems.size();
  462. for (UINT c = 0; c < cEnd; ++c)
  463. {
  464. if (0 == _wcsicmp(pwszVariableName, m_vecItems[c]->Alias()))
  465. {
  466. *ppunkValue = m_vecItems[c]->Item();
  467. (*ppunkValue)->AddRef();
  468. return S_OK;
  469. }
  470. }
  471. return DMUS_E_SCRIPT_VARIABLE_NOT_FOUND;
  472. }