/*----------------------------------------------------------------------------
/ Title;
/   misc.cpp
/
/ Authors;
/   David De Vorchik (daviddv)
/
/ Notes;
/   Misc functions
/----------------------------------------------------------------------------*/
#include "pch.h"
#include "atlbase.h"
#include <advpub.h>         // For REGINSTALL
#pragma hdrstop


/*-----------------------------------------------------------------------------
/ GetKeyForCLSID
/ --------------
/   Given a reference to a CLSID open up the key that represents it.
/
/ In:
/   clsid = clsid reference
/   pSubKey -> name of sub key to be opened
/   phkey = receives the newly opened key
/
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
EXTERN_C HRESULT GetKeyForCLSID(REFCLSID clsid, LPCTSTR pSubKey, HKEY* phkey)
{
    HRESULT hr;
    TCHAR szBuffer[(MAX_PATH*2)+GUIDSTR_MAX];
    TCHAR szGuid[GUIDSTR_MAX];

    TraceEnter(TRACE_COMMON_MISC, "GetKeyForCLSID");
    TraceGUID("clsid", clsid);
    Trace(TEXT("pSubKey -%s-"), pSubKey ? pSubKey:TEXT("<none>"));

    TraceAssert(phkey);

    // - format the CLSID so we can find it in the registry
    // - then open it (the client is reponsible for closing it)

    *phkey = NULL;              // incase we fail

    if ( 0 == GetStringFromGUID(clsid, szGuid, ARRAYSIZE(szGuid)) )
        ExitGracefully(hr, E_FAIL, "Failed to convert GUID to string");

    wsprintf(szBuffer, TEXT("CLSID\\%s"), szGuid);

    if ( pSubKey )
    {
        StrCat(szBuffer, TEXT("\\"));
        StrCat(szBuffer, pSubKey);
    }

    Trace(TEXT("Trying to open -%s-"), szBuffer);

    if ( ERROR_SUCCESS != RegOpenKeyEx(HKEY_CLASSES_ROOT, szBuffer, NULL, KEY_READ, phkey) )
        ExitGracefully(hr, E_FAIL, "Failed to open key");

    hr = S_OK;                  // success

exit_gracefully:

    TraceLeaveResult(hr);
}


/*-----------------------------------------------------------------------------
/ GetRealWindowInfo
/ -----------------
/   Get the window dimensions and client position.
/
/ In:
/   hwnd = window to enquire about
/   pRect -> receives the client position of the window / == NULL
/   pSize -> receives the size of the window  / == NULL
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
EXTERN_C HRESULT GetRealWindowInfo(HWND hwnd, LPRECT pRect, LPSIZE pSize)
{
    HRESULT hr;
    RECT rect;

    TraceEnter(TRACE_COMMON_MISC, "GetRealWindowInfo");

    if ( !GetWindowRect(hwnd, &rect) )
        ExitGracefully(hr, E_FAIL, "Failed to get window rectangles");

    MapWindowPoints(NULL, GetParent(hwnd), (LPPOINT)&rect, 2);
    
    if ( pRect )
        *pRect = rect;

    if ( pSize )
    {
        pSize->cx = rect.right - rect.left;
        pSize->cy = rect.bottom - rect.top;
    }

    hr = S_OK;

exit_gracefully:

    TraceLeaveResult(hr);
}


/*-----------------------------------------------------------------------------
/ OffsetWindow
/ ------------
/   Adjust the position of the given window by the given delta.  If the
/   delta is 0,0 then this is a NOP.
/
/ In:
/   hwnd = window to enquire about
/   dx, dy = offset to be applied to the window
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
EXTERN_C VOID OffsetWindow(HWND hwnd, INT dx, INT dy)
{
    RECT rect;

    TraceEnter(TRACE_COMMON_MISC, "OffsetWindow");

    if ( hwnd && (dx || dy) )
    {
        GetWindowRect(hwnd, &rect);
        MapWindowPoints(NULL, GetParent(hwnd), (LPPOINT)&rect, 2);
        SetWindowPos(hwnd, NULL, rect.left + dx, rect.top + dy, 0, 0, SWP_NOZORDER|SWP_NOSIZE);
    }

    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ CallRegInstall
/ --------------
/   Call ADVPACK for the given section of our resource based INF>
/
/ In:
/   hInstance = resource instance to get REGINST section from
/   szSection = section name to invoke
/
/ Out:
/   HRESULT:
/----------------------------------------------------------------------------*/
EXTERN_C HRESULT CallRegInstall(HINSTANCE hInstance, LPSTR szSection)
{
    HRESULT hr = E_FAIL;
    HINSTANCE hinstAdvPack = LoadLibrary(TEXT("ADVPACK.DLL"));

    TraceEnter(TRACE_COMMON_MISC, "CallRegInstall");

    if (hinstAdvPack)
    {
        REGINSTALL pfnri = (REGINSTALL)GetProcAddress(hinstAdvPack, "RegInstall");

#ifdef UNICODE
        if ( pfnri )
        {
            STRENTRY seReg[] = 
            {
                // These two NT-specific entries must be at the end
                { "25", "%SystemRoot%" },
                { "11", "%SystemRoot%\\system32" },
            };
            STRTABLE stReg = { ARRAYSIZE(seReg), seReg };
            
            hr = pfnri(hInstance, szSection, &stReg);
        }
#else
        if (pfnri)
        {
            hr = pfnri(hInstance, szSection, NULL);
        }

#endif
        FreeLibrary(hinstAdvPack);
    }

    TraceLeaveResult(hr);
}


/*-----------------------------------------------------------------------------
/ SetDefButton
/ ------------
/   Jump through hoops, avoid barking dogs and dice with death all to set
/   the default button in a dialog.
/
/ In:
/   hWnd, idButton = button to set
/
/ Out:
/   HRESULT:
/----------------------------------------------------------------------------*/
EXTERN_C VOID SetDefButton(HWND hwndDlg, int idButton)
{
    LRESULT lr;
    LONG style;

    TraceEnter(TRACE_COMMON_MISC, "SetDefButton");

    if (HIWORD(lr = SendMessage(hwndDlg, DM_GETDEFID, 0, 0)) == DC_HASDEFID)
    {
        HWND hwndOldDefButton = GetDlgItem(hwndDlg, LOWORD(lr));

        style = GetWindowLong(hwndOldDefButton, GWL_STYLE) & ~BS_DEFPUSHBUTTON;
        SendMessage (hwndOldDefButton,
                     BM_SETSTYLE,
                     MAKEWPARAM(style, 0),
                     MAKELPARAM(TRUE, 0));
    }

    SendMessage( hwndDlg, DM_SETDEFID, idButton, 0L );
    style = GetWindowLong(GetDlgItem(hwndDlg, idButton), GWL_STYLE)| BS_DEFPUSHBUTTON;
    SendMessage( GetDlgItem(hwndDlg, idButton),
                 BM_SETSTYLE,
                 MAKEWPARAM( style, 0 ),
                 MAKELPARAM( TRUE, 0 ));
    
    TraceLeave();
}


/*-----------------------------------------------------------------------------
/ Data collection functions
/----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
/ AllocStorageMedium
/ ------------------
/   Allocate a storage medium (validating the clipboard format as required).
/
/ In:
/   pFmt, pMedium -> describe the allocation
/   cbStruct = size of allocation
/   ppAlloc -> receives a pointer to the allocation / = NULL
/
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
EXTERN_C HRESULT AllocStorageMedium(FORMATETC* pFmt, STGMEDIUM* pMedium, SIZE_T cbStruct, LPVOID* ppAlloc)
{
    HRESULT hr;

    TraceEnter(TRACE_COMMON_MISC, "AllocStorageMedium");

    TraceAssert(pFmt);
    TraceAssert(pMedium);

    // Validate parameters

    if ( ( cbStruct <= 0 ) || !( pFmt->tymed & TYMED_HGLOBAL ) )
        ExitGracefully(hr, E_INVALIDARG, "Zero size stored medium requested or non HGLOBAL");

    if ( ( pFmt->ptd ) || !( pFmt->dwAspect & DVASPECT_CONTENT) || !( pFmt->lindex == -1 ) )
        ExitGracefully(hr, E_INVALIDARG, "Bad format requested");

    // Allocate the medium via GlobalAlloc

    pMedium->tymed = TYMED_HGLOBAL;
    pMedium->hGlobal = GlobalAlloc(GPTR, cbStruct);
    pMedium->pUnkForRelease = NULL;

    if ( !pMedium->hGlobal )
        ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate StgMedium");
    
    hr = S_OK;                  // success

exit_gracefully:

    if ( ppAlloc )
        *ppAlloc = SUCCEEDED(hr) ? (LPVOID)pMedium->hGlobal:NULL;

    TraceLeaveResult(hr);
}


/*-----------------------------------------------------------------------------
/ CopyStorageMedium
/ ------------------
/   Copies a storage medium (and the data in an HGLOBAL).  Only works
/   for TYMED_HGLOBAL mediums...
/
/ In:
/   pMediumDst -> where to copy to...
/   pFmt, pMediumSrc -> describe the source
/
/ Out:
/   HRESULT
/----------------------------------------------------------------------------*/
EXTERN_C HRESULT CopyStorageMedium(FORMATETC* pFmt, STGMEDIUM* pMediumDst, STGMEDIUM* pMediumSrc)
{
    HRESULT hr;
    LPVOID pSrc, pDst;
    HGLOBAL hGlobal;
    SIZE_T cbStruct;

    TraceEnter(TRACE_COMMON_MISC, "CopyStorageMedium");

    if ( !(pFmt->tymed & TYMED_HGLOBAL) )
        ExitGracefully(hr, E_INVALIDARG, "Only HGLOBAL mediums suppported to copy");

    // stored in a HGLOBAl, therefore get the size, allocate a new storage
    // object and copy the data away into it.

    cbStruct = GlobalSize((HGLOBAL)pMediumSrc->hGlobal);

    hr = AllocStorageMedium(pFmt, pMediumDst, cbStruct, (LPVOID*)&hGlobal);
    FailGracefully( hr, "Unable to allocated storage medium" );

    *pMediumDst = *pMediumSrc;
    pMediumDst->hGlobal = hGlobal;

    pSrc = GlobalLock(pMediumSrc->hGlobal);
    pDst = GlobalLock(pMediumDst->hGlobal);

    CopyMemory(pDst, pSrc, cbStruct);

    GlobalUnlock(pMediumSrc->hGlobal);
    GlobalUnlock(pMediumDst->hGlobal);

    hr = S_OK;                      // success

exit_gracefully:

    TraceLeaveResult(hr);
}


/*-----------------------------------------------------------------------------
/ GetStringFromGUID
/ -----------------
/   Given a GUID convert it to a string.
/
/ In:
/   rGUID = guid to be converted
/   psz, cchMax = buffer to fill
/
/ Out:
/   NUMBER OF characters
/----------------------------------------------------------------------------*/

static const BYTE c_rgbGuidMap[] = { 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-', 8, 9, '-', 10, 11, 12, 13, 14, 15 };
static const TCHAR c_szDigits[] = TEXT("0123456789ABCDEF");

EXTERN_C INT GetStringFromGUID(UNALIGNED REFGUID rguid, LPTSTR psz, INT cchMax)
{
    INT i;
    const BYTE* pBytes = (const BYTE*)&rguid;

    if ( cchMax < GUIDSTR_MAX )
        return 0;

#ifdef BIG_ENDIAN
    // This is the slow, but portable version
    wsprintf(psz, TEXT("{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"),
                    rguid->Data1, rguid->Data2, rguid->Data3,
                    rguid->Data4[0], rguid->Data4[1],
                    rguid->Data4[2], rguid->Data4[3],
                    rguid->Data4[4], rguid->Data4[5],
                    rguid->Data4[6], rguid->Data4[7]);
#else
    // The following algorithm is faster than the wsprintf.
    *psz++ = TEXT('{');

    for (i = 0; i < SIZEOF(c_rgbGuidMap); i++)
    {
        if (c_rgbGuidMap[i] == TEXT('-'))      // don't TEXT() this line
        {
            *psz++ = TEXT('-');
        }
        else
        {
            // Convert a byte-value into a character representation
            *psz++ = c_szDigits[ (pBytes[c_rgbGuidMap[i]] & 0xF0) >> 4 ];
            *psz++ = c_szDigits[ (pBytes[c_rgbGuidMap[i]] & 0x0F) ];
        }
    }
    *psz++ = TEXT('}');
    *psz   = TEXT('\0');
#endif /* !BIG_ENDIAN */

    return GUIDSTR_MAX;
}


/*-----------------------------------------------------------------------------
/ GetGUIDFromString
/ -----------------
/   Given a string convert it to a GUID.
/
/ In:
/   psz -> string to be parsed
/   rGUID = GUID return into
/
/ Out:
/   BOOL
/----------------------------------------------------------------------------*/

BOOL _HexStringToDWORD(LPCTSTR * ppsz, DWORD * lpValue, int cDigits, TCHAR chDelim)
{
    int ich;
    LPCTSTR psz = *ppsz;
    DWORD Value = 0;
    BOOL fRet = TRUE;

    for (ich = 0; ich < cDigits; ich++)
    {
        TCHAR ch = psz[ich];
        if (InRange(ch, TEXT('0'), TEXT('9')))
        {
            Value = (Value << 4) + ch - TEXT('0');
        }
        else if ( InRange( (ch |= (TEXT('a')-TEXT('A'))), TEXT('a'), TEXT('f')) )
        {
            Value = (Value << 4) + ch - TEXT('a') + 10;
        }
        else
            return(FALSE);
    }

    if (chDelim)
    {
        fRet = (psz[ich++] == chDelim);
    }

    *lpValue = Value;
    *ppsz = psz+ich;

    return fRet;
}

#ifndef UNICODE

EXTERN_C BOOL GetGUIDFromStringW(LPCWSTR psz, GUID* pguid)
{
    USES_CONVERSION;
    return GetGUIDFromString(W2CT(psz), pguid);
}

#endif

EXTERN_C BOOL GetGUIDFromString(LPCTSTR psz, GUID* pguid)
{
    DWORD dw;
    if (*psz++ != TEXT('{') /*}*/ )
        return FALSE;

    if (!_HexStringToDWORD(&psz, &pguid->Data1, SIZEOF(DWORD)*2, TEXT('-')))
        return FALSE;

    if (!_HexStringToDWORD(&psz, &dw, SIZEOF(WORD)*2, TEXT('-')))
        return FALSE;

    pguid->Data2 = (WORD)dw;

    if (!_HexStringToDWORD(&psz, &dw, SIZEOF(WORD)*2, TEXT('-')))
        return FALSE;

    pguid->Data3 = (WORD)dw;

    if (!_HexStringToDWORD(&psz, &dw, SIZEOF(BYTE)*2, 0))
        return FALSE;

    pguid->Data4[0] = (BYTE)dw;

    if (!_HexStringToDWORD(&psz, &dw, SIZEOF(BYTE)*2, TEXT('-')))
        return FALSE;

    pguid->Data4[1] = (BYTE)dw;

    if (!_HexStringToDWORD(&psz, &dw, SIZEOF(BYTE)*2, 0))
        return FALSE;

    pguid->Data4[2] = (BYTE)dw;

    if (!_HexStringToDWORD(&psz, &dw, SIZEOF(BYTE)*2, 0))
        return FALSE;

    pguid->Data4[3] = (BYTE)dw;

    if (!_HexStringToDWORD(&psz, &dw, SIZEOF(BYTE)*2, 0))
        return FALSE;

    pguid->Data4[4] = (BYTE)dw;

    if (!_HexStringToDWORD(&psz, &dw, SIZEOF(BYTE)*2, 0))
        return FALSE;

    pguid->Data4[5] = (BYTE)dw;

    if (!_HexStringToDWORD(&psz, &dw, SIZEOF(BYTE)*2, 0))
        return FALSE;

    pguid->Data4[6] = (BYTE)dw;
    if (!_HexStringToDWORD(&psz, &dw, SIZEOF(BYTE)*2, /*(*/ TEXT('}')))
        return FALSE;

    pguid->Data4[7] = (BYTE)dw;

    return TRUE;
}


//
// Replacement for LoadStringW that will work on the Win95 downlevel client
//

#ifndef UNICODE

EXTERN_C INT MyLoadStringW(HINSTANCE hInstance, UINT uID, LPWSTR pszBuffer, INT cchBuffer)
{
    TCHAR szBuffer[MAX_PATH];
    INT cchResult;

    cchResult = LoadString(hInstance, uID, szBuffer, ARRAYSIZE(szBuffer));
    MultiByteToWideChar(CP_ACP, 0, szBuffer, -1, pszBuffer, cchBuffer);
    
    return cchResult;
}

#endif
