1162 lines
39 KiB
C++
1162 lines
39 KiB
C++
//
|
|
// Copyright (c) 1999-2001 Microsoft Corporation. All rights reserved.
|
|
//
|
|
// Implementation of CDirectMusicScript.
|
|
//
|
|
|
|
#include "stdinc.h"
|
|
#include "dll.h"
|
|
#include "dmscript.h"
|
|
#include "oleaut.h"
|
|
#include "globaldisp.h"
|
|
#include "activescript.h"
|
|
#include "sourcetext.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Creation
|
|
|
|
CDirectMusicScript::CDirectMusicScript()
|
|
: m_cRef(0),
|
|
m_fZombie(false),
|
|
m_fCriticalSectionInitialized(false),
|
|
m_pPerformance8(NULL),
|
|
m_pLoader8P(NULL),
|
|
m_pDispPerformance(NULL),
|
|
m_pComposer8(NULL),
|
|
m_fUseOleAut(true),
|
|
m_pScriptManager(NULL),
|
|
m_pContainerDispatch(NULL),
|
|
m_pGlobalDispatch(NULL),
|
|
m_fInitError(false)
|
|
{
|
|
LockModule(true);
|
|
InitializeCriticalSection(&m_CriticalSection);
|
|
// Note: on pre-Blackcomb OS's, this call can raise an exception; if it
|
|
// ever pops in stress, we can add an exception handler and retry loop.
|
|
m_fCriticalSectionInitialized = TRUE;
|
|
|
|
m_info.fLoaded = false;
|
|
m_vDirectMusicVersion.dwVersionMS = 0;
|
|
m_vDirectMusicVersion.dwVersionLS = 0;
|
|
Zero(&m_iohead);
|
|
ZeroAndSize(&m_InitErrorInfo);
|
|
}
|
|
|
|
void CDirectMusicScript::ReleaseObjects()
|
|
{
|
|
if (m_pScriptManager)
|
|
{
|
|
m_pScriptManager->Close();
|
|
SafeRelease(m_pScriptManager);
|
|
}
|
|
SafeRelease(m_pPerformance8);
|
|
SafeRelease(m_pDispPerformance);
|
|
if (m_pLoader8P)
|
|
{
|
|
m_pLoader8P->ReleaseP();
|
|
m_pLoader8P = NULL;
|
|
}
|
|
SafeRelease(m_pComposer8);
|
|
delete m_pContainerDispatch;
|
|
m_pContainerDispatch = NULL;
|
|
delete m_pGlobalDispatch;
|
|
m_pGlobalDispatch = NULL;
|
|
}
|
|
|
|
HRESULT CDirectMusicScript::CreateInstance(
|
|
IUnknown* pUnknownOuter,
|
|
const IID& iid,
|
|
void** ppv)
|
|
{
|
|
*ppv = NULL;
|
|
if (pUnknownOuter)
|
|
return CLASS_E_NOAGGREGATION;
|
|
|
|
CDirectMusicScript *pInst = new CDirectMusicScript;
|
|
if (pInst == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
return pInst->QueryInterface(iid, ppv);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// IUnknown
|
|
|
|
STDMETHODIMP
|
|
CDirectMusicScript::QueryInterface(const IID &iid, void **ppv)
|
|
{
|
|
V_INAME(CDirectMusicScript::QueryInterface);
|
|
V_PTRPTR_WRITE(ppv);
|
|
V_REFGUID(iid);
|
|
|
|
if (iid == IID_IUnknown || iid == IID_IDirectMusicScript)
|
|
{
|
|
*ppv = static_cast<IDirectMusicScript*>(this);
|
|
}
|
|
else if (iid == IID_IDirectMusicScriptPrivate)
|
|
{
|
|
*ppv = static_cast<IDirectMusicScriptPrivate*>(this);
|
|
}
|
|
else if (iid == IID_IDirectMusicObject)
|
|
{
|
|
*ppv = static_cast<IDirectMusicObject*>(this);
|
|
}
|
|
else if (iid == IID_IDirectMusicObjectP)
|
|
{
|
|
*ppv = static_cast<IDirectMusicObjectP*>(this);
|
|
}
|
|
else if (iid == IID_IPersistStream)
|
|
{
|
|
*ppv = static_cast<IPersistStream*>(this);
|
|
}
|
|
else if (iid == IID_IPersist)
|
|
{
|
|
*ppv = static_cast<IPersist*>(this);
|
|
}
|
|
else if (iid == IID_IDispatch)
|
|
{
|
|
*ppv = static_cast<IDispatch*>(this);
|
|
}
|
|
else
|
|
{
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
reinterpret_cast<IUnknown*>(this)->AddRef();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
CDirectMusicScript::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_cRef);
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG)
|
|
CDirectMusicScript::Release()
|
|
{
|
|
if (!InterlockedDecrement(&m_cRef))
|
|
{
|
|
this->Zombie();
|
|
DeleteCriticalSection(&m_CriticalSection);
|
|
delete this;
|
|
LockModule(false);
|
|
return 0;
|
|
}
|
|
|
|
return m_cRef;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// IPersistStream
|
|
|
|
STDMETHODIMP
|
|
CDirectMusicScript::Load(IStream* pStream)
|
|
{
|
|
V_INAME(CDirectMusicScript::Load);
|
|
V_INTERFACE(pStream);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(1, "Error: Call of IDirectMusicScript::Load after the script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
SmartRef::CritSec CS(&m_CriticalSection);
|
|
|
|
// Clear any old info
|
|
this->ReleaseObjects();
|
|
m_info.fLoaded = false;
|
|
m_info.oinfo.Clear();
|
|
m_vDirectMusicVersion.dwVersionMS = 0;
|
|
m_vDirectMusicVersion.dwVersionLS = 0;
|
|
m_wstrLanguage = NULL;
|
|
m_fInitError = false;
|
|
|
|
// Get the loader from stream
|
|
IDirectMusicGetLoader *pIDMGetLoader = NULL;
|
|
SmartRef::ComPtr<IDirectMusicLoader> scomLoader;
|
|
hr = pStream->QueryInterface(IID_IDirectMusicGetLoader, reinterpret_cast<void **>(&pIDMGetLoader));
|
|
if (FAILED(hr))
|
|
{
|
|
Trace(1, "Error: unable to load script from a stream because it doesn't support the IDirectMusicGetLoader interface.\n");
|
|
return DMUS_E_UNSUPPORTED_STREAM;
|
|
}
|
|
|
|
hr = pIDMGetLoader->GetLoader(&scomLoader);
|
|
pIDMGetLoader->Release();
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = scomLoader->QueryInterface(IID_IDirectMusicLoader8P, reinterpret_cast<void **>(&m_pLoader8P)); // OK if this fails -- just means the scripts won't be garbage collected
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Hold only a private ref on the loader. See IDirectMusicLoader8P::AddRefP for more info.
|
|
m_pLoader8P->AddRefP();
|
|
m_pLoader8P->Release(); // offset the QI
|
|
}
|
|
|
|
// Read the script's header information
|
|
|
|
SmartRef::RiffIter riForm(pStream);
|
|
if (!riForm)
|
|
{
|
|
#ifdef DBG
|
|
if (SUCCEEDED(riForm.hr()))
|
|
{
|
|
Trace(1, "Error: Unable to load script: Unexpected end of file.\n");
|
|
}
|
|
#endif
|
|
return SUCCEEDED(riForm.hr()) ? DMUS_E_SCRIPT_INVALID_FILE : riForm.hr();
|
|
}
|
|
hr = riForm.FindRequired(SmartRef::RiffIter::Riff, DMUS_FOURCC_SCRIPT_FORM, DMUS_E_SCRIPT_INVALID_FILE);
|
|
if (FAILED(hr))
|
|
{
|
|
#ifdef DBG
|
|
if (hr == DMUS_E_SCRIPT_INVALID_FILE)
|
|
{
|
|
Trace(1, "Error: Unable to load script: Form 'DMSC' not found.\n");
|
|
}
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
SmartRef::RiffIter ri = riForm.Descend();
|
|
if (!ri)
|
|
return ri.hr();
|
|
|
|
hr = ri.FindRequired(SmartRef::RiffIter::Chunk, DMUS_FOURCC_SCRIPT_CHUNK, DMUS_E_SCRIPT_INVALID_FILE);
|
|
if (FAILED(hr))
|
|
{
|
|
#ifdef DBG
|
|
if (hr == DMUS_E_SCRIPT_INVALID_FILE)
|
|
{
|
|
Trace(1, "Error: Unable to load script: Chunk 'schd' not found.\n");
|
|
}
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
hr = SmartRef::RiffIterReadChunk(ri, &m_iohead);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = ri.LoadObjectInfo(&m_info.oinfo, SmartRef::RiffIter::Chunk, DMUS_FOURCC_SCRIPTVERSION_CHUNK);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = SmartRef::RiffIterReadChunk(ri, &m_vDirectMusicVersion);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Read the script's embedded container
|
|
IDirectMusicContainer *pContainer = NULL;
|
|
hr = ri.FindAndGetEmbeddedObject(
|
|
SmartRef::RiffIter::Riff,
|
|
DMUS_FOURCC_CONTAINER_FORM,
|
|
DMUS_E_SCRIPT_INVALID_FILE,
|
|
scomLoader,
|
|
CLSID_DirectMusicContainer,
|
|
IID_IDirectMusicContainer,
|
|
reinterpret_cast<void**>(&pContainer));
|
|
if (FAILED(hr))
|
|
{
|
|
#ifdef DBG
|
|
if (hr == DMUS_E_SCRIPT_INVALID_FILE)
|
|
{
|
|
Trace(1, "Error: Unable to load script: Form 'DMCN' no found.\n");
|
|
}
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
// Build the container object that will represent the items in the container to the script
|
|
|
|
m_pContainerDispatch = new CContainerDispatch(pContainer, scomLoader, m_iohead.dwFlags, &hr);
|
|
pContainer->Release();
|
|
if (!m_pContainerDispatch)
|
|
return E_OUTOFMEMORY;
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Create the global dispatch object
|
|
|
|
m_pGlobalDispatch = new CGlobalDispatch(this);
|
|
if (!m_pGlobalDispatch)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// Get the script's language
|
|
|
|
hr = ri.FindRequired(SmartRef::RiffIter::Chunk, DMUS_FOURCC_SCRIPTLANGUAGE_CHUNK, DMUS_E_SCRIPT_INVALID_FILE);
|
|
if (FAILED(hr))
|
|
{
|
|
#ifdef DBG
|
|
if (hr == DMUS_E_SCRIPT_INVALID_FILE)
|
|
{
|
|
Trace(1, "Error: Unable to load script: Chunk 'scla' no found.\n");
|
|
}
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
hr = ri.ReadText(&m_wstrLanguage);
|
|
if (FAILED(hr))
|
|
{
|
|
#ifdef DBG
|
|
if (hr == E_FAIL)
|
|
{
|
|
Trace(1, "Error: Unable to load script: Problem reading 'scla' chunk.\n");
|
|
}
|
|
#endif
|
|
return hr == E_FAIL ? DMUS_E_SCRIPT_INVALID_FILE : hr;
|
|
}
|
|
|
|
// Get the script's source code
|
|
|
|
SmartRef::WString wstrSource;
|
|
for (++ri; ;++ri)
|
|
{
|
|
if (!ri)
|
|
{
|
|
Trace(1, "Error: Unable to load script: Expected chunk 'scsr' or list 'DMRF'.\n");
|
|
return DMUS_E_SCRIPT_INVALID_FILE;
|
|
}
|
|
|
|
SmartRef::RiffIter::RiffType type = ri.type();
|
|
FOURCC id = ri.id();
|
|
|
|
if (type == SmartRef::RiffIter::Chunk)
|
|
{
|
|
if (id == DMUS_FOURCC_SCRIPTSOURCE_CHUNK)
|
|
{
|
|
hr = ri.ReadText(&wstrSource);
|
|
if (FAILED(hr))
|
|
{
|
|
#ifdef DBG
|
|
if (hr == E_FAIL)
|
|
{
|
|
Trace(1, "Error: Unable to load script: Problem reading 'scsr' chunk.\n");
|
|
}
|
|
#endif
|
|
return hr == E_FAIL ? DMUS_E_SCRIPT_INVALID_FILE : hr;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else if (type == SmartRef::RiffIter::List)
|
|
{
|
|
if (id == DMUS_FOURCC_REF_LIST)
|
|
{
|
|
DMUS_OBJECTDESC desc;
|
|
hr = ri.ReadReference(&desc);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
// The resulting desc shouldn't have a name or GUID (the plain text file can't hold name/GUID info)
|
|
// and it should have a clsid should be GUID_NULL, which we'll replace with the clsid of our private
|
|
// source helper object.
|
|
if (desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_OBJECT) ||
|
|
!(desc.dwValidData & DMUS_OBJ_CLASS) || desc.guidClass != GUID_NULL)
|
|
{
|
|
#ifdef DBG
|
|
if (desc.dwValidData & (DMUS_OBJ_NAME | DMUS_OBJ_OBJECT))
|
|
{
|
|
Trace(1, "Error: Unable to load script: 'DMRF' list must have dwValidData with DMUS_OBJ_CLASS and guidClassID of GUID_NULL.\n");
|
|
}
|
|
else
|
|
{
|
|
Trace(1, "Error: Unable to load script: 'DMRF' list cannot have dwValidData with DMUS_OBJ_NAME or DMUS_OBJ_OBJECT.\n");
|
|
}
|
|
#endif
|
|
return DMUS_E_SCRIPT_INVALID_FILE;
|
|
}
|
|
desc.guidClass = CLSID_DirectMusicSourceText;
|
|
IDirectMusicSourceText *pISource = NULL;
|
|
hr = scomLoader->EnableCache(CLSID_DirectMusicSourceText, false); // This is a private object we just use temporarily. Don't want these guys hanging around in the cache.
|
|
if (FAILED(hr))
|
|
return hr;
|
|
hr = scomLoader->GetObject(&desc, IID_IDirectMusicSourceText, reinterpret_cast<void**>(&pISource));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
DWORD cwchSourceBufferSize = 0;
|
|
pISource->GetTextLength(&cwchSourceBufferSize);
|
|
WCHAR *pwszSource = new WCHAR[cwchSourceBufferSize];
|
|
if (!pwszSource)
|
|
return E_OUTOFMEMORY;
|
|
pISource->GetText(pwszSource);
|
|
*&wstrSource = pwszSource;
|
|
pISource->Release();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_info.fLoaded = true;
|
|
|
|
// Now that we are loaded and initialized, we can start active scripting
|
|
|
|
// See if we're dealing with a custom DirectMusic scripting engine. Such engines are marked with the key DMScript. They can be
|
|
// called on multiple threads and they don't use oleaut32. Ordinary active scripting engines are marked with the key OLEScript.
|
|
SmartRef::HKey shkeyLanguage;
|
|
SmartRef::HKey shkeyMark;
|
|
SmartRef::AString astrLanguage = m_wstrLanguage;
|
|
if (ERROR_SUCCESS != ::RegOpenKeyEx(HKEY_CLASSES_ROOT, astrLanguage, 0, KEY_QUERY_VALUE, &shkeyLanguage) || !shkeyLanguage)
|
|
{
|
|
Trace(1, "Error: Unable to load script: Scripting engine for language %s does not exist or is not registered.\n", astrLanguage);
|
|
return DMUS_E_SCRIPT_LANGUAGE_INCOMPATIBLE;
|
|
}
|
|
bool fCustomScriptEngine = ERROR_SUCCESS == ::RegOpenKeyEx(shkeyLanguage, "DMScript", 0, KEY_QUERY_VALUE, &shkeyMark) && shkeyMark;
|
|
if (!fCustomScriptEngine)
|
|
{
|
|
if (ERROR_SUCCESS != ::RegOpenKeyEx(shkeyLanguage, "OLEScript", 0, KEY_QUERY_VALUE, &shkeyMark) || !shkeyMark)
|
|
{
|
|
Trace(1, "Error: Unable to load script: Language %s refers to a COM object that is not registered as a scripting engine (OLEScript key).\n", astrLanguage);
|
|
return DMUS_E_SCRIPT_LANGUAGE_INCOMPATIBLE;
|
|
}
|
|
}
|
|
|
|
m_fUseOleAut = !fCustomScriptEngine;
|
|
if (fCustomScriptEngine)
|
|
{
|
|
m_pScriptManager = new CActiveScriptManager(
|
|
m_fUseOleAut,
|
|
m_wstrLanguage,
|
|
wstrSource,
|
|
this,
|
|
&hr,
|
|
&m_InitErrorInfo);
|
|
}
|
|
else
|
|
{
|
|
m_pScriptManager = new CSingleThreadedScriptManager(
|
|
m_fUseOleAut,
|
|
m_wstrLanguage,
|
|
wstrSource,
|
|
this,
|
|
&hr,
|
|
&m_InitErrorInfo);
|
|
}
|
|
|
|
if (!m_pScriptManager)
|
|
return E_OUTOFMEMORY;
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
SafeRelease(m_pScriptManager);
|
|
}
|
|
|
|
if (hr == DMUS_E_SCRIPT_ERROR_IN_SCRIPT)
|
|
{
|
|
// If we fail here, load would fail and client would never be able to get the
|
|
// error information. Instead, return S_OK and save the error to return from Init.
|
|
m_fInitError = true;
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// IDirectMusicObject
|
|
|
|
STDMETHODIMP
|
|
CDirectMusicScript::GetDescriptor(LPDMUS_OBJECTDESC pDesc)
|
|
{
|
|
V_INAME(CDirectMusicScript::GetDescriptor);
|
|
V_PTR_WRITE(pDesc, DMUS_OBJECTDESC);
|
|
|
|
ZeroMemory(pDesc, sizeof(DMUS_OBJECTDESC));
|
|
pDesc->dwSize = sizeof(DMUS_OBJECTDESC);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(1, "Error: Call of IDirectMusicScript::GetDescriptor after the script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
if (wcslen(m_info.oinfo.wszName) > 0)
|
|
{
|
|
pDesc->dwValidData |= DMUS_OBJ_NAME;
|
|
wcsncpy(pDesc->wszName, m_info.oinfo.wszName, DMUS_MAX_NAME);
|
|
pDesc->wszName[DMUS_MAX_NAME-1] = L'\0';
|
|
}
|
|
|
|
if (GUID_NULL != m_info.oinfo.guid)
|
|
{
|
|
pDesc->guidObject = m_info.oinfo.guid;
|
|
pDesc->dwValidData |= DMUS_OBJ_OBJECT;
|
|
}
|
|
|
|
pDesc->vVersion = m_info.oinfo.vVersion;
|
|
pDesc->dwValidData |= DMUS_OBJ_VERSION;
|
|
|
|
pDesc->guidClass = CLSID_DirectMusicScript;
|
|
pDesc->dwValidData |= DMUS_OBJ_CLASS;
|
|
|
|
if (m_info.wstrFilename)
|
|
{
|
|
wcsncpy(pDesc->wszFileName, m_info.wstrFilename, DMUS_MAX_FILENAME);
|
|
pDesc->wszFileName[DMUS_MAX_FILENAME-1] = L'\0';
|
|
pDesc->dwValidData |= DMUS_OBJ_FILENAME;
|
|
}
|
|
|
|
if (m_info.fLoaded)
|
|
{
|
|
pDesc->dwValidData |= DMUS_OBJ_LOADED;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CDirectMusicScript::SetDescriptor(LPDMUS_OBJECTDESC pDesc)
|
|
{
|
|
V_INAME(CDirectMusicScript::SetDescriptor);
|
|
V_STRUCTPTR_READ(pDesc, DMUS_OBJECTDESC);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(1, "Error: Call of IDirectMusicScript::SetDescriptor after the script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
DWORD dwTemp = pDesc->dwValidData;
|
|
|
|
if (pDesc->dwValidData & DMUS_OBJ_OBJECT)
|
|
{
|
|
m_info.oinfo.guid = pDesc->guidObject;
|
|
}
|
|
|
|
if (pDesc->dwValidData & DMUS_OBJ_CLASS)
|
|
{
|
|
pDesc->dwValidData &= ~DMUS_OBJ_CLASS;
|
|
}
|
|
|
|
if (pDesc->dwValidData & DMUS_OBJ_NAME)
|
|
{
|
|
wcsncpy(m_info.oinfo.wszName, pDesc->wszName, DMUS_MAX_NAME);
|
|
m_info.oinfo.wszName[DMUS_MAX_NAME-1] = L'\0';
|
|
}
|
|
|
|
if (pDesc->dwValidData & DMUS_OBJ_CATEGORY)
|
|
{
|
|
pDesc->dwValidData &= ~DMUS_OBJ_CATEGORY;
|
|
}
|
|
|
|
if (pDesc->dwValidData & DMUS_OBJ_FILENAME)
|
|
{
|
|
m_info.wstrFilename = pDesc->wszFileName;
|
|
}
|
|
|
|
if (pDesc->dwValidData & DMUS_OBJ_FULLPATH)
|
|
{
|
|
pDesc->dwValidData &= ~DMUS_OBJ_FULLPATH;
|
|
}
|
|
|
|
if (pDesc->dwValidData & DMUS_OBJ_URL)
|
|
{
|
|
pDesc->dwValidData &= ~DMUS_OBJ_URL;
|
|
}
|
|
|
|
if (pDesc->dwValidData & DMUS_OBJ_VERSION)
|
|
{
|
|
m_info.oinfo.vVersion = pDesc->vVersion;
|
|
}
|
|
|
|
if (pDesc->dwValidData & DMUS_OBJ_DATE)
|
|
{
|
|
pDesc->dwValidData &= ~DMUS_OBJ_DATE;
|
|
}
|
|
|
|
if (pDesc->dwValidData & DMUS_OBJ_LOADED)
|
|
{
|
|
pDesc->dwValidData &= ~DMUS_OBJ_LOADED;
|
|
}
|
|
|
|
return dwTemp == pDesc->dwValidData ? S_OK : S_FALSE;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CDirectMusicScript::ParseDescriptor(LPSTREAM pStream, LPDMUS_OBJECTDESC pDesc)
|
|
{
|
|
V_INAME(CDirectMusicScript::ParseDescriptor);
|
|
V_INTERFACE(pStream);
|
|
V_PTR_WRITE(pDesc, DMUS_OBJECTDESC);
|
|
|
|
ZeroMemory(pDesc, sizeof(DMUS_OBJECTDESC));
|
|
pDesc->dwSize = sizeof(DMUS_OBJECTDESC);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(1, "Error: Call of IDirectMusicScript::ParseDescriptor after the script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
SmartRef::CritSec CS(&m_CriticalSection);
|
|
|
|
// Read the script's header information
|
|
|
|
SmartRef::RiffIter riForm(pStream);
|
|
if (!riForm)
|
|
{
|
|
#ifdef DBG
|
|
if (SUCCEEDED(riForm.hr()))
|
|
{
|
|
Trace(2, "Error: ParseDescriptor on a script failed: Unexpected end of file. "
|
|
"(Note that this may be OK, such as when ScanDirectory is used to parse a set of unknown files, some of which are not scripts.)\n");
|
|
}
|
|
#endif
|
|
return SUCCEEDED(riForm.hr()) ? DMUS_E_SCRIPT_INVALID_FILE : riForm.hr();
|
|
}
|
|
HRESULT hr = riForm.FindRequired(SmartRef::RiffIter::Riff, DMUS_FOURCC_SCRIPT_FORM, DMUS_E_SCRIPT_INVALID_FILE);
|
|
if (FAILED(hr))
|
|
{
|
|
#ifdef DBG
|
|
if (hr == DMUS_E_SCRIPT_INVALID_FILE)
|
|
{
|
|
Trace(1, "Error: ParseDescriptor on a script failed: Form 'DMSC' not found. "
|
|
"(Note that this may be OK, such as when ScanDirectory is used to parse a set of unknown files, some of which are not scripts.)\n");
|
|
}
|
|
#endif
|
|
return hr;
|
|
}
|
|
|
|
SmartRef::RiffIter ri = riForm.Descend();
|
|
if (!ri)
|
|
return ri.hr();
|
|
|
|
hr = ri.LoadObjectInfo(&m_info.oinfo, SmartRef::RiffIter::Chunk, DMUS_FOURCC_SCRIPTVERSION_CHUNK);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = this->GetDescriptor(pDesc);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP_(void)
|
|
CDirectMusicScript::Zombie()
|
|
{
|
|
m_fZombie = true;
|
|
this->ReleaseObjects();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// IDirectMusicScript
|
|
|
|
STDMETHODIMP
|
|
CDirectMusicScript::Init(IDirectMusicPerformance *pPerformance, DMUS_SCRIPT_ERRORINFO *pErrorInfo)
|
|
{
|
|
V_INAME(CDirectMusicScript::Init);
|
|
V_INTERFACE(pPerformance);
|
|
V_PTR_WRITE_OPT(pErrorInfo, DMUS_SCRIPT_ERRORINFO);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(1, "Error: Call of IDirectMusicScript::Init after the script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
SmartRef::ComPtr<IDirectMusicPerformance8> scomPerformance8;
|
|
HRESULT hr = pPerformance->QueryInterface(IID_IDirectMusicPerformance8, reinterpret_cast<void **>(&scomPerformance8));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Don't take the critical section if the script is already initialized.
|
|
// For example, this is necessary in the following situation:
|
|
// - The critical section has already been taken by CallRoutine.
|
|
// - The routine played a segment with a script track referencing this script.
|
|
// - The script track calls Init (from a different thread) to make sure the script
|
|
// is initialized.
|
|
if (m_pPerformance8)
|
|
{
|
|
// Additional calls to Init are ignored.
|
|
// First call wins. Return S_FALSE if performance doesn't match.
|
|
if (m_pPerformance8 == scomPerformance8)
|
|
return S_OK;
|
|
else
|
|
return S_FALSE;
|
|
}
|
|
|
|
SmartRef::CritSec CS(&m_CriticalSection);
|
|
|
|
if (m_fInitError)
|
|
{
|
|
if (pErrorInfo)
|
|
{
|
|
// Syntax errors in a script occur as it is loaded, before SetDescriptor gives a script
|
|
// its filename. We'll have it after the load (before init is called) so can add it
|
|
// back in here.
|
|
if (m_InitErrorInfo.wszSourceFile[0] == L'\0' && m_info.wstrFilename)
|
|
wcsTruncatedCopy(m_InitErrorInfo.wszSourceFile, m_info.wstrFilename, DMUS_MAX_FILENAME);
|
|
|
|
CopySizedStruct(pErrorInfo, &m_InitErrorInfo);
|
|
}
|
|
|
|
return DMUS_E_SCRIPT_ERROR_IN_SCRIPT;
|
|
}
|
|
|
|
if (!m_info.fLoaded)
|
|
{
|
|
Trace(1, "Error: IDirectMusicScript::Init called before the script has been loaded.\n");
|
|
return DMUS_E_NOT_LOADED;
|
|
}
|
|
|
|
// Get the dispatch interface for the performance
|
|
SmartRef::ComPtr<IDispatch> scomDispPerformance = NULL;
|
|
hr = pPerformance->QueryInterface(IID_IDispatch, reinterpret_cast<void **>(&scomDispPerformance));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// Get a composer object
|
|
hr = CoCreateInstance(CLSID_DirectMusicComposer, NULL, CLSCTX_INPROC_SERVER, IID_IDirectMusicComposer8, reinterpret_cast<void **>(&m_pComposer8));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
m_pDispPerformance = scomDispPerformance.disown();
|
|
m_pPerformance8 = scomPerformance8.disown();
|
|
|
|
hr = m_pScriptManager->Start(pErrorInfo);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
hr = m_pContainerDispatch->OnScriptInit(m_pPerformance8);
|
|
return hr;
|
|
}
|
|
|
|
// Returns DMUS_E_SCRIPT_ROUTINE_NOT_FOUND if routine doesn't exist in the script.
|
|
STDMETHODIMP
|
|
CDirectMusicScript::CallRoutine(WCHAR *pwszRoutineName, DMUS_SCRIPT_ERRORINFO *pErrorInfo)
|
|
{
|
|
V_INAME(CDirectMusicScript::CallRoutine);
|
|
V_BUFPTR_READ(pwszRoutineName, 2);
|
|
V_PTR_WRITE_OPT(pErrorInfo, DMUS_SCRIPT_ERRORINFO);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(1, "Error: Call of IDirectMusicScript::CallRoutine after the script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
SmartRef::CritSec CS(&m_CriticalSection);
|
|
|
|
if (!m_pScriptManager || !m_pPerformance8)
|
|
{
|
|
Trace(1, "Error: IDirectMusicScript::Init must be called before IDirectMusicScript::CallRoutine.\n");
|
|
return DMUS_E_NOT_INIT;
|
|
}
|
|
|
|
return m_pScriptManager->CallRoutine(pwszRoutineName, pErrorInfo);
|
|
}
|
|
|
|
// Returns DMUS_E_SCRIPT_VARIABLE_NOT_FOUND if variable doesn't exist in the script.
|
|
STDMETHODIMP
|
|
CDirectMusicScript::SetVariableVariant(
|
|
WCHAR *pwszVariableName,
|
|
VARIANT varValue,
|
|
BOOL fSetRef,
|
|
DMUS_SCRIPT_ERRORINFO *pErrorInfo)
|
|
{
|
|
V_INAME(CDirectMusicScript::SetVariableVariant);
|
|
V_BUFPTR_READ(pwszVariableName, 2);
|
|
V_PTR_WRITE_OPT(pErrorInfo, DMUS_SCRIPT_ERRORINFO);
|
|
|
|
switch (varValue.vt)
|
|
{
|
|
case VT_BSTR:
|
|
V_BUFPTR_READ_OPT(varValue.bstrVal, sizeof(OLECHAR));
|
|
// We could be more thorough and verify each character until we hit the terminator but
|
|
// that would be inefficient. We could also use the length preceding a BSTR pointer,
|
|
// but that would be cheating COM's functions that encapsulate BSTRs and could lead to
|
|
// problems in future versions of windows such as 64 bit if the BSTR format changes.
|
|
break;
|
|
case VT_UNKNOWN:
|
|
V_INTERFACE_OPT(varValue.punkVal);
|
|
break;
|
|
case VT_DISPATCH:
|
|
V_INTERFACE_OPT(varValue.pdispVal);
|
|
break;
|
|
}
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(1, "Error: Call of IDirectMusicScript::SetVariableObject/SetVariableNumber/SetVariableVariant after the script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
SmartRef::CritSec CS(&m_CriticalSection);
|
|
|
|
if (!m_pScriptManager || !m_pPerformance8)
|
|
{
|
|
Trace(1, "Error: IDirectMusicScript::Init must be called before IDirectMusicScript::SetVariableVariant.\n");
|
|
return DMUS_E_NOT_INIT;
|
|
}
|
|
|
|
HRESULT hr = m_pScriptManager->SetVariable(pwszVariableName, varValue, !!fSetRef, pErrorInfo);
|
|
if (hr == DMUS_E_SCRIPT_VARIABLE_NOT_FOUND)
|
|
{
|
|
// There are also items in the script's container that the m_pScriptManager object isn't available.
|
|
// If that's the case, we should return a more specific error message.
|
|
IUnknown *punk = NULL;
|
|
hr = m_pContainerDispatch->GetVariableObject(pwszVariableName, &punk);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// We don't actually need the object--it can't be set. Just needed to find out if it's there
|
|
// in order to return a more specific error message.
|
|
punk->Release();
|
|
return DMUS_E_SCRIPT_CONTENT_READONLY;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// Returns DMUS_E_SCRIPT_VARIABLE_NOT_FOUND and empty value if variable doesn't exist in the script.
|
|
// Certain varient types such as BSTRs and interface pointers must be freed/released according to the standards for VARIANTS.
|
|
// If unsure, use VariantClear (requires oleaut32).
|
|
STDMETHODIMP
|
|
CDirectMusicScript::GetVariableVariant(WCHAR *pwszVariableName, VARIANT *pvarValue, DMUS_SCRIPT_ERRORINFO *pErrorInfo)
|
|
{
|
|
V_INAME(CDirectMusicScript::GetVariableVariant);
|
|
V_BUFPTR_READ(pwszVariableName, 2);
|
|
V_PTR_WRITE(pvarValue, VARIANT);
|
|
V_PTR_WRITE_OPT(pErrorInfo, DMUS_SCRIPT_ERRORINFO);
|
|
|
|
DMS_VariantInit(m_fUseOleAut, pvarValue);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(1, "Error: Call of IDirectMusicScript::GetVariableObject/GetVariableNumber/GetVariableVariant after the script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
SmartRef::CritSec CS(&m_CriticalSection);
|
|
|
|
if (!m_pScriptManager || !m_pPerformance8)
|
|
{
|
|
Trace(1, "Error: IDirectMusicScript::Init must be called before IDirectMusicScript::GetVariableVariant.\n");
|
|
return DMUS_E_NOT_INIT;
|
|
}
|
|
|
|
HRESULT hr = m_pScriptManager->GetVariable(pwszVariableName, pvarValue, pErrorInfo);
|
|
|
|
if (hr == DMUS_E_SCRIPT_VARIABLE_NOT_FOUND)
|
|
{
|
|
// There are also items in the script's container that we need to return.
|
|
// This is implemented by the container, which returns the IUnknown pointer directly rather than through a variant.
|
|
IUnknown *punk = NULL;
|
|
hr = m_pContainerDispatch->GetVariableObject(pwszVariableName, &punk);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pvarValue->vt = VT_UNKNOWN;
|
|
pvarValue->punkVal = punk;
|
|
}
|
|
}
|
|
|
|
#ifdef DBG
|
|
if (hr == DMUS_E_SCRIPT_VARIABLE_NOT_FOUND)
|
|
{
|
|
Trace(1, "Error: Attempt to get variable '%S' that is not defined in the script.\n", pwszVariableName);
|
|
}
|
|
#endif
|
|
|
|
if (!m_fUseOleAut && pvarValue->vt == VT_BSTR)
|
|
{
|
|
// m_fUseOleAut is false when we're using our own custom scripting engine that avoids
|
|
// depending on oleaut32.dll. But in this case we're returning a BSTR variant to the
|
|
// caller. We have to allocate this string with SysAllocString (from oleaut32)
|
|
// because the caller is going to free it with SysFreeString--the standard thing to
|
|
// do with a variant BSTR.
|
|
BSTR bstrOle = DMS_SysAllocString(true, pvarValue->bstrVal); // allocate a copy with oleaut
|
|
DMS_SysFreeString(false, pvarValue->bstrVal); // free the previous value (allocated without oleaut)
|
|
pvarValue->bstrVal = bstrOle; // return the oleaut string to the user
|
|
if (!bstrOle)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Returns DMUS_E_SCRIPT_VARIABLE_NOT_FOUND if variable doesn't exist in the script.
|
|
STDMETHODIMP
|
|
CDirectMusicScript::SetVariableNumber(WCHAR *pwszVariableName, LONG lValue, DMUS_SCRIPT_ERRORINFO *pErrorInfo)
|
|
{
|
|
VARIANT var;
|
|
var.vt = VT_I4;
|
|
var.lVal = lValue;
|
|
return this->SetVariableVariant(pwszVariableName, var, false, pErrorInfo);
|
|
}
|
|
|
|
// Returns DMUS_E_SCRIPT_VARIABLE_NOT_FOUND and 0 if variable doesn't exist in the script.
|
|
// Returns DISP_E_TYPEMISMATCH if variable's datatype cannot be converted to LONG.
|
|
STDMETHODIMP
|
|
CDirectMusicScript::GetVariableNumber(WCHAR *pwszVariableName, LONG *plValue, DMUS_SCRIPT_ERRORINFO *pErrorInfo)
|
|
{
|
|
V_INAME(CDirectMusicScript::GetVariableNumber);
|
|
V_PTR_WRITE(plValue, LONG);
|
|
*plValue = 0;
|
|
|
|
VARIANT var;
|
|
HRESULT hr = this->GetVariableVariant(pwszVariableName, &var, pErrorInfo);
|
|
if (FAILED(hr) || hr == S_FALSE || hr == DMUS_S_GARBAGE_COLLECTED)
|
|
return hr;
|
|
|
|
hr = DMS_VariantChangeType(m_fUseOleAut, &var, &var, 0, VT_I4);
|
|
if (SUCCEEDED(hr))
|
|
*plValue = var.lVal;
|
|
|
|
// GetVariableVariant forces a BSTR to be allocated with SysAllocString;
|
|
// so if we allocated a BSTR there, we need to free it with SysAllocString here.
|
|
bool fUseOleAut = m_fUseOleAut;
|
|
if (!m_fUseOleAut && var.vt == VT_BSTR)
|
|
{
|
|
fUseOleAut = true;
|
|
}
|
|
DMS_VariantClear(fUseOleAut, &var);
|
|
return hr;
|
|
}
|
|
|
|
// Returns DMUS_E_SCRIPT_VARIABLE_NOT_FOUND if variable doesn't exist in the script.
|
|
STDMETHODIMP
|
|
CDirectMusicScript::SetVariableObject(WCHAR *pwszVariableName, IUnknown *punkValue, DMUS_SCRIPT_ERRORINFO *pErrorInfo)
|
|
{
|
|
VARIANT var;
|
|
var.vt = VT_UNKNOWN;
|
|
var.punkVal = punkValue;
|
|
return this->SetVariableVariant(pwszVariableName, var, true, pErrorInfo);
|
|
}
|
|
|
|
// Returns DMUS_E_SCRIPT_VARIABLE_NOT_FOUND and NULL if variable doesn't exist in the script.
|
|
// Returns DISP_E_TYPEMISMATCH if variable's datatype cannot be converted to IUnknown.
|
|
STDMETHODIMP
|
|
CDirectMusicScript::GetVariableObject(WCHAR *pwszVariableName, REFIID riid, LPVOID FAR *ppv, DMUS_SCRIPT_ERRORINFO *pErrorInfo)
|
|
{
|
|
V_INAME(CDirectMusicScript::GetVariableObject);
|
|
V_PTR_WRITE(ppv, IUnknown *);
|
|
*ppv = NULL;
|
|
|
|
VARIANT var;
|
|
HRESULT hr = this->GetVariableVariant(pwszVariableName, &var, pErrorInfo);
|
|
if (FAILED(hr) || hr == DMUS_S_GARBAGE_COLLECTED)
|
|
return hr;
|
|
|
|
hr = DMS_VariantChangeType(m_fUseOleAut, &var, &var, 0, VT_UNKNOWN);
|
|
if (SUCCEEDED(hr))
|
|
hr = var.punkVal->QueryInterface(riid, ppv);
|
|
DMS_VariantClear(m_fUseOleAut, &var);
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CDirectMusicScript::EnumRoutine(DWORD dwIndex, WCHAR *pwszName)
|
|
{
|
|
V_INAME(CDirectMusicScript::EnumRoutine);
|
|
V_BUFPTR_WRITE(pwszName, MAX_PATH);
|
|
|
|
*pwszName = L'\0';
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(1, "Error: Call of IDirectMusicScript::EnumRoutine after the script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
if (!m_pScriptManager || !m_pPerformance8)
|
|
{
|
|
Trace(1, "Error: IDirectMusicScript::Init must be called before IDirectMusicScript::EnumRoutine.\n");
|
|
return DMUS_E_NOT_INIT;
|
|
}
|
|
|
|
return m_pScriptManager->EnumItem(true, dwIndex, pwszName, NULL);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CDirectMusicScript::EnumVariable(DWORD dwIndex, WCHAR *pwszName)
|
|
{
|
|
V_INAME(CDirectMusicScript::EnumRoutine);
|
|
V_BUFPTR_WRITE(pwszName, MAX_PATH);
|
|
|
|
*pwszName = L'\0';
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(1, "Error: Call of IDirectMusicScript::EnumVariable after the script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
if (!m_pScriptManager || !m_pPerformance8)
|
|
{
|
|
Trace(1, "Error: IDirectMusicScript::Init must be called before IDirectMusicScript::EnumVariable.\n");
|
|
return DMUS_E_NOT_INIT;
|
|
}
|
|
|
|
int cScriptItems = 0;
|
|
HRESULT hr = m_pScriptManager->EnumItem(false, dwIndex, pwszName, &cScriptItems);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (hr == S_FALSE)
|
|
{
|
|
// There are also items in the script's container that we need to report.
|
|
assert(dwIndex >= cScriptItems);
|
|
hr = m_pContainerDispatch->EnumItem(dwIndex - cScriptItems, pwszName);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CDirectMusicScript::ScriptTrackCallRoutine(
|
|
WCHAR *pwszRoutineName,
|
|
IDirectMusicSegmentState *pSegSt,
|
|
DWORD dwVirtualTrackID,
|
|
bool fErrorPMsgsEnabled,
|
|
__int64 i64IntendedStartTime,
|
|
DWORD dwIntendedStartTimeFlags)
|
|
{
|
|
V_INAME(CDirectMusicScript::CallRoutine);
|
|
V_BUFPTR_READ(pwszRoutineName, 2);
|
|
V_INTERFACE(pSegSt);
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(1, "Error: Script track attempted to call a routine after the script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
SmartRef::CritSec CS(&m_CriticalSection);
|
|
|
|
if (!m_pScriptManager || !m_pPerformance8)
|
|
{
|
|
Trace(1, "Error: Unitialized Script elements in an attempt to call a Script Routine.\n");
|
|
return DMUS_E_NOT_INIT;
|
|
}
|
|
|
|
return m_pScriptManager->ScriptTrackCallRoutine(
|
|
pwszRoutineName,
|
|
pSegSt,
|
|
dwVirtualTrackID,
|
|
fErrorPMsgsEnabled,
|
|
i64IntendedStartTime,
|
|
dwIntendedStartTimeFlags);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CDirectMusicScript::GetTypeInfoCount(UINT *pctinfo)
|
|
{
|
|
V_INAME(CDirectMusicScript::GetTypeInfoCount);
|
|
V_PTR_WRITE(pctinfo, UINT);
|
|
*pctinfo = 0;
|
|
|
|
if (m_fZombie)
|
|
{
|
|
Trace(1, "Error: Call of IDirectMusicScript::GetTypeInfoCount after the script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CDirectMusicScript::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo __RPC_FAR *__RPC_FAR *ppTInfo)
|
|
{
|
|
*ppTInfo = NULL;
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CDirectMusicScript::GetIDsOfNames(
|
|
REFIID riid,
|
|
LPOLESTR __RPC_FAR *rgszNames,
|
|
UINT cNames,
|
|
LCID lcid,
|
|
DISPID __RPC_FAR *rgDispId)
|
|
{
|
|
if (m_fZombie)
|
|
{
|
|
if (rgDispId)
|
|
{
|
|
for (int i = 0; i < cNames; ++i)
|
|
{
|
|
rgDispId[i] = DISPID_UNKNOWN;
|
|
}
|
|
}
|
|
Trace(1, "Error: Call of GetIDsOfNames after a script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
if (!m_pScriptManager || !m_pPerformance8)
|
|
{
|
|
Trace(1, "Error: IDirectMusicScript::Init must be called before GetIDsOfNames.\n");
|
|
return DMUS_E_NOT_INIT;
|
|
}
|
|
|
|
return m_pScriptManager->DispGetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CDirectMusicScript::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 (m_fZombie)
|
|
{
|
|
if (pVarResult)
|
|
DMS_VariantInit(m_fUseOleAut, pVarResult);
|
|
Trace(1, "Error: Call of Invoke after the script has been garbage collected. "
|
|
"It is invalid to continue using a script after releasing it from the loader (ReleaseObject/ReleaseObjectByUnknown) "
|
|
"and then calling CollectGarbage or Release on the loader.");
|
|
return DMUS_S_GARBAGE_COLLECTED;
|
|
}
|
|
|
|
if (!m_pScriptManager || !m_pPerformance8)
|
|
{
|
|
Trace(1, "Error: IDirectMusicScript::Init must be called before Invoke.\n");
|
|
return DMUS_E_NOT_INIT;
|
|
}
|
|
|
|
return m_pScriptManager->DispInvoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Methods that allow CActiveScriptManager access to private script interfaces
|
|
|
|
IDispatch *CDirectMusicScript::GetGlobalDispatch()
|
|
{
|
|
assert(m_pGlobalDispatch);
|
|
return m_pGlobalDispatch;
|
|
}
|