windows-nt/Source/XPSP1/NT/multimedia/directx/dmusic/dmscript/containerdisp.cpp
2020-09-26 16:20:57 +08:00

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;
}