// Copyright (c) 2000 Microsoft Corporation. All rights reserved. // // Implementation of CContainerDispatch. // #include "stdinc.h" #include "containerdisp.h" #include "oleaut.h" #include "dmusicf.h" #include "activescript.h" #include "authelper.h" //#include "..\shared\dmusicp.h" ////////////////////////////////////////////////////////////////////// // CContainerItemDispatch CContainerItemDispatch::CContainerItemDispatch( IDirectMusicLoader *pLoader, const WCHAR *wszAlias, const DMUS_OBJECTDESC &desc, bool fPreload, bool fAutodownload, HRESULT *phr) : m_pLoader(pLoader), m_pLoader8P(NULL), m_wstrAlias(wszAlias), m_desc(desc), m_fLoaded(false), m_pDispLoadedItem(NULL), m_fAutodownload(fAutodownload), m_pPerfForUnload(NULL) { assert(pLoader && phr); *phr = S_OK; HRESULT hr = m_pLoader->QueryInterface(IID_IDirectMusicLoader8P, reinterpret_cast(&m_pLoader8P)); if (SUCCEEDED(hr)) { // Hold only a private ref on the loader. See IDirectMusicLoader8P::AddRefP for more info. m_pLoader8P->AddRefP(); m_pLoader->Release(); // offset the QI } else { // It's OK if there's no private interface. We just won't tell the garbage collector about stuff we load. // And we hold a normal reference. m_pLoader->AddRef(); } if (fPreload) *phr = this->Load(false); } CContainerItemDispatch::~CContainerItemDispatch() { if (m_pPerfForUnload) { // We need to unload to correspond with the automatic download done when we were loaded. this->DownloadOrUnload(false, m_pPerfForUnload); } SafeRelease(m_pPerfForUnload); ReleaseLoader(); SafeRelease(m_pDispLoadedItem); } STDMETHODIMP CContainerItemDispatch::GetIDsOfNames( REFIID riid, LPOLESTR __RPC_FAR *rgszNames, UINT cNames, LCID lcid, DISPID __RPC_FAR *rgDispId) { // If we're loaded and have a dispatch interface, defer to the real object. if (m_pDispLoadedItem) return m_pDispLoadedItem->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId); // Otherwise implement just the Load method. return AutLoadDispatchGetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId); } STDMETHODIMP CContainerItemDispatch::Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS __RPC_FAR *pDispParams, VARIANT __RPC_FAR *pVarResult, EXCEPINFO __RPC_FAR *pExcepInfo, UINT __RPC_FAR *puArgErr) { // If we're loaded and have a dispatch interface, defer to the real object. if (m_pDispLoadedItem) return m_pDispLoadedItem->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); // Otherwise implement just the Load method. bool fUseOleAut = false; HRESULT hr = AutLoadDispatchInvoke(&fUseOleAut, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); if (FAILED(hr)) return hr; InitWithPerfomanceFailureType eFailureType = IWP_Success; hr = m_fLoaded ? S_OK // if we've already been loaded, load can be called again and is a no-op : this->Load(true); // otherwise, actually load the object if (SUCCEEDED(hr)) { IDirectMusicPerformance *pPerf = CActiveScriptManager::GetCurrentPerformanceWEAK(); if (pPerf) { hr = this->InitWithPerformance(pPerf, &eFailureType); } else { assert(false); hr = E_FAIL; } } if (SUCCEEDED(hr)) return hr; // everything worked--we're done // From here on, we've failed and need to return an exception. if (!pExcepInfo) return DISP_E_EXCEPTION; pExcepInfo->wCode = 0; pExcepInfo->wReserved = 0; pExcepInfo->bstrSource = DMS_SysAllocString(fUseOleAut, L"Microsoft DirectMusic Runtime Error"); const WCHAR *pwszErrorBeg = NULL; if (eFailureType == IWP_DownloadFailed) { pwszErrorBeg = L"Unable to download the requested content ("; } else if (eFailureType == IWP_ScriptInitFailed && hr == DMUS_E_SCRIPT_ERROR_IN_SCRIPT) { pwszErrorBeg = L"Syntax error loading the requested script ("; } else { // Must have been a problem before InitWithPerformance or a problem with the script // that wasn't a syntax error. pwszErrorBeg = L"Unable to load the requested content ("; } static const WCHAR wszErrorEnd[] = L".Load)"; WCHAR *pwszDescription = new WCHAR[wcslen(pwszErrorBeg) + wcslen(m_wstrAlias) + wcslen(wszErrorEnd) + 1]; if (!pwszDescription) { pExcepInfo->bstrDescription = NULL; } else { wcscpy(pwszDescription, pwszErrorBeg); wcscat(pwszDescription, m_wstrAlias); wcscat(pwszDescription, wszErrorEnd); pExcepInfo->bstrDescription = DMS_SysAllocString(fUseOleAut, pwszDescription); delete[] pwszDescription; } pExcepInfo->bstrHelpFile = NULL; pExcepInfo->pvReserved = NULL; pExcepInfo->pfnDeferredFillIn = NULL; pExcepInfo->scode = hr; return DISP_E_EXCEPTION; } HRESULT CContainerItemDispatch::InitWithPerformance(IDirectMusicPerformance *pPerf, InitWithPerfomanceFailureType *peFailureType) { if (!m_fLoaded || !pPerf || !peFailureType) { assert(false); return E_FAIL; } *peFailureType = IWP_Success; if (!m_pDispLoadedItem) return S_OK; // don't have an active item so no initialization is necessary HRESULT hr = S_OK; if (m_fAutodownload) { hr = this->DownloadOrUnload(true, pPerf); if (hr == S_OK) { m_pPerfForUnload = pPerf; m_pPerfForUnload->AddRef(); } if (FAILED(hr)) { *peFailureType = IWP_DownloadFailed; return hr; } } if (m_desc.guidClass == CLSID_DirectMusicScript) { IDirectMusicScript *pScript = NULL; hr = m_pDispLoadedItem->QueryInterface(IID_IDirectMusicScript, reinterpret_cast(&pScript)); if (SUCCEEDED(hr)) { hr = pScript->Init(pPerf, NULL); pScript->Release(); } if (FAILED(hr)) { *peFailureType = IWP_ScriptInitFailed; return hr; } } else if (m_desc.guidClass == CLSID_DirectMusicSong) { IDirectMusicSong *pSong = NULL; hr = m_pDispLoadedItem->QueryInterface(IID_IDirectMusicSong, reinterpret_cast(&pSong)); if (SUCCEEDED(hr)) { hr = pSong->Compose(); pSong->Release(); } if (FAILED(hr)) { *peFailureType = IWP_DownloadFailed; return hr; } } return S_OK; } void CContainerItemDispatch::ReleaseLoader() { if (m_pLoader8P) { // If we had the private interface, we just need to do a private release. m_pLoader8P->ReleaseP(); m_pLoader8P = NULL; m_pLoader = NULL; } else { // We just have the public interface, so do a normal release. SafeRelease(m_pLoader); } } HRESULT CContainerItemDispatch::Load(bool fDynamicLoad) { HRESULT hr = S_OK; assert(m_pLoader); IUnknown *punkLoadedItem = NULL; if (fDynamicLoad && m_pLoader8P) { IDirectMusicObject *pScriptObject = CActiveScriptManager::GetCurrentScriptObjectWEAK(); hr = m_pLoader8P->GetDynamicallyReferencedObject(pScriptObject, &m_desc, IID_IUnknown, reinterpret_cast(&punkLoadedItem)); } else { // It's OK if there's no private interface. We just won't tell the garbage collector about this load. hr = m_pLoader->GetObject(&m_desc, IID_IUnknown, reinterpret_cast(&punkLoadedItem)); } if (SUCCEEDED(hr)) { assert(punkLoadedItem); ReleaseLoader(); m_fLoaded = true; // save the object's IDispatch interface, if it has one punkLoadedItem->QueryInterface(IID_IDispatch, reinterpret_cast(&m_pDispLoadedItem)); punkLoadedItem->Release(); } return hr; } HRESULT CContainerItemDispatch::DownloadOrUnload(bool fDownload, IDirectMusicPerformance *pPerf) { HRESULT hr = S_OK; if (m_desc.guidClass == CLSID_DirectMusicSegment) { assert(pPerf); IDirectMusicSegment8 *pSegment = NULL; hr = m_pDispLoadedItem->QueryInterface(IID_IDirectMusicSegment8, reinterpret_cast(&pSegment)); if (FAILED(hr)) return hr; hr = fDownload ? pSegment->Download(pPerf) : pSegment->Unload(pPerf); pSegment->Release(); } else if (m_desc.guidClass == CLSID_DirectMusicSong) { assert(pPerf); IDirectMusicSong8 *pSong = NULL; hr = m_pDispLoadedItem->QueryInterface(IID_IDirectMusicSong8, reinterpret_cast(&pSong)); if (FAILED(hr)) return hr; hr = fDownload ? pSong->Download(pPerf) : pSong->Unload(pPerf); pSong->Release(); } else { hr = S_FALSE; // this type of object doesn't need to be downloaded } return hr; } ////////////////////////////////////////////////////////////////////// // CContainerDispatch CContainerDispatch::CContainerDispatch(IDirectMusicContainer *pContainer, IDirectMusicLoader *pLoader, DWORD dwScriptFlags, HRESULT *phr) { assert(pContainer && pLoader && phr); *phr = S_OK; DMUS_OBJECTDESC desc; ZeroAndSize(&desc); WCHAR wszAlias[MAX_PATH] = L""; // we need to download all the segments when the script is initialized if both loading and downloading are on bool fLoad = !!(dwScriptFlags & DMUS_SCRIPTIOF_LOAD_ALL_CONTENT); bool fDownload = !!(dwScriptFlags & DMUS_SCRIPTIOF_DOWNLOAD_ALL_SEGMENTS); m_fDownloadOnInit = fLoad && fDownload; DWORD i = 0; for (;;) { // Read an item out of the container *phr = pContainer->EnumObject(GUID_DirectMusicAllTypes, i, &desc, wszAlias); if (FAILED(*phr)) return; if (*phr == S_FALSE) { // we've read all the items *phr = S_OK; return; } // Make an object to represent the item CContainerItemDispatch *pNewItem = new CContainerItemDispatch( pLoader, wszAlias, desc, fLoad, fDownload, phr); if (!pNewItem) *phr = E_OUTOFMEMORY; if (FAILED(*phr)) { if(pNewItem) { pNewItem->Release(); } return; } // Add an entry to the table UINT iSlot = m_vecItems.size(); if (!m_vecItems.AccessTo(iSlot)) { pNewItem->Release(); *phr = E_OUTOFMEMORY; return; } m_vecItems[iSlot] = pNewItem; // Set up for next iteration ZeroAndSize(&desc); wszAlias[0] = L'\0'; ++i; } } CContainerDispatch::~CContainerDispatch() { UINT iEnd = m_vecItems.size(); for (UINT i = 0; i < m_vecItems.size(); ++i) { m_vecItems[i]->Release(); } } HRESULT CContainerDispatch::OnScriptInit(IDirectMusicPerformance *pPerf) { if (m_fDownloadOnInit) { UINT iEnd = m_vecItems.size(); for (UINT i = 0; i < m_vecItems.size(); ++i) { CContainerItemDispatch::InitWithPerfomanceFailureType eFailureType; m_vecItems[i]->InitWithPerformance(pPerf, &eFailureType); } } return S_OK; } HRESULT CContainerDispatch::GetIDsOfNames( REFIID riid, LPOLESTR __RPC_FAR *rgszNames, UINT cNames, LCID lcid, DISPID __RPC_FAR *rgDispId) { // Otherwise implement just the Load method. V_INAME(CContainerDispatch::GetIDsOfNames); 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 UINT cEnd = m_vecItems.size(); for (c = 0; c < cEnd; ++c) { if (0 == _wcsicmp(rgszNames[0], m_vecItems[c]->Alias())) { rgDispId[0] = c + 1; 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; } HRESULT CContainerDispatch::Invoke( 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(CContainerDispatch::Invoke); V_PTR_READ(pDispParams, DISPPARAMS); V_PTR_WRITE_OPT(pVarResult, VARIANT); V_PTR_WRITE_OPT(pExcepInfo, EXCEPINFO); bool fUseOleAut = !!(riid == IID_NULL); // Additional parameter validation if (!fUseOleAut && riid != g_guidInvokeWithoutOleaut) return DISP_E_UNKNOWNINTERFACE; if (!(wFlags & DISPATCH_PROPERTYGET)) return DISP_E_MEMBERNOTFOUND; if (pDispParams->cArgs > 0) return DISP_E_BADPARAMCOUNT; if (pDispParams->cNamedArgs > 0) return DISP_E_NONAMEDARGS; // Zero the out params if (puArgErr) *puArgErr = 0; if (pVarResult) { DMS_VariantInit(fUseOleAut, pVarResult); } if (dispIdMember > m_vecItems.size()) return DISP_E_MEMBERNOTFOUND; // Return the value if (pVarResult) { pVarResult->vt = VT_DISPATCH; pVarResult->pdispVal = m_vecItems[dispIdMember - 1]->Item(); pVarResult->pdispVal->AddRef(); } return S_OK; } HRESULT CContainerDispatch::EnumItem(DWORD dwIndex, WCHAR *pwszName) { if (dwIndex >= m_vecItems.size()) return S_FALSE; CContainerItemDispatch *pItem = m_vecItems[dwIndex]; return wcsTruncatedCopy(pwszName, pItem->Alias(), MAX_PATH); } HRESULT CContainerDispatch::GetVariableObject(WCHAR *pwszVariableName, IUnknown **ppunkValue) { assert(pwszVariableName && ppunkValue); UINT cEnd = m_vecItems.size(); for (UINT c = 0; c < cEnd; ++c) { if (0 == _wcsicmp(pwszVariableName, m_vecItems[c]->Alias())) { *ppunkValue = m_vecItems[c]->Item(); (*ppunkValue)->AddRef(); return S_OK; } } return DMUS_E_SCRIPT_VARIABLE_NOT_FOUND; }