//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1993.
//
//  File:       disputil.cxx
//
//  Contents:   Dispatch Utilities.
//
//  Classes:
//
//  Functions:
//
//  History:    21-Feb-94   adams   Created
//              08-Apr-94   DonCl   modified Set/GetDispProp to take REFIID
//                                  to meet sysmgmt needs
//              25-Oct-94   KrishnaG appropriated from the ADs project
//
//----------------------------------------------------------------------------

#include "procs.hxx"

#define VT_TYPEMASK   0x3ff


static HRESULT VARIANTARGToCVar(VARIANTARG * pvarg, VARTYPE vt, void* pv);
static void CVarToVARIANTARG(void* pv, VARTYPE vt, VARIANTARG * pvarg);


//+---------------------------------------------------------------------------
//
//  Function:   FreeEXCEPINFO
//
//  Synopsis:   Frees resources in an excepinfo.  Does not reinitialize
//              these fields.
//
//----------------------------------------------------------------------------

void
FreeEXCEPINFO(EXCEPINFO * pEI)
{
    if (pEI)
    {
        ADsFreeString(pEI->bstrSource);
        ADsFreeString(pEI->bstrDescription);
        ADsFreeString(pEI->bstrHelpFile);
    }
}



//+---------------------------------------------------------------------------
//
//  Function:   ValidateInvoke
//
//  Synopsis:   Validates arguments to a call of IDispatch::Invoke.  A call
//              to this function takes less space than the function itself.
//
//----------------------------------------------------------------------------

HRESULT
ValidateInvoke(
        DISPPARAMS *    pdispparams,
        VARIANT *       pvarResult,
        EXCEPINFO *     pexcepinfo,
        UINT *          puArgErr)
{
    if (pvarResult)
        VariantInit(pvarResult);

    if (pexcepinfo)
        InitEXCEPINFO(pexcepinfo);

    if (puArgErr)
        *puArgErr = 0;

    if (!pdispparams)
        RRETURN(E_INVALIDARG);

    return S_OK;
}



//+------------------------------------------------------------------------
//
//  Function:   LoadTypeInfo
//
//  Synopsis:   Loads a typeinfo from a registered typelib.
//
//  Arguments:  [clsidTL] --  TypeLib GUID
//              [clsidTI] --  TypeInfo GUID
//              [ppTI]    --  Resulting typeInfo
//
//  Returns:    HRESULT
//
//-------------------------------------------------------------------------

HRESULT
LoadTypeInfo(CLSID clsidTL, CLSID clsidTI, LPTYPEINFO *ppTI)
{
    HRESULT     hr;
    ITypeLib *  pTL;

    ADsAssert(ppTI);
    *ppTI = NULL;
    hr = LoadRegTypeLib(clsidTL, 1, 0, LOCALE_SYSTEM_DEFAULT, &pTL);
    if (!SUCCEEDED(hr))
        RRETURN(hr);

    hr = pTL->GetTypeInfoOfGuid(clsidTI, ppTI);
    pTL->Release();
    RRETURN(hr);
}



//+---------------------------------------------------------------------------
//
//  Function:   VARIANTARGToCVar
//
//  Synopsis:   Converts a VARIANT to a C-language variable.
//
//  Arguments:  [pvarg] -- Variant to convert.
//              [vt]    -- Type to convert to.
//              [pv]    -- Location to place C-language variable.
//
//  Modifies:   [pv].
//
//  Returns:    HRESULT.
//
//  History:    2-23-94   adams   Created
//
//  Notes:      Supports all variant pointer types, VT_I2, VT_I4, VT_R4,
//              VT_R8.
//----------------------------------------------------------------------------

static HRESULT
VARIANTARGToCVar(VARIANT * pvarg, VARTYPE vt, void * pv)
{
    HRESULT     hr      = S_OK;
    VARIANTARG  vargNew;                    // variant of new type

    ADsAssert(pvarg);
    ADsAssert(pv);
    ADsAssert((vt & ~VT_TYPEMASK) == 0 || (vt & ~VT_TYPEMASK) == VT_BYREF);

    if (vt & VT_BYREF)
    {
        if (V_VT(pvarg) != vt)
        {
            hr = DISP_E_TYPEMISMATCH;
            goto Cleanup;
        }

        // Use a supported pointer type for derefencing.
        vt = VT_UNKNOWN;
        vargNew = *pvarg;
    }
    else
    {
        VariantInit(&vargNew);
        hr = VariantChangeType(&vargNew, pvarg, 0, vt);
        if (hr)
            goto Cleanup;
    }

    switch (vt)
    {
    case VT_BOOL:
        if (V_BOOL(&vargNew) != VB_FALSE && V_BOOL(&vargNew) != VB_TRUE)
        {
            hr = E_FAIL;           // BUGBUG: scode?
            goto Cleanup;
        }

        // convert VT_TRUE to TRUE
        *(BOOL *)pv = - V_BOOL(&vargNew);
        break;

    case VT_I2:
        *(short *)pv = V_I2(&vargNew);
        break;

    case VT_I4:
        *(long *)pv = V_I4(&vargNew);
        break;

    case VT_R4:
        *(float *)pv = V_R4(&vargNew);
        break;

    case VT_R8:
        *(double *)pv = V_R8(&vargNew);
        break;

    //
    // All Pointer types.
    //
    case VT_BSTR:
    case VT_LPSTR:
    case VT_LPWSTR:
    case VT_DISPATCH:
    case VT_UNKNOWN:
        *(void **)pv = V_BYREF(&vargNew);
        break;

    default:
        ADsAssert(FALSE && "Unknown type in VARIANTARGToCVar().\n");
        break;
    }

Cleanup:
    RRETURN(hr);
}



//+---------------------------------------------------------------------------
//
//  Function:   CVarToVARIANTARG
//
//  Synopsis:   Converts a C-language variable to a VARIANT.
//
//  Arguments:  [pv]    -- Pointer to C-language variable.
//              [vt]    -- Type of C-language variable.
//              [pvarg] -- Resulting VARIANT.  Must be initialized by caller.
//                         Any contents will be freed.
//
//  Modifies:   [pvarg]
//
//  History:    2-23-94   adams   Created
//
//  Notes:      Supports all variant pointer types, VT_UI2, VT_I2, VT_UI4,
//              VT_I4, VT_R4, VT_R8.
//
//----------------------------------------------------------------------------

static void
CVarToVARIANTARG(void* pv, VARTYPE vt, VARIANTARG * pvarg)
{
    ADsAssert(pv);
    ADsAssert(pvarg);

    VariantClear(pvarg);

    V_VT(pvarg) = vt;
    if (V_ISBYREF(pvarg))
    {
        // Use a supported pointer type for derefencing.
        vt = VT_UNKNOWN;
    }

    switch (vt)
    {
    case VT_BOOL:
        // convert TRUE to VT_TRUE
        ADsAssert(*(BOOL *) pv == 1 || *(BOOL *) pv == 0);
        V_BOOL(pvarg) = VARIANT_BOOL(-*(BOOL *) pv);
        break;

    case VT_I2:
        V_I2(pvarg) = *(short *) pv;
        break;

    case VT_I4:
        V_I4(pvarg) = *(long *) pv;
        break;

    case VT_R4:
         V_R4(pvarg) = *(float *) pv;
        break;

    case VT_R8:
        V_R8(pvarg) = *(double *) pv;
        break;

    //
    // All Pointer types.
    //
    case VT_BSTR:
    case VT_LPSTR:
    case VT_LPWSTR:
    case VT_DISPATCH:
    case VT_UNKNOWN:
        V_BYREF(pvarg) = *(long **)pv;
        break;

    default:
        Assert(FALSE && "Unknown type.");
        break;
    }
}



//+---------------------------------------------------------------------------
//
//  Function:   CParamsToDispParams
//
//  Synopsis:   Converts a C parameter list to a dispatch parameter list.
//
//  Arguments:  [pDispParams] -- Resulting dispatch parameter list.
//                               Note that the rgvarg member of pDispParams
//                               must be initialized with an array of
//                               EVENTPARAMS_MAX VARIANTs.
//
//              [pvt]         -- List of C parameter types.  May be NULL.
//                               If not NULL, Last elem in list MUST be
//                               VT_EMPTY.
//
//              [va]          -- List of C arguments.
//
//  Modifies:   [pDispParams]
//
//  History:    05-Jan-94   adams   Created
//              23-Feb-94   adams   Reversed order of disp arguments, added
//                                  support for VT_R4, VT_R8, and pointer
//                                  types.
//
//  Notes:      Only types VT_I2,VT_I4, and VT_UNKNOWN are supported.
//
//----------------------------------------------------------------------------

void
CParamsToDispParams(
        DISPPARAMS *    pDispParams,
        VARTYPE *       pvt,
        va_list         va)
{
    ADsAssert(pDispParams);
    ADsAssert(pDispParams->rgvarg);

    VARIANTARG *    pvargCur;           // current variant
    VARTYPE *       pvtCur;            // current vartype

    // Assign vals to dispatch param list.
    pDispParams->cNamedArgs         = 0;
    pDispParams->rgdispidNamedArgs  = NULL;

    // Get count of arguments.
    if (!pvt)
    {
        pDispParams->cArgs = 0;
        return;
    }

    for (pvtCur = pvt; *pvtCur != VT_EMPTY; pvtCur++)
        ;

    pDispParams->cArgs = pvtCur - pvt;
    ADsAssert(pDispParams->cArgs < EVENTPARAMS_MAX);


    //
    // Convert each C-param to a dispparam.  Note that the order of dispatch
    // parameters is the reverse of the order of c-params.
    //

    ADsAssert(pDispParams->rgvarg);
    pvargCur = pDispParams->rgvarg + pDispParams->cArgs;
    for (pvtCur = pvt; *pvtCur != VT_EMPTY; pvtCur++)
    {
        pvargCur--;
        ADsAssert(pvargCur >= pDispParams->rgvarg);

        V_VT(pvargCur) = *pvtCur;
        if ((*pvtCur & VT_BYREF) == VT_BYREF)
        {
            V_BYREF(pvargCur) = va_arg(va, long *);
        }
        else
        {
            switch (*pvtCur)
            {
            case VT_BOOL:
                // convert TRUE to VT_TRUE
                V_BOOL(pvargCur) = VARIANT_BOOL(-va_arg(va, BOOL));
                ADsAssert(V_BOOL(pvargCur) == VB_FALSE ||
                        V_BOOL(pvargCur) == VB_TRUE);
                break;

            case VT_I2:
                V_I2(pvargCur) = va_arg(va, short);
                break;

            case VT_I4:
                V_I4(pvargCur) = va_arg(va, long);
                break;

            case VT_R4:
                V_R4(pvargCur) = va_arg(va, float);
                break;

            case VT_R8:
                V_R8(pvargCur) = va_arg(va, double);
                break;

            //
            // All Pointer types.
            //
            case VT_BSTR:
            case VT_LPSTR:
            case VT_LPWSTR:
            case VT_DISPATCH:
            case VT_UNKNOWN:
                V_BYREF(pvargCur) = va_arg(va, long *);
                break;

            default:
                Assert(FALSE && "Unknown type.\n");
            }
        }
    }
}



//+---------------------------------------------------------------------------
//
//  Function:   DispParamsToCParams
//
//  Synopsis:   Converts Dispatch::Invoke method params to C-language params.
//
//  Arguments:  [pDP] -- Dispatch params to be converted.
//              [pvt] -- Array of types of C-params.  May be NULL.  If
//                       non-NULL, last element must be VT_EMPTY.
//              [...] -- List of pointers to c-params to be converted to.
//
//  Returns:    HRESULT.
//
//  History:    2-23-94   adams   Created
//
//  Notes:      Supports types listed in VARIANTToCParam.
//
//----------------------------------------------------------------------------

STDAPI
DispParamsToCParams(
        DISPPARAMS *    pDP,
        UINT *          puArgErr,
        VARTYPE *       pvt,
        ...)
{
    HRESULT         hr;
    va_list         va;                // list of pointers to c-params.
    VARTYPE *       pvtCur;            // current VARTYPE of c-param.
    VARIANTARG *    pvargCur;          // current VARIANT being converted.
    void *          pv;                // current c-param being converted.
    int             cArgs;             // count of arguments.

    ADsAssert(pDP);

    hr = S_OK;
    va_start(va, pvt);
    if (!pvt)
    {
        if (pDP->cArgs > 0)
            goto BadParamCountError;

        goto Cleanup;
    }

    pvargCur = pDP->rgvarg + pDP->cArgs - 1;
    pvtCur = pvt;
    for (cArgs = 0; cArgs < (int)pDP->cArgs; cArgs++)
    {
        if (*pvtCur == VT_EMPTY)
            goto BadParamCountError;

        pv = va_arg(va, void *);
        hr = VARIANTARGToCVar(pvargCur, *pvtCur, pv);
        if (hr)
        {
            if (puArgErr)
                *puArgErr = cArgs;

            goto Cleanup;
        }

        pvargCur--;
        pvtCur++;
    }

    if (*pvtCur != VT_EMPTY)
        goto BadParamCountError;

Cleanup:
    va_end(va);
    RRETURN(hr);

BadParamCountError:
    hr = DISP_E_BADPARAMCOUNT;
    goto Cleanup;
}



//+---------------------------------------------------------------------------
//
//  Function:   GetDispProp
//
//  Synopsis:   Gets a property of an object.
//
//  Arguments:  [pDisp]  -- The object containing the property.
//              [dispid] -- The ID of the property.
//              [riid]   -- interface of object desired
//              [lcid]   -- The locale of the object.
//              [pvar]   -- The resulting property.  Must be initialized.
//
//  Returns:    HRESULT.
//
//  Modifies:   [pvarg].
//
//  History:    23-Feb-94   adams   Created
//              08-Apr-94   DonCl   modified to take REFIID
//
//----------------------------------------------------------------------------

HRESULT
GetDispProp(
        IDispatch * pDisp,
        DISPID      dispid,
        REFIID      riid,
        LCID        lcid,
        VARIANT *   pvar,
        EXCEPINFO * pexcepinfo)
{
    HRESULT     hr;
    DISPPARAMS  dp;                    // Params for IDispatch::Invoke.
    UINT        uiErr;                 // Argument error.

    ADsAssert(pDisp);
    ADsAssert(pvar);

    dp.rgvarg = NULL;
    dp.rgdispidNamedArgs = NULL;
    dp.cArgs = 0;
    dp.cNamedArgs = 0;

    hr = pDisp->Invoke(
            dispid,
            riid,
            lcid,
            DISPATCH_PROPERTYGET,
            &dp,
            pvar,
            pexcepinfo,
            &uiErr);

    RRETURN(hr);
}



//+---------------------------------------------------------------------------
//
//  Function:   SetDispProp
//
//  Synopsis:   Sets a property on an object.
//
//  Arguments:  [pDisp]  -- The object to set the property on.
//              [dispid] -- The ID of the property.
//              [riid]   -- interface of object
//              [lcid]   -- The locale of the property.
//              [pvarg]  -- The value to set.
//
//  Returns:    HRESULT.
//
//  History:    23-Feb-94   adams   Created
//              08-Apr-94   DonCl   modified to take REFIID
//
//----------------------------------------------------------------------------

HRESULT
SetDispProp(
        IDispatch *     pDisp,
        DISPID          dispid,
        REFIID          riid,
        LCID            lcid,
        VARIANTARG *    pvarg,
        EXCEPINFO *     pexcepinfo)
{
    HRESULT     hr;
    DISPID      dispidPut = DISPID_PROPERTYPUT; // Dispid of prop arg.
    DISPPARAMS  dp;                    // Params for Invoke
    UINT        uiErr;                 // Invoke error param.

    ADsAssert(pDisp);
    ADsAssert(pvarg);

    dp.rgvarg = pvarg;
    dp.rgdispidNamedArgs = &dispidPut;
    dp.cArgs = 1;
    dp.cNamedArgs = 1;
    hr = pDisp->Invoke(
            dispid,
            riid,
            lcid,
            DISPATCH_PROPERTYPUT,
            &dp,
            NULL,
            pexcepinfo,
            &uiErr);

    RRETURN(hr);
}



//+---------------------------------------------------------------------------
//
//  Function:   GetDispPropOfType
//
//  Synopsis:   Gets a property from an object, and converts it to a c
//              variable.
//
//  Arguments:  [pDisp]  -- The object to retrieve the property from.
//              [dispid] -- Property ID.
//              [lcid]   -- Locale of property.
//              [vt]     -- Type of c-variable to receive property.
//              [pv]     -- Pointer to resulting c-variable.
//
//  Returns:    HRESULT.
//
//  Modifies:   [pv].
//
//  History:    2-23-94   adams   Created
//
//  Notes:      Supports variable types found in VARIANTARGToCVar.
//
//----------------------------------------------------------------------------

HRESULT
GetDispPropOfType(
        IDispatch * pDisp,
        DISPID      dispid,
        LCID        lcid,
        VARTYPE     vt,
        void *      pv)
{
    HRESULT     hr;
    VARIANT     varProp;               // Property retrieved.
    DISPPARAMS  dp;                    // Params for IDispatch::Invoke.

    ADsAssert(pDisp);
    ADsAssert(pv);

    dp.rgvarg = NULL;
    dp.rgdispidNamedArgs = NULL;
    dp.cArgs = 0;
    dp.cNamedArgs = 0;

    VariantInit(&varProp);
    hr = pDisp->Invoke(
            dispid,
            IID_NULL,
            lcid,
            DISPATCH_PROPERTYGET,
            &dp,
            &varProp,
            NULL,
            NULL);
    if (hr)
        goto Cleanup;

    hr = VARIANTARGToCVar(&varProp, vt, pv);

Cleanup:
    RRETURN(hr);
}



//+---------------------------------------------------------------------------
//
//  Function:   SetDispPropOfType
//
//  Synopsis:   Sets a property on an object.
//
//  Arguments:  [pDisp]  -- Object to set property on.
//              [dispid] -- Property ID to set.
//              [lcid]   -- Locale of property.
//              [vt]     -- Type of property to set.
//              [pv]     -- Pointer to property value.
//
//  Returns:    HRESULT.
//
//  History:    2-23-94   adams   Created
//
//  Notes:      Supports types found in VARIANTARGToCVar.
//
//----------------------------------------------------------------------------

HRESULT
SetDispPropOfType(
        IDispatch * pDisp,
        DISPID      dispid,
        LCID        lcid,
        VARTYPE     vt,
        void *      pv)
{
    HRESULT     hr;
    VARIANTARG  varg;                   // Variant property to put.
    DISPID      dispidPut = DISPID_PROPERTYPUT; // Dispid of prop arg.
    DISPPARAMS  dp;                    // Params for Invoke

    ADsAssert(pDisp);
    ADsAssert(pv);

    VariantInit(&varg);
    CVarToVARIANTARG(pv, vt, &varg);
    dp.rgvarg = &varg;
    dp.rgdispidNamedArgs = &dispidPut;
    dp.cArgs = 1;
    dp.cNamedArgs = 1;
    hr = pDisp->Invoke(
            dispid,
            IID_NULL,
            lcid,
            DISPATCH_PROPERTYPUT,
            &dp,
            NULL,
            NULL,
            NULL);
    RRETURN(hr);
}



//+---------------------------------------------------------------------------
//
//  Function:   CallDispMethod
//
//  Synopsis:   Calls a late-bound method on a object via IDispatch::Invoke.
//
//  Arguments:  [pDisp]     -- Object to call method on.
//              [dispid]    -- Method ID.
//              [lcid]      -- Locale of method.
//              [vtReturn]  -- Type of return value.  If no return value,
//                             must be VT_VOID.
//              [pvReturn]  -- Location of return value.  If no return value,
//                             must be NULL.
//              [pvtParams] -- List of param types.  May be NULL.  If
//                             non-NULL, last entry must be VT_EMPTY.
//              [...]       -- List of params.
//
//  Returns:    HRESULT.
//
//  History:    2-23-94   adams   Created
//
//----------------------------------------------------------------------------

HRESULT
CallDispMethod(
        IDispatch * pDisp,
        DISPID      dispid,
        LCID        lcid,
        VARTYPE     vtReturn,
        void *      pvReturn,
        VARTYPE *   pvtParams,
        ...)
{
    HRESULT     hr;
    VARIANTARG  av[EVENTPARAMS_MAX];   // List of args for Invoke.
    DISPPARAMS  dp;                    // Params for Invoke.
    VARIANT     varReturn;             // Return value.
    va_list     va;                    // List of C-params.

    ADsAssert(pDisp);
    ADsAssert((vtReturn != VT_VOID) == (pvReturn != NULL));

    va_start(va, pvtParams);
    dp.rgvarg = av;
    CParamsToDispParams(&dp, pvtParams, va);
    va_end(va);

    if (pvReturn)
        VariantInit(&varReturn);

    hr = pDisp->Invoke(
            dispid,
            IID_NULL,
            lcid,
            DISPATCH_METHOD,
            &dp,
            pvReturn ? &varReturn : NULL,
            NULL,
            NULL);
    if (hr)
        goto Cleanup;

    if (pvReturn)
        hr = VARIANTARGToCVar(&varReturn, vtReturn, pvReturn);

Cleanup:
    RRETURN(hr);
}



//+------------------------------------------------------------------------
//
//  Function:   IsVariantEqual, public API
//
//  Synopsis:   Compares the values of two VARIANTARGs.
//
//  Arguments:  [pvar1], [pvar2] -- VARIANTARGs to compare.
//
//  Returns:    TRUE if equal, FALSE if not.
//
//  History:    18-Mar-93   SumitC      Created.
//              11-May-94   SumitC      don't assert for VT_UNKNOWN
//
//  Notes:      Variant type unequal returns FALSE, even if actual values
//              are the same.
//              Currently does I2, I4, R4, R8, CY, BSTR, BOOL
//              Returns FALSE for all other VariantTypes.
//
//-------------------------------------------------------------------------

BOOL
IsVariantEqual( VARIANTARG FAR* pvar1, VARIANTARG FAR* pvar2 )
{
    if( V_VT(pvar1) != V_VT(pvar2) )
        return FALSE;

    switch (V_VT(pvar1))
    {
    case VT_EMPTY :
    case VT_NULL:
        return TRUE;    // just the types being equal is good enough

    case VT_I2 :
        return (V_I2(pvar1) == V_I2(pvar2));

    case VT_I4 :
        return (V_I4(pvar1) == V_I4(pvar2));

    case VT_R4 :
        return (V_R4(pvar1) == V_R4(pvar2));

    case VT_R8 :
        return (V_R8(pvar1) == V_R8(pvar2));

    case VT_CY :
        return !memcmp(&V_CY(pvar1), &V_CY(pvar2), sizeof(CY));

    case VT_BSTR :
        return !ADsStringCmp(V_BSTR(pvar1), V_BSTR(pvar2));

    case VT_BOOL :
        return (V_BOOL(pvar1) == V_BOOL(pvar2));

    case VT_UNKNOWN:
        // returns FALSE unless the objects are the same
        return (V_UNKNOWN(pvar1) == V_UNKNOWN(pvar2));

    default:
        ADsAssert(0 && "Type not handled");
        break;
    };

    return(FALSE);
}










































































































































































