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

451 lines
10 KiB
C++

// Copyright (c) 1999 Microsoft Corporation. All rights reserved.
//
// Implementation of EngineDispatch.
//
#include "stdinc.h"
#include "enginc.h"
#include "engdisp.h"
#include "limits"
#include "oleaut.h"
//////////////////////////////////////////////////////////////////////
// Global constants
const DISPID g_dispidFirstRoutine = 1;
const DISPID g_dispidFirstGlobal = 1000001;
//////////////////////////////////////////////////////////////////////
// ASCII comparison of WCHAR and char strings
bool wcsstrimatch(const WCHAR *pwsz, const char *pasz)
{
for (;;)
{
if (*pwsz > std::numeric_limits<char>::max())
return false;
char ch1 = (char)tolower((char)*pwsz++); // §§ make sure tolower is the right kind of function
char ch2 = (char)tolower((char)*pasz++);
if (ch1 != ch2)
return false;
if (!ch1)
return true;
}
}
//////////////////////////////////////////////////////////////////////
// Creation
EngineDispatch::EngineDispatch(IUnknown *punkParent, Script &script, IDispatch *pGlobalDispatch)
: m_cRef(1),
m_scomParent(punkParent),
m_script(script),
m_exec(script, pGlobalDispatch)
{
punkParent->AddRef();
}
//////////////////////////////////////////////////////////////////////
// IUnknown
STDMETHODIMP
EngineDispatch::QueryInterface(const IID &iid, void **ppv)
{
V_INAME(EngineDispatch::QueryInterface);
V_PTRPTR_WRITE(ppv);
V_REFGUID(iid);
if (iid == IID_IUnknown || iid == IID_IDispatch)
{
*ppv = static_cast<IDispatch*>(this);
}
else
{
*ppv = NULL;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown*>(this)->AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG)
EngineDispatch::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG)
EngineDispatch::Release()
{
if (!InterlockedDecrement(&m_cRef))
{
delete this;
return 0;
}
return m_cRef;
}
//////////////////////////////////////////////////////////////////////
// IDispatch
STDMETHODIMP
EngineDispatch::GetTypeInfoCount(UINT *pctinfo)
{
V_INAME(EngineDispatch::GetTypeInfoCount);
V_PTR_WRITE(pctinfo, *pctinfo);
*pctinfo = 1;
return S_OK;
}
STDMETHODIMP
EngineDispatch::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
{
V_INAME(EngineDispatch::GetTypeInfo);
V_PTR_WRITE(ppTInfo, *ppTInfo);
if (iTInfo != 0)
return DISP_E_BADINDEX;
*ppTInfo = static_cast<ITypeInfo *>(this);
this->AddRef();
return S_OK;
}
STDMETHODIMP
EngineDispatch::GetIDsOfNames(
REFIID riid,
LPOLESTR *rgszNames,
UINT cNames,
LCID lcid,
DISPID *rgDispId)
{
V_INAME(EngineDispatch::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;
}
// §§ Possible optimization: sort the routines/globals so that we can bsearch for names.
// See if we have a routine with the first name
Routines::index irtnLast = m_script.routines.Next();
for (Routines::index irtn = 0; irtn < irtnLast; ++irtn)
{
if (wcsstrimatch(rgszNames[0], m_script.strings[m_script.routines[irtn].istrIdentifier]))
{
rgDispId[0] = g_dispidFirstRoutine + irtn;
break;
}
}
if (rgDispId[0] == DISPID_UNKNOWN)
{
// See if we have a global variable with the first name
Variables::index ivarLast = m_script.globals.Next();
for (Variables::index ivar = g_cBuiltInConstants; ivar < ivarLast; ++ivar)
{
Variable &variable = m_script.globals[ivar];
if (variable.dispid == DISPID_UNKNOWN && // variable must be in script (not member of global dispatch)
wcsstrimatch(rgszNames[0], m_script.strings[variable.istrIdentifier]))
{
rgDispId[0] = g_dispidFirstGlobal + ivar;
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;
}
STDMETHODIMP
EngineDispatch::Invoke(
DISPID dispIdMember,
REFIID riid,
LCID lcid,
WORD wFlags,
DISPPARAMS *pDispParams,
VARIANT *pVarResult,
EXCEPINFO *pExcepInfo,
UINT *puArgErr)
{
V_INAME(EngineDispatch::Invoke);
V_PTR_READ(pDispParams, DISPPARAMS);
V_PTR_WRITE_OPT(pVarResult, VARIANT);
V_PTR_WRITE_OPT(puArgErr, UINT);
// Additional parameter validation
bool fReturnValueUsingOleAut = g_fUseOleAut || riid != g_guidInvokeWithoutOleaut;
// This is true unless g_fUseOleAut is false (script engine is set to always use oleaut32.dll
// and riid is g_guidInvokeWithoutOleaut (caller expects this not to return values allocated with
// oleaut32.dll. See oleaut.h for more info.
if (fReturnValueUsingOleAut && riid != IID_NULL)
{
return DISP_E_UNKNOWNINTERFACE;
}
// Zero the out params
if (puArgErr)
*puArgErr = 0;
HRESULT hr = S_OK;
if (dispIdMember < g_dispidFirstGlobal)
{
// it's a routine
if (!(wFlags & DISPATCH_METHOD))
return DISP_E_MEMBERNOTFOUND;
Routines::index irtn = dispIdMember - g_dispidFirstRoutine;
if (irtn >= m_script.routines.Next())
return DISP_E_MEMBERNOTFOUND;
if (pDispParams->cArgs > 0)
return DISP_E_BADPARAMCOUNT;
if (pDispParams->cNamedArgs > 0)
return DISP_E_NONAMEDARGS;
if (pVarResult)
{
assert(false);
return E_UNEXPECTED;
}
hr = m_exec.ExecRoutine(irtn, pExcepInfo);
}
else
{
// it's a global variable
Variables::index ivar = dispIdMember - g_dispidFirstGlobal;
if (ivar >= m_script.globals.Next())
return DISP_E_MEMBERNOTFOUND;
if (wFlags & DISPATCH_PROPERTYGET)
{
if (pDispParams->cArgs > 0)
return DISP_E_BADPARAMCOUNT;
if (pDispParams->cNamedArgs > 0)
return DISP_E_NONAMEDARGS;
if (pVarResult)
{
DMS_VariantInit(fReturnValueUsingOleAut, pVarResult);
DMS_VariantCopy(fReturnValueUsingOleAut, pVarResult, &m_exec.GetGlobal(ivar));
}
return S_OK;
}
else
{
if (!(wFlags & (DISPATCH_PROPERTYPUTREF | DISPATCH_PROPERTYPUT)))
return DISP_E_MEMBERNOTFOUND;
bool fPutRef = !!(wFlags & DISPATCH_PROPERTYPUTREF);
assert(fPutRef || wFlags & DISPATCH_PROPERTYPUT);
if (pDispParams->cArgs != 1)
return DISP_E_BADPARAMCOUNT;
if (pDispParams->cNamedArgs != 1)
return DISP_E_BADPARAMCOUNT;
if (*pDispParams->rgdispidNamedArgs != DISPID_PROPERTYPUT)
return DISP_E_PARAMNOTFOUND;
if (pVarResult)
return E_INVALIDARG;
hr = m_exec.SetGlobal(ivar, pDispParams->rgvarg[0], fPutRef, pExcepInfo);
}
}
// If an exception occurred, we need to convert the error strings into our own kind of BSTR.
if (hr == DISP_E_EXCEPTION)
ConvertOleAutExceptionBSTRs(false, fReturnValueUsingOleAut, pExcepInfo);
return hr;
}
//////////////////////////////////////////////////////////////////////
// ITypeInfo
HRESULT STDMETHODCALLTYPE
EngineDispatch::GetTypeAttr(
/* [out] */ TYPEATTR **ppTypeAttr)
{
V_INAME(EngineDispatch::GetTypeAttr);
V_PTR_WRITE(ppTypeAttr, *ppTypeAttr);
*ppTypeAttr = new TYPEATTR;
if (!*ppTypeAttr)
return E_OUTOFMEMORY;
Zero(*ppTypeAttr);
(*ppTypeAttr)->cFuncs = (unsigned short)m_script.routines.Next();
// Count the global variables -- necessary because some are on the global dispatch
// and we don't want to report them.
int cVars = 0;
Variables::index iLastGlobal = m_script.globals.Next();
for (Variables::index iGlobal = g_cBuiltInConstants; iGlobal < iLastGlobal; ++iGlobal)
{
if (m_script.globals[iGlobal].dispid == DISPID_UNKNOWN)
++cVars;
}
(*ppTypeAttr)->cVars = (unsigned short)cVars;
return S_OK;
}
void STDMETHODCALLTYPE
EngineDispatch::ReleaseTypeAttr(
/* [in] */ TYPEATTR *pTypeAttr)
{
assert(!IsBadReadPtr(pTypeAttr, sizeof(*pTypeAttr)));
delete pTypeAttr;
}
HRESULT STDMETHODCALLTYPE
EngineDispatch::GetFuncDesc(
/* [in] */ UINT index,
/* [out] */ FUNCDESC **ppFuncDesc)
{
V_INAME(EngineDispatch::GetFuncDesc);
V_PTR_WRITE(ppFuncDesc, *ppFuncDesc);
if (index >= m_script.routines.Next())
return E_INVALIDARG;
*ppFuncDesc = new FUNCDESC;
if (!*ppFuncDesc)
return E_OUTOFMEMORY;
Zero(*ppFuncDesc);
(*ppFuncDesc)->funckind = FUNC_DISPATCH;
(*ppFuncDesc)->invkind = INVOKE_FUNC;
(*ppFuncDesc)->cParams = 0;
(*ppFuncDesc)->memid = index + g_dispidFirstRoutine;
return S_OK;
}
void STDMETHODCALLTYPE
EngineDispatch::ReleaseFuncDesc(
/* [in] */ FUNCDESC *pFuncDesc)
{
assert(!IsBadReadPtr(pFuncDesc, sizeof(*pFuncDesc)));
delete pFuncDesc;
}
HRESULT STDMETHODCALLTYPE
EngineDispatch::GetVarDesc(
/* [in] */ UINT index,
/* [out] */ VARDESC **ppVarDesc)
{
V_INAME(EngineDispatch::GetVarDesc);
V_PTR_WRITE(ppVarDesc, *ppVarDesc);
// Count until we find the global (non-dispatch-based) variable at the index position.
UINT cFuncs = 0;
Variables::index iLastGlobal = m_script.globals.Next();
for (Variables::index iGlobal = g_cBuiltInConstants; iGlobal < iLastGlobal; ++iGlobal)
{
if (m_script.globals[iGlobal].dispid == DISPID_UNKNOWN)
{
if (cFuncs == index)
break;
else
++cFuncs;
}
}
if (cFuncs < index)
{
// there aren't that many variables
return E_INVALIDARG;
}
*ppVarDesc = new VARDESC;
if (!*ppVarDesc)
return E_OUTOFMEMORY;
Zero(*ppVarDesc);
(*ppVarDesc)->varkind = VAR_DISPATCH;
(*ppVarDesc)->memid = iGlobal + g_dispidFirstGlobal;
return S_OK;
}
void STDMETHODCALLTYPE
EngineDispatch::ReleaseVarDesc(
/* [in] */ VARDESC *pVarDesc)
{
assert(!IsBadReadPtr(pVarDesc, sizeof(*pVarDesc)));
delete pVarDesc;
}
HRESULT STDMETHODCALLTYPE
EngineDispatch::GetNames(
/* [in] */ MEMBERID memid,
/* [length_is][size_is][out] */ BSTR *rgBstrNames,
/* [in] */ UINT cMaxNames,
/* [out] */ UINT *pcNames)
{
V_INAME(EngineDispatch::GetNames);
if (memid < g_dispidFirstRoutine)
return E_INVALIDARG;
V_PTR_WRITE(rgBstrNames, *rgBstrNames);
if (cMaxNames != 1)
return E_INVALIDARG;
V_PTR_WRITE(pcNames, *pcNames);
assert(g_dispidFirstRoutine < g_dispidFirstGlobal);
Strings::index iStr = 0;
if (memid < g_dispidFirstGlobal)
{
const int iSlot = memid - g_dispidFirstRoutine;
if (iSlot >= m_script.routines.Next())
return E_INVALIDARG;
iStr = m_script.routines[iSlot].istrIdentifier;
}
else
{
const int iSlot = memid - g_dispidFirstGlobal;
if (iSlot >= m_script.globals.Next())
return E_INVALIDARG;
iStr = m_script.globals[iSlot].istrIdentifier;
}
SmartRef::WString wstrName = m_script.strings[iStr];
if (!wstrName)
return E_OUTOFMEMORY;
*rgBstrNames = DMS_SysAllocString(g_fUseOleAut, wstrName);
if (!*rgBstrNames)
return E_OUTOFMEMORY;
*pcNames = 1;
return S_OK;
}