/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
//
//  File:       PropVar.c
//
//  Purpose:    This file provides Office-aware routines which 
//              operate on PropVariants.  They are Office-aware in
//              that they only operate on the subset of
//              VarTypes which are used by Office.
//
/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////


#include "priv.h"
#pragma hdrstop


/////////////////////////////////////////////////////////////////////////////////
//
//  Function:   FPropVarLoad
//
//  Purpse:     Load data into a PropVariant.  If the target PropVariant
//              already contains data, it will be freed.
//
//              Note that new memory is allocated, if necessary, to hold
//              the data in the PropVariant.  Also note that the
//              resulting PropVariant should be freed by the caller using
//              PropVariantClear.
//
//  Inputs:     LPPROPVARIANT - to be loaded.  This should be a valid
//                              (i.e. intialized) PropVariant.
//              VARTYPE       - of the new PropVariant (must be a member of
//                              the limited set used by Office).
//              LPVOID        - Either the data to be loaded, or a pointer
//                              to such data, depending on the type.
//
//  Output:     TRUE if and only if successful.
//
/////////////////////////////////////////////////////////////////////////////////


BOOL
FPropVarLoad
    ( LPPROPVARIANT     lppropvar,
      VARTYPE           vt,
      LPVOID const      lpv )
{
    //  ------
    //  Locals
    //  ------

    BOOL    fSuccess = FALSE;   // Return code
    ULONG   cch, cb;

    //  ----------
    //  Initialize
    //  ----------

    Assert (lppropvar != NULL);
    Assert (lpv != NULL);

    // Free any data currently in the PropVariant.

    PropVariantClear (lppropvar);

    //  ---------------------------------------------------
    //  Set the value of the PropVariant based on the type.
    //  ---------------------------------------------------

    switch (vt)
    {
        // Strings

        case VT_LPTSTR:

            // Determine the character and byte count.

            cch = CchTszLen(lpv);    // Doesn't include the NULL.
            cb = CBTSTR(lpv);       // *Does* include the NULL.

            // Allocate memory in the PropVariant.

            lppropvar->pszVal = CoTaskMemAlloc (cb);
            if (lppropvar->pszVal == NULL)
            {
                MESSAGE(TEXT("Couldn't allocate new VT_LPTSTR"));
                goto Exit;
            }

            // Copy the string to the PropVariant and terminate it.

            PbSzNCopy (lppropvar->pszVal, lpv, cch);
            ((LPTSTR)lppropvar->pszVal)[cch] = TEXT('\0');

            break;

        // DWORDs

        case VT_I4:

            lppropvar->lVal = *(DWORD*) lpv;
            break;

        // FileTime

        case VT_FILETIME:

            PbMemCopy (&lppropvar->filetime, lpv, sizeof(FILETIME));
            break;

        // Double

        case VT_R8:
            PbMemCopy (&lppropvar->dblVal, lpv, sizeof(double));
            break;

        // Bool

        case VT_BOOL:
            lppropvar->boolVal = *(VARIANT_BOOL*) lpv ? VARIANT_TRUE : VARIANT_FALSE;
            break;

        // Invalid type.

        default:
            MESSAGE(TEXT("Invalid VarType"));
            goto Exit;
    }

    // Set the VT of the PropVariant, and we're done.

    lppropvar->vt = vt;

    //  ----
    //  Exit
    //  ----

    fSuccess = TRUE;

Exit:
    return (fSuccess);

}   // FPropVarLoad



////////////////////////////////////////////////////////////////////////////////
//
//  Function:   FCoStrToWStr
//
//  Purpose:    Convert a COM string (ANSI) to a COM wide-string.
//              ("COM" because the string is allocated using
//              the COM heap).
//
//  Inputs:     LPWSTR* - The converted string.
//              LPSTR   - The original string.
//              UINT    - The ANSI code page.
//
//  Output:     TRUE if and only if successful.
//
////////////////////////////////////////////////////////////////////////////////

BOOL
FCoStrToWStr( LPWSTR            *lplpwstr,
              const LPSTR       lpstr,
              UINT              uCodePage)
{
    //  ------
    //  Locals
    //  ------

    BOOL fSuccess = FALSE;  // Return value.
    ULONG cchBuffer = 0;    // Size of converted string (includes NULL).

    Assert (lpstr != NULL && lplpwstr != NULL);

    //  ------------------
    //  Convert the string
    //  ------------------

    // Make two passes.  The first will calculate the
    // size of the target buffer, the second will actually
    // make the conversion.

    *lplpwstr = NULL;

    while (TRUE)
    {
        cchBuffer = MultiByteToWideChar(
                        uCodePage,          // Source code page
                        0,                  // Default flags
                        lpstr,              // Source string
                        -1,                 // Default length
                        *lplpwstr,          // Destination string
                        cchBuffer );        // Max dest string characters.

        // Is this the second pass (when the conversion should
        // have taken place)?

        if (*lplpwstr != NULL)
        {
            // If we got a good result, then we're done.
            if (cchBuffer != 0)
            {
                break;
            }

            // 0 was returned.  There was an error.
            else
            {
                AssertSz (0, TEXT("Couldn't convert MBCS to Wide"));
                goto Exit;
            }
        }

        // Otherwise, this is the first pass.  We need to
        // allocate a buffer.

        else
        {
            // We should have gotten a positive buffer size.

            if (cchBuffer == 0)
            {
                AssertSz(0, TEXT("MultiByteToWideChar returned invalid target buffer size"));
                goto Exit;
            }

            // Allocate memory for the converted string.
            else
            {
                *lplpwstr = (LPWSTR) CoTaskMemAlloc( cchBuffer * 2 );
                if ( *lplpwstr == NULL)
                {
                    AssertSz (0, TEXT("Could not allocate memory for wide string"));
                    goto Exit;
                }
            }
        }   // if( *lplpwstr != NULL ... else
    }   // while (TRUE)



    //  ----
    //  Exit
    //  ----

    fSuccess = TRUE;

Exit:

    // If there was a problem, free the Unicode string.

    if (!fSuccess)
    {
        CoTaskMemFree (*lplpwstr);
        *lplpwstr = NULL;
    }

    return (fSuccess);

}   // FCoStrToWStr


////////////////////////////////////////////////////////////////////////////////
//
//  Function:   FCoWStrToStr
//
//  Purpose:    Convert a COM wide-string to an ANSI string.
//              ("COM" because the string is allocated using
//              the COM heap).
//
//  Inputs:     LPSTR*  - The converted string.
//              LPWSTR  - The source string.
//              UINT    - The ANSI code page.
//
//  Output:     TRUE if and only if successful.
//
////////////////////////////////////////////////////////////////////////////////

BOOL
FCoWStrToStr( LPSTR             *lplpstr,
              const LPWSTR      lpwstr,
              UINT              uCodePage)
{
    //  ------
    //  Locals
    //  ------

    BOOL fSuccess = FALSE;  // Return result
    ULONG cch;              // Charcters in original string (w/o NULL).
    ULONG cbBuffer = 0;     // Size of target buffer (including NULL)

    Assert (lpwstr != NULL && lplpstr != NULL);

    //  ------------------
    //  Convert the String
    //  ------------------

    // We'll make two calls to WideCharToMultiByte.
    // In the first, we'll determine the size required
    // for the multi-byte string.  We'll use this
    // to allocate memory.  In the second pass, we'll actually
    // make the conversion.

    cch = CchWszLen( lpwstr );   // How big is the source string?
    *lplpstr = NULL;            // Initialize the target buffer.

    while (TRUE)
    {
        cbBuffer = WideCharToMultiByte(
                    uCodePage,         // Source code page
                    0,                  // Default flags
                    lpwstr,             // Source string
                    cch + 1,            // # chars in wide string (including NULL)
                    *lplpstr,           // Destination string
                    cbBuffer,           // Size of destination buffer
                    NULL, NULL );       // No default character


        // Is this the second pass (when the conversion should
        // have taken place)?

        if (*lplpstr != NULL)
        {
            // If we got a good result, then we're done.
            if (cbBuffer != 0)
            {
                break;
            }

            // 0 was returned.  There was an error.
            else
            {
                AssertSz (0, TEXT("Couldn't convert Wide to MBCS"));
                goto Exit;
            }
        }

        // Otherwise, this is the first pass.  We need to
        // allocate a buffer.

        else
        {
            // We should have gotten a positive buffer size.

            if (cbBuffer == 0)
            {
                AssertSz(0, TEXT("WideCharMultiByte returned invalid target buffer size"));
                goto Exit;
            }

            // Allocate memory for the converted string.
            else
            {
                *lplpstr = (LPSTR) CoTaskMemAlloc( cbBuffer );
                if ( *lplpstr == NULL)
                {
                    AssertSz (0, TEXT("Could not allocate memory for wide string"));
                    goto Exit;
                }
            }
        }   // if( lpstr != NULL ... else
    }   //  while (TRUE)
        

    //  ----
    //  Exit
    //  ----

    fSuccess = TRUE;

Exit:

    // If there was a problem, free the new string.

    if (!fSuccess)
    {
        CoTaskMemFree (*lplpstr);
        *lplpstr = NULL;
    }

    return (fSuccess);

}   // FCoWStrToStr




//////////////////////////////////////////////////////////////////////////////
//
//  Function:   FPropVarConvertString
//
//  Purpose:    Convert a PropVariant from VT_LPSTR to VT_LPWSTR,
//              or vice-versa.  The correct direction is inferred
//              from the input.  The source PropVariant is not 
//              modified.
//
//              If the PropVariant is a Vector, all elements are
//              converted.
//
//  Inputs:     LPPROPVARIANT   - The buffer in which to put the
//                                converted PropVariant.
//              LPPROPVARIANT   - The source of the conversion.
//              UINT            - The code page of VT_LPSTRs.
//
//  Output:     TRUE if successful.  If unsuccessful, the original 
//              PropVariant will be returned unmodified.
//
//  Pre-Conditions:
//              The input must be either a VT_LPSTR or a VT_LPWSTR
//              (with or without the VT_VECTOR bit set).
//              &&
//              The destination PropVariant is VT_EMPTY.
//              &&
//              The code page must not be CP_WINUNICODE (Unicode
//              LPSTRs are not legal in the SumInfo property sets).
//
//////////////////////////////////////////////////////////////////////////////

BOOL FPropVarConvertString( LPPROPVARIANT       lppropvarDest,
                            const LPPROPVARIANT lppropvarSource,
                            UINT                uCodePage)
{

    //  ------
    //  Locals
    //  ------

    BOOL    fSuccess = FALSE;   // Return code.

    BOOL    fConvertToAnsi;     // Indicates the direction of the conversion.
    LPSTR   *lplpstrDest;       // Pointer to pointer to a converted string.
    LPSTR   lpstrSource;        // Pointer to a string to be converted.
    ULONG   cElems;             // The number of strings still requiring conversion
    ULONG   ulIndex = 0;        // Index into vector (if this VT is a vector)

    //  ----------
    //  Initialize
    //  ----------

    Assert(lppropvarDest != NULL && lppropvarSource != NULL);
    Assert(lppropvarDest->vt == VT_EMPTY);
    Assert(uCodePage != CP_WINUNICODE);

    // Determine the direction of the conversion.

    fConvertToAnsi = (lppropvarSource->vt & ~VT_VECTOR) == VT_LPSTR
                     ? FALSE
                     : TRUE;

    //  -----------------------------------
    //  Initialize based on the Vector bit.
    //  -----------------------------------

    if (lppropvarSource->vt & VT_VECTOR)
    {
        // We're a vector.

        cElems = lppropvarSource->calpstr.cElems;

        // Allocate an array of string pointers, putting it in
        // lppropvarDest.

        lppropvarDest->calpstr.pElems = CoTaskMemAlloc( cElems
                                                        * sizeof(*lppropvarDest->calpstr.pElems) );
        if (lppropvarDest->calpstr.pElems == NULL)
        {
            AssertSz(0,TEXT("Couldn't allocate memory for pElemsNew"));
            goto Exit;
        }

        // Fill this new buffer so we don't get confused in the error path.

        FillBuf (lppropvarDest->calpstr.pElems, 0,
                 cElems * sizeof(*lppropvarDest->calpstr.pElems));

        lppropvarDest->calpstr.cElems = cElems;

        // Initialize the pointers for the first string to convert.

        lplpstrDest = &lppropvarDest->calpstr.pElems[ 0 ];
        lpstrSource = lppropvarSource->calpstr.pElems[ 0 ];

    }   // if (lppropvar->vt & VT_VECTOR)
    else
    {
        // We're not a vector, initialize to the only string.

        cElems = 1;

        lplpstrDest = &lppropvarDest->pszVal;
        lpstrSource = lppropvarSource->pszVal;
    }
        

    //  ---------------------
    //  Convert the String(s)
    //  ---------------------

    while (cElems)
    {

        if (fConvertToAnsi)
        {
            if (!FCoWStrToStr ((LPSTR*)lplpstrDest, (LPWSTR) lpstrSource,
                               uCodePage))
            {
                goto Exit;
            }
        }
        else
        {
            if (!FCoStrToWStr ((LPWSTR*) lplpstrDest, (LPSTR) lpstrSource,
                               uCodePage))
            {
                goto Exit;
            }
        }

        // Move on to the next entry, if there is one.

        if (--cElems)
        {
            ulIndex++;
            lplpstrDest = &lppropvarDest->calpstr.pElems[ ulIndex ];
            lpstrSource = lppropvarSource->calpstr.pElems[ ulIndex ];

        }   // if (--cElems)
    }   // while (cElems)


    // Switch the destination VT to VT_LPSTR (for ansi strings) or VT_LPWSTR
    // (for Unicode strings), preserving all other bits.

    lppropvarDest->vt = (lppropvarSource->vt & ~(VT_LPSTR|VT_LPWSTR))
                        |
                        (fConvertToAnsi ? VT_LPSTR : VT_LPWSTR);
                    

    //  ----
    //  Exit
    //  ----

    fSuccess = TRUE;

Exit:

    if (!fSuccess)
        PropVariantClear (lppropvarDest);

    return( fSuccess );

}   // FPropVarConvertString



////////////////////////////////////////////////////////////////////////////////
//
//  Function:   FPropVarCopyToBuf
//
//  Purpose:    Copy the value from a PropVariant to a buffer.
//              (The caller indicates how large the buffer is).
//              The VarType of the PropVar must be from the 
//              valid Office sub-set.
//
//  Inputs:     LPPROPVARIANT   - The source.
//              DWORD           - The size of the target buffer.
//              LPVOID          - The target buffer.
//
//  Output:     TRUE if successful.
//
////////////////////////////////////////////////////////////////////////////////

BOOL
FPropVarCopyToBuf
  (LPPROPVARIANT const lppropvar,
   DWORD cbMax,
   LPVOID lpvBuf)
{
    //  ------
    //  Locals
    //  ------

    BOOL    fSuccess = FALSE;
    DWORD   cb;

    //  ----------------
    //  Check the inputs
    //  ----------------

    Assert (lppropvar != NULL);
    Assert (lpvBuf != NULL);

    //  ---------------------
    //  Copy, based on the VT
    //  ---------------------

    switch (lppropvar->vt)
    {
        // File Time

        case VT_FILETIME :
            if (cbMax < sizeof(FILETIME))
                return(FALSE);
            PbMemCopy (lpvBuf, &lppropvar->filetime, sizeof(FILETIME));
            break;

        // Double

        case VT_R8:
            if (cbMax < sizeof(double))
                return(FALSE);
            PbMemCopy (lpvBuf, &lppropvar->dblVal, sizeof(double));

        // Boolean

        case VT_BOOL:
            if (cbMax < sizeof(WORD))
                return(FALSE);

            *(WORD *)lpvBuf = (WORD)lppropvar->boolVal;
            break;

        // DWORD

        case VT_I4:
            if (cbMax < sizeof(DWORD))
                return(FALSE);

            *(DWORD *)lpvBuf = (DWORD)lppropvar->ulVal;
            break;

        // String

        case VT_LPTSTR:
            if (lppropvar->pszVal == NULL)
                return(FALSE);

            // Calculate the number of characters to copy (not
            // including the NULL).  But don't let it exceed
            // cbMax - sizeof('\0').

            cb = min ( CchTszLen ((LPTSTR)lppropvar->pszVal) * sizeof(TCHAR),
                       cbMax - sizeof(TCHAR));

            PbMemCopy(lpvBuf, lppropvar->pszVal, cb);
            ((LPTSTR) lpvBuf)[ cb/sizeof(TCHAR) ] = TEXT('\0');
            break;

        // Invalid type.

        default :
            AssertSz (0, TEXT("Invalid type in FPropVarCopyToBuf"));
            goto Exit;

    }   // switch (lppropvar->vt)

    fSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    return (fSuccess);

} // FPropVarCopyToBuf