binder.cpp

#include "headers.h"

Binder::Binder()
{
DLLAddRef();
this->m_cref = 1;
this->m_thread = GetCurrentThreadId();
}

Binder::~Binder(void)
{
DLLRelease();
}

HRESULT Binder::Create(Binder * * ppBinder)
{
AssertOutPtr(ppBinder);

    *ppBinder = new Binder();
if (NULL == *ppBinder)
return E_OUTOFMEMORY;

    return S_OK;
}

// IUnknown

STDMETHODIMP_(ULONG) Binder::AddRef(void)
{
return InterlockedIncrement(&this->m_cref);
}

STDMETHODIMP_(ULONG) Binder::Release(void)
{
long cref = InterlockedDecrement(&this->m_cref);
if (0 == cref)
delete this;
return cref;
}

STDMETHODIMP Binder::QueryInterface(REFIID riid, void * * ppv)
{
if (NULL == ppv)
{
Bug("Null out pointer");
return E_POINTER;
}

AssertOutPtr(ppv);

    *ppv = NULL;

    if (IsEqualIID(riid, IID_IUnknown))
*ppv = (IUnknown *)(IDispatch *) this;
else if (IsEqualIID(riid, IID_IDispatch))
*ppv = (IDispatch *) this;
else
return E_NOINTERFACE;

    this->AddRef();
return S_OK;
}

// IDispatch

STDMETHODIMP Binder::GetTypeInfoCount(UINT * pcTypeInfo)
{
if (NULL == pcTypeInfo)
{
Bug("Null out pointer");
return E_POINTER;
}

    *pcTypeInfo = 1;
return S_OK;
}

STDMETHODIMP Binder::GetTypeInfo(UINT iTypeInfo, LCID lcid, ITypeInfo * * ppTypeInfo)
{
HRESULT hr;
// TypeInfoBuilder * pTypeInfoBuilder = NULL;

    hr = this->VerifyThread();
if (FAILED(hr))
goto LError;

    if (NULL == ppTypeInfo)
{
Bug("Null out pointer");
hr = E_POINTER;
goto LError;
}

    if (0 != iTypeInfo)
{
Bug("We only have one type info -- why are you asking for more?");
hr = DISP_E_BADINDEX;
goto LError;
}

    hr = E_NOTIMPL;

// hr = TypeInfoBuilder::Create(lcid, &pTypeInfoBuilder);
// if (FAILED(hr))
// goto LError;
//
// hr = pTypeInfoBuilder->AddBinder(this);
// if (FAILED(hr))
// goto LError;
//
// hr = pTypeInfoBuilder->GetTypeInfo(ppTypeInfo);
// if (FAILED(hr))
// goto LError;
//
// hr = S_OK;
//
LError:

// if (NULL != pTypeInfoBuilder)
// pTypeInfoBuilder->Release();

    return hr;
}

STDMETHODIMP Binder::GetIDsOfNames(REFIID riid, WCHAR * * rgpszNames, UINT cNames,
LCID lcid, DISPID * rgdispids)
{
// The first name is the name of a property or method. The rest are names
// of arguments to the method. Since we do not support invocation via named
// parameters, we'll decline to identify additional names.

HRESULT hr;
UINT iName;

    hr = this->VerifyThread();
if (FAILED(hr))
goto LError;

    if (IID_NULL != riid)
{
return hr= DISP_E_UNKNOWNINTERFACE;
goto LError;
}

    if (NULL == rgdispids)
{
Bug("Bad out pointer");
hr = E_POINTER;
goto LError;
}

    if (0 == cNames)
{
Bug("Why are you asking for ids of an empty list of names?");
hr = DISP_E_UNKNOWNNAME;
goto LError;
}

    for (iName = 0 ; iName < cNames; ++iName)
rgdispids[iName] = DISPID_UNKNOWN;

    WCHAR * pszName = rgpszNames[0];
if (NULL == pszName)
{
Bug("Bad string argument");
hr = E_POINTER;
goto LError;
}

    hr = this->GetIdOfName(pszName, &rgdispids[0]);
if (FAILED(hr))
goto LError;

    if (cNames > 1)
{
hr = DISP_E_UNKNOWNNAME;
goto LError;
}

    hr = S_OK;

LError:

    return hr;
}

STDMETHODIMP Binder::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD flags,
DISPPARAMS * pDispParams, VARIANT * pvarResult, EXCEPINFO * pExcepInfo,
UINT * pError)
{
HRESULT hr;
Name * pName;

    // The number of ways you can call Invoke wrong is enormous. Let's
// check most of them up front, after we null out the return values.

    if (NULL != pvarResult)
pvarResult->vt = VT_EMPTY;

    if (NULL != pExcepInfo)
memset(pExcepInfo, 0x00, sizeof EXCEPINFO);

    if (NULL != pError)
pError = 0;

    hr = this->VerifyThread();
if (FAILED(hr))
goto LError;

    if (IID_NULL != riid)
{
hr = DISP_E_UNKNOWNINTERFACE;
goto LError;
}

    if (NULL == pDispParams)
{
Bug("Null dispatch parameters.");
hr = E_POINTER;
goto LError;
}

    if (pDispParams->cArgs < pDispParams->cNamedArgs)
{
Bug("More named arguments than arguments!");
hr = E_INVALIDARG;
goto LError;
}

    if (0 == (flags & (DISPATCH_PROPERTYGET | DISPATCH_PROPERTYPUT |
DISPATCH_PROPERTYPUTREF | DISPATCH_METHOD)))
{
Bug("Must pass in at least one flag.");
hr = E_INVALIDARG;
goto LError;
}

    if (0 != (flags & ~(DISPATCH_PROPERTYGET | DISPATCH_PROPERTYPUT |
DISPATCH_PROPERTYPUTREF | DISPATCH_METHOD)))
{
Bug("Unsupported flag.");
hr = E_INVALIDARG;
goto LError;
}

    BOOL fCall = (0 != (flags & DISPATCH_METHOD));
BOOL fGet = (0 != (flags & DISPATCH_PROPERTYGET));
BOOL fPut = (0 != (flags & (DISPATCH_PROPERTYPUT | DISPATCH_PROPERTYPUTREF)));

    if (fPut && fCall)
{
Bug("A property put is not a method call.");
hr = E_INVALIDARG;
goto LError;
}

    if (fPut && fGet)
{
Bug("Cannot mix property put and property get.");
hr = E_INVALIDARG;
goto LError;
}

    if (fPut && (0 == pDispParams->cNamedArgs ||
DISPID_PROPERTYPUT != pDispParams->rgdispidNamedArgs[0]))
{
Bug("Must supply exactly one named argument when assigning a property.");
hr = DISP_E_PARAMNOTOPTIONAL;
goto LError;
}

    if (fPut && (1 != pDispParams->cNamedArgs))
{
Bug("We don't support multiple named arguments to property puts.")
hr = E_INVALIDARG;
goto LError;
}

    if (!fPut && (0 != pDispParams->cNamedArgs))
{
Bug("We don't support named arguments to except to property puts.")
hr = E_INVALIDARG;
goto LError;
}

    if (dispid < 0)
{
Bug("We do not support any 'special' dispatch ids.");
hr = DISP_E_MEMBERNOTFOUND;
goto LError;
}

// This code is going to get a little weird, but it does work out in the end.
//
// I hope.
//
// What we're going to do here is first take care of all the cases where
// we're calling the default property of an object. Then we'll take care
// of all property puts, then all calls to functions. At that point,
// we've exhausted everything that makes sense to have an arguments list,
// so the only thing left is property gets.
//
// I'll spell it out in more detail as we go.

    // You think this code is crazy, you should see the actual JScript/VBScript
// implementations -- which support garbage collection, property accessor
// functions and arrays called like methods!

    hr = this->GetNameById(dispid, &pName);
if (FAILED(hr))
goto LError;

// Case 1: We're doing some kind of function call on an object: either
//
// (a) a property get with arguments, or
// (b) a function call which is not a property get, or
// (c) a property put with arguments
//
// and the value associated with the dispid is a valid object.
//
// We simply defer to the object's implementation of Invoke and return.

    if (IsValidDispatch(&pName->m_var) &&
((fGet && 0 != pDispParams->cArgs) || (fCall && !fGet) ||
(fPut && 1 != pDispParams->cArgs)))
{
hr = InvokeDispatch(pName->m_var.pdispVal, DISPID_VALUE, IID_NULL, lcid,
flags, pDispParams, pvarResult, pExcepInfo, pError);
goto LError;
}

    // Case 2:
// (a) We're doing a property put with arguments, but since case 1 didn't
// handle it, this must not be a dispatch method. Error out.
// (b) We're doing a property put without arguments. Assign the property

    if (fPut)
{
if (pDispParams->cArgs > 1)
{
hr = DISP_E_TYPEMISMATCH;
goto LError;
}

        hr = pName->SetValue(&pDispParams->rgvarg[0]);
goto LError;
}

    // Case 3: We're doing a function call on a function.

    if (pName->IsFunction())
{
if (!fCall)
{
hr = DISP_E_TYPEMISMATCH;
goto LError;
}

        hr = pName->ExecuteFunction(pDispParams->cArgs, pDispParams->rgvarg, pvarResult);
goto LError;
}

    // Case 4: We've got arguments, but this isn't a dispatch object or
// a function. What the heck?

    if (0 != pDispParams->cArgs)
{
hr = DISP_E_TYPEMISMATCH;
goto LError;
}

    // Case 5: We're doing a property get with no arguments. Just return
// the value. (We can't be calling a function here.) Failing to
// provide room for the return value is dumb, but not illegal.

    if (fGet)
{
if (NULL == pvarResult)
hr = S_OK;
else
hr = pName->GetValue(pvarResult);
goto LError;
}

    // Case 6: Something has gone terribly wrong.

    hr = E_INVALIDARG;

LError:

    return hr;
}

HRESULT Binder::VerifyThread(void)
{
if (this->m_thread != GetCurrentThreadId())
{
Bug("The host is in violation of the script engine threading contract. "
"A script dispatch object is STA threaded.");
return E_UNEXPECTED;
}
return S_OK;
}

HRESULT Binder::GetIdOfName(const WCHAR * pszName, DISPID * pdispid)
{
AssertOutPtr(pdispid);

    return E_NOTIMPL;
}

HRESULT Binder::GetNameById(DISPID dispid, Name * * ppName)
{
AssertOutPtr(ppName);

    return E_NOTIMPL;
}

Binder::Name::Name()
{
this->m_var.vt = VT_EMPTY;
}

Binder::Name::~Name()
{
VariantClear(&this->m_var);
}

HRESULT Binder::Name::SetValue(VARIANTARG * pvar)
{
AssertReadPtr(pvar);
return VariantCopyInd(&this->m_var, pvar);
}

HRESULT Binder::Name::GetValue(VARIANT * pvar)
{
AssertOutPtr(pvar);
return VariantCopy(pvar, &this->m_var);
}

BOOL Binder::Name::IsFunction(void)
{
// UNDONE
return FALSE;
}

HRESULT Binder::Name::ExecuteFunction(UINT cArgs, VARIANTARG * rgvarArgs, VARIANT * pvarResult)
{
Assert(this->IsFunction());
return E_NOTIMPL;
}