Fabulous Adventures In Coding

Eric Lippert's Blog

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

Published Tuesday, May 04, 2004 10:01 AM by Eric Lippert
Filed under:

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

 

binder and binder said:

July 28, 2008 9:16 PM

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required
Submit

About Eric Lippert

Eric Lippert is a senior developer on the Microsoft C# compiler team. Before that he worked on the framework of Visual Studio Tools For Office. Before that, he worked on the compilers, runtimes and tools for VBScript, JScript, Windows Script Host and other Microsoft Scripting technologies. He lives in Seattle and spends his free time editing books about programming languages, playing the piano, and trying to keep his tiny sailboat upright in Puget Sound.

This Blog

Syndication


© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker