541 lines
13 KiB
C++
541 lines
13 KiB
C++
// 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<void**>(&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<void**>(&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<void**>(&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<void**>(&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<void**>(&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<void**>(&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<void**>(&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<void**>(&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;
|
|
}
|