//+---------------------------------------------------------------------------
//
//  Copyright (C) Microsoft Corporation, 1994-1995
//
//  File:       unicwrap.cpp
//
//  Contents:   Wrappers for all Unicode functions used in MSHTML.
//              Any Unicode parameters/structure fields/buffers are converted
//              to ANSI, and then the corresponding ANSI version of the function
//              is called.
//
//----------------------------------------------------------------------------

// This file expects to be compiled UNICODE

//----------------------------------------------------------------------------
//
//  HOW TO WRITE A WRAPPER
//
//----------------------------------------------------------------------------
//
//  Suppose you want to write a wrapper for CreateMutex.
//
//  -   Look up CreateMutex and see that it is a function in KERNEL32.DLL.
//      If your function is in some other DLL, make the appropriate changes
//      to the instructions below.
//
//  -   Write a wrapper for your function, wrapped inside the appropriate
//      #ifdef for the DLL being wrapped.
//
//          #ifdef NEED_KERNEL32_WRAPPER
//          STDAPI_(HANDLE) WINAPI
//          CreateMutexWrapW(...)
//          {
//              VALIDATE_PROTOTYPE(CreateMutex);
//              blahblahblah
//          }
//          #endif // NEED_KERNEL32_WRAPPER
//
//  -   If your wrapped function is in a DLL other than KERNEL32,
//      USER32, GDI32, or ADVAPI32, then you need to write a delay-load
//      stub in dllload.c.
//
//      Furthermore, if the delay-load stub uses DELAY_MAP instead of
//      DELAY_LOAD (SHELL32, WINMM, COMDLG32), then the line
//
//          VALIDATE_PROTOTYPE(WrappedFunction)
//
//      needs to be changed to
//
//          VALIDATE_PROTOTYPE_DELAYLOAD(WrappedFunction, _WrappedFunction)
//
//  -   Add a line for your function to thunk.h.  In our case, we would
//      look for the #ifndef NEED_KERNEL32_WRAPPER section and add the
//      line
//
//          #define CreateMutexWrapW CreateMutexW
//
//      (This prevents shlwapi from trying to call a nonexistent wrapper.)
//
//  -   Add a line for your function to shlwapi.src.
//
//          KERNEL32_WRAP(CreateMutexW)     @nnn NONAME PRIVATE
//
//  -   If your wrapper is high-frequency, you may want to use
//      FORWARD_AW or THUNK_AW to make it faster on NT.
//
//----------------------------------------------------------------------------

// WARNING: if you add a new call to FORWARD_AW or THUNK_AW (or modify the
// macros), make sure you build this file on both x86 and non-x86 (or
// alternately, build w/ PERF_ASM=0 on x86) to make sure there are no
// syntax errors.  the macros are *very* different between x86 and non-x86,
// so successful compilation on one platform in no way guarantees success
// on the other.  i speak from experience, not theory...

#include "priv.h"
#define _SHELL32_
#define _SHDOCVW_
#include <shellapi.h>
#include <commctrl.h>       // for treeview
#include "unicwrap.h"
#include <wininet.h>            // INTERNET_MAX_URL_LENGTH
#include <winnetp.h>
#include "mlui.h"

#include <platform.h>
#include "apithk.h"

#define DM_PERF     0    // perf stats

#ifndef ANSI_SHELL32_ON_UNIX
#define UseUnicodeShell32() ( g_bRunningOnNT )
#else
#define UseUnicodeShell32() ( FALSE )
#endif

//
//  Do this in every wrapper function to make sure the wrapper
//  prototype matches the function it is intending to replace.
//
#define VALIDATE_PROTOTYPE(f) if (f##W == f##WrapW) 0
#define VALIDATE_PROTOTYPEA(f) if (f##A == f##WrapA) 0
#define VALIDATE_PROTOTYPEX(f) if (f == f##Wrap) 0
#define VALIDATE_PROTOTYPE_DELAYLOAD(fWrap, fDelay) if (fDelay##W == fWrap##WrapW) 0
#define VALIDATE_PROTOTYPE_DELAYLOADX(fWrap, fDelay) if (fDelay == fWrap##Wrap) 0
#define VALIDATE_PROTOTYPE_NO_W(f) if (f## == f##Wrap) 0


//
//  Do this in every wrapper function that has an output parameter.
//  It raises assertion failures on the main code path so that
//  the same assertions are raised on NT and 95.  The CStrOut class
//  doesn't like it when you say that an output buffer is NULL yet
//  has nonzero length.  Without this macro, the bug would go undetected
//  on NT and appear only on Win95.
//
// FEATURE raymondc - Turn this on after fixing all the RIPs it catches
// (otherwise we assert too much)
#if 0
#define VALIDATE_OUTBUF(s, cch) ASSERT((s) != NULL || (cch) == 0)
#else
#define VALIDATE_OUTBUF(s, cch)
#endif

// compiler should do this opt for us (call-thunk => jmp), but it doesn't
// so we do it ourselves (i raided it and they're adding it to vc6.x so
// hopefully we'll get it someday)
#if _X86_
#define PERF_ASM        1       // turn on inline-asm opts
#endif

// todo??? #ifdef SUNDOWN    #undef PERF_ASM

#if PERF_ASM // {

// APPCOMPAT workaround compiler bug
// compiler should know this, but doesn't, so we make it explicit
#define IMPORT_PTR  dword ptr       // doesn't work for sundown


//***   FORWARD_AW, THUNK_AW -- simple forwarders and thunks
// ENTRY/EXIT
//  - declare function w/ FORWARD_API
//  - if you're using THUNK_AW, create the 'A' thunk helper
//  - make the body FORWARD_AW or THUNK_AW.
//  - make sure there's *no* other code in the func.  o.w. you'll get bogus
//  code.
// EXAMPLE
//  int FORWARD_API WINAPI FooWrapW(int i, void *p)
//  {
//      VALIDATE_PROTOTYPE(Foo);
//
//      FORWARD_AW(Foo, (i, p));
//  }
//
//  int WINAPI BarAThunk(HWND hwnd, WPARAM wParam)
//  {
//      ... ansi thunk helper ...
//  }
//
//  int FORWARD_API WINAPI BarWrapW(HWND hwnd, WPARAM wParam)
//  {
//      VALIDATE_PROTOTYPE(Bar);
//
//      THUNK_AW(Bar, (hwnd, wParam));
//  }
// NOTES
//  - WARNING: can only be used for 'simple' thunks (NAKED => no non-global
//  vars, etc.).
//  - WARNING: calling func must be declared FORWARD_API.  if not you'll
//  get bogus code.  happily if you forget you get the (obscure) error
//  message "error C4035: 'FooW': no return value"
//  - note that the macro ends up w/ an extra ";" from the caller, oh well...
//  - TODO: perf: better still would be to have a g_pfnCallWndProc, init
//  it 1x, and then jmp indirect w/o the test.  it would cost us a ptr but
//  we only do it for the top-2 funcs (CallWindowProc and SendMessage)
#define FORWARD_API     _declspec(naked)

#define FORWARD_AW(_fn, _args) \
    if (g_bRunningOnNT) { \
        _asm { jmp     IMPORT_PTR _fn##W } \
    } \
    _asm { jmp     IMPORT_PTR _fn##A }

#define THUNK_AW(_fn, _args) \
    if (g_bRunningOnNT) { \
        _asm { jmp     IMPORT_PTR _fn##W } \
    } \
    _asm { jmp     _fn##AThunk }    // n.b. no IMPORT_PTR

#else // }{

#define FORWARD_API     /*NOTHING*/

#define FORWARD_AW(_fn, _args) \
    if (g_bRunningOnNT) { \
        return _fn##W _args; \
    } \
    return _fn##A _args;

#define THUNK_AW(_fn, _args) \
    if (g_bRunningOnNT) { \
        return _fn##W _args; \
    } \
    return _fn##AThunk _args;

#endif // }

//
//  Windows 95 and NT5 do not have the hbmpItem field in their MENUITEMINFO
//  structure.
//
#if (_WIN32_WINNT >= 0x0500)
#define MENUITEMINFOSIZE_WIN95  FIELD_OFFSET(MENUITEMINFOW, hbmpItem)
#else
#define MENUITEMINFOSIZE_WIN95  sizeof(MENUITEMINFOW)
#endif


//
// FEATURE - Undefine functions that are defined in thunk.h
//          These are duplicates of functions defined here
//          and should be deleted from thunk.c.

#undef RegSetValueExW
#undef CompareStringW
#undef GetFileAttributesW
#undef GetFullPathNameW
#undef SearchPathW
#undef GetWindowsDirectoryW
#undef GetSystemDirectoryW
#undef GetEnvironmentVariableW

// Undefine mappings
#undef CharLowerW


//
// FEATURE - There are numerous registry functions that are duplicated
//          here and in reg.c
//

//
//  Some W functions are implemented on Win95, so complain if anybody
//  writes thunks for them.
//
//  Though Win95's implementation of TextOutW is incomplete for FE languages.
//  Remove this section when we implement FE-aware TextOutW for Win95.
//
#if defined(TextOutWrap)
#error Do not write thunks for TextOutW; Win95 supports it.
#endif

//+---------------------------------------------------------------------------
//
//  Member:     CConvertStr::Free
//
//  Synopsis:   Frees string if alloc'd and initializes to NULL.
//
//----------------------------------------------------------------------------

void
CConvertStr::Free()
{
    if (_pstr != _ach && HIWORD64(_pstr) != 0 && !IsAtom())
    {
        delete [] _pstr;
    }

    _pstr = NULL;
}


//+---------------------------------------------------------------------------
//
//  Member:     CConvertStrW::Free
//
//  Synopsis:   Frees string if alloc'd and initializes to NULL.
//
//----------------------------------------------------------------------------

void
CConvertStrW::Free()
{
    if (_pwstr != _awch && HIWORD64(_pwstr) != 0)
    {
        delete [] _pwstr;
    }

    _pwstr = NULL;
}


//+---------------------------------------------------------------------------
//
//  Member:     CStrInW::Init
//
//  Synopsis:   Converts a LPSTR function argument to a LPWSTR.
//
//  Arguments:  [pstr] -- The function argument.  May be NULL or an atom
//                              (HIWORD64(pwstr) == 0).
//
//              [cch]  -- The number of characters in the string to
//                          convert.  If -1, the string is assumed to be
//                          NULL terminated and its length is calculated.
//
//  Modifies:   [this]
//
//----------------------------------------------------------------------------

void
CStrInW::Init(LPCSTR pstr, int cch)
{
    int cchBufReq;

    _cwchLen = 0;

    // Check if string is NULL or an atom.
    if (HIWORD64(pstr) == 0)
    {
        _pwstr = (LPWSTR) pstr;
        return;
    }

    ASSERT(cch == -1 || cch > 0);

    //
    // Convert string to preallocated buffer, and return if successful.
    //
    // Since the passed in buffer may not be null terminated, we have
    // a problem if cch==ARRAYSIZE(_awch), because MultiByteToWideChar
    // will succeed, and we won't be able to null terminate the string!
    // Decrease our buffer by one for this case.
    //
    _cwchLen = MultiByteToWideChar(
            CP_ACP, 0, pstr, cch, _awch, ARRAYSIZE(_awch)-1);

    if (_cwchLen > 0)
    {
        // Some callers don't NULL terminate.
        //
        // We could check "if (-1 != cch)" before doing this,
        // but always doing the null is less code.
        //
        _awch[_cwchLen] = 0;

        if (0 == _awch[_cwchLen-1]) // account for terminator
            _cwchLen--;

        _pwstr = _awch;
        return;
    }

    //
    // Alloc space on heap for buffer.
    //

    cchBufReq = MultiByteToWideChar( CP_ACP, 0, pstr, cch, NULL, 0 );

    // Again, leave room for null termination
    cchBufReq++;

    ASSERT(cchBufReq > 0);
    _pwstr = new WCHAR[cchBufReq];
    if (!_pwstr)
    {
        // On failure, the argument will point to the empty string.
        _awch[0] = 0;
        _pwstr = _awch;
        return;
    }

    ASSERT(HIWORD64(_pwstr));
    _cwchLen = MultiByteToWideChar(
            CP_ACP, 0, pstr, cch, _pwstr, cchBufReq );

#if DBG == 1 /* { */
    if (0 == _cwchLen)
    {
        int errcode = GetLastError();
        ASSERT(0 && "MultiByteToWideChar failed in unicode wrapper.");
    }
#endif /* } */

    // Again, make sure we're always null terminated
    ASSERT(_cwchLen < cchBufReq);
    _pwstr[_cwchLen] = 0;

    if (0 == _pwstr[_cwchLen-1]) // account for terminator
        _cwchLen--;

}


//+---------------------------------------------------------------------------
//
//  Member:     CStrIn::CStrIn
//
//  Synopsis:   Inits the class.
//
//  NOTE:       Don't inline this function or you'll increase code size
//              by pushing -1 on the stack for each call.
//
//----------------------------------------------------------------------------

CStrIn::CStrIn(LPCWSTR pwstr) : CConvertStr(CP_ACP)
{
    Init(pwstr, -1);
}


CStrIn::CStrIn(UINT uCP, LPCWSTR pwstr) : CConvertStr(uCP)
{
    Init(pwstr, -1);
}

//+---------------------------------------------------------------------------
//
//  Member:     CStrIn::Init
//
//  Synopsis:   Converts a LPWSTR function argument to a LPSTR.
//
//  Arguments:  [pwstr] -- The function argument.  May be NULL or an atom
//                              (HIWORD(pwstr) == 0).
//
//              [cwch]  -- The number of characters in the string to
//                          convert.  If -1, the string is assumed to be
//                          NULL terminated and its length is calculated.
//
//  Modifies:   [this]
//
//  Note:       We ignore AreFileApisANSI() and always use CP_ACP.
//              The reason is that nobody uses SetFileApisToOEM() except
//              console apps, and once you set file APIs to OEM, you
//              cannot call shell/user/gdi APIs, since they assume ANSI
//              regardless of the FileApis setting.  So you end up in
//              this horrible messy state where the filename APIs interpret
//              the strings as OEM but SHELL32 interprets the strings
//              as ANSI and you end up with a big mess.
//
//----------------------------------------------------------------------------

void
CStrIn::Init(LPCWSTR pwstr, int cwch)
{
    int cchBufReq;

#if DBG == 1 /* { */
    int errcode;
#endif /* } */

    _cchLen = 0;

    // Check if string is NULL or an atom.
    if (HIWORD64(pwstr) == 0 || IsAtom())
    {
        _pstr = (LPSTR) pwstr;
        return;
    }

    if ( cwch == 0 )
    {
        *_ach = '\0';
        _pstr = _ach;
        return;
    }

    //
    // Convert string to preallocated buffer, and return if successful.
    //

    _cchLen = WideCharToMultiByte(
            _uCP, 0, pwstr, cwch, _ach, ARRAYSIZE(_ach)-1, NULL, NULL);

    if (_cchLen > 0)
    {
        // This is DBCS safe since byte before _cchLen is last character
        _ach[_cchLen] = 0;
        // this may not be safe if the last character
        // was a multibyte character...
        if (_ach[_cchLen-1]==0)
            _cchLen--;          // account for terminator
        _pstr = _ach;
        return;
    }


    cchBufReq = WideCharToMultiByte(
            CP_ACP, 0, pwstr, cwch, NULL, 0, NULL, NULL);

    cchBufReq++;

    ASSERT(cchBufReq > 0);
    _pstr = new char[cchBufReq];
    if (!_pstr)
    {
        // On failure, the argument will point to the empty string.
        _ach[0] = 0;
        _pstr = _ach;
        return;
    }

    ASSERT(HIWORD64(_pstr));
    _cchLen = WideCharToMultiByte(
            _uCP, 0, pwstr, cwch, _pstr, cchBufReq, NULL, NULL);
#if DBG == 1 /* { */
    if (_cchLen < 0)
    {
        errcode = GetLastError();
        ASSERT(0 && "WideCharToMultiByte failed in unicode wrapper.");
    }
#endif /* } */

    // Again, make sure we're always null terminated
    ASSERT(_cchLen < cchBufReq);
    _pstr[_cchLen] = 0;
    if (0 == _pstr[_cchLen-1]) // account for terminator
        _cchLen--;
}

//+---------------------------------------------------------------------------
//
//  Member:     CStrInMulti::CStrInMulti
//
//  Synopsis:   Converts mulitple LPWSTRs to a multiple LPSTRs.
//
//  Arguments:  [pwstr] -- The strings to convert.
//
//  Modifies:   [this]
//
//----------------------------------------------------------------------------

CStrInMulti::CStrInMulti(LPCWSTR pwstr)
{
    LPCWSTR pwstrT;

    // We don't handle atoms because we don't need to.
    ASSERT(HIWORD64(pwstr));

    //
    // Count number of characters to convert.
    //

    pwstrT = pwstr;
    if (pwstr)
    {
        do {
            while (*pwstrT++)
                ;

        } while (*pwstrT++);
    }

    Init(pwstr, (int)(pwstrT - pwstr));
}


//+---------------------------------------------------------------------------
//
//  Member:     CPPFIn::CPPFIn
//
//  Synopsis:   Inits the class.  Truncates the filename to MAX_PATH
//              so Win9x DBCS won't fault.  Win9x SBCS silently truncates
//              to MAX_PATH, so we're bug-for-bug compatible.
//
//----------------------------------------------------------------------------

CPPFIn::CPPFIn(LPCWSTR pwstr)
{
    SHUnicodeToAnsi(pwstr, _ach, ARRAYSIZE(_ach));
}

//+---------------------------------------------------------------------------
//
//  Member:     CStrOut::CStrOut
//
//  Synopsis:   Allocates enough space for an out buffer.
//
//  Arguments:  [pwstr]   -- The Unicode buffer to convert to when destroyed.
//                              May be NULL.
//
//              [cwchBuf] -- The size of the buffer in characters.
//
//  Modifies:   [this].
//
//----------------------------------------------------------------------------

CStrOut::CStrOut(LPWSTR pwstr, int cwchBuf) : CConvertStr(CP_ACP)
{
    Init(pwstr, cwchBuf);
}

CStrOut::CStrOut(UINT uCP, LPWSTR pwstr, int cwchBuf) : CConvertStr(uCP)
{
    Init(pwstr, cwchBuf);
}

void
CStrOut::Init(LPWSTR pwstr, int cwchBuf) 
{
    ASSERT(cwchBuf >= 0);

    _pwstr = pwstr;
    _cwchBuf = cwchBuf;

    if (!pwstr)
    {
        // Force cwchBuf = 0 because many callers (in particular, registry
        // munging functions) pass garbage as the length because they know
        // it will be ignored.
        _cwchBuf = 0;
        _pstr = NULL;
        return;
    }

    ASSERT(HIWORD64(pwstr));

    // Initialize buffer in case Windows API returns an error.
    _ach[0] = 0;

    // Use preallocated buffer if big enough.
    if (cwchBuf * 2 <= ARRAYSIZE(_ach))
    {
        _pstr = _ach;
        return;
    }

    // Allocate buffer.
    _pstr = new char[cwchBuf * 2];
    if (!_pstr)
    {
        //
        // On failure, the argument will point to a zero-sized buffer initialized
        // to the empty string.  This should cause the Windows API to fail.
        //

        ASSERT(cwchBuf > 0);
        _pwstr[0] = 0;
        _cwchBuf = 0;
        _pstr = _ach;
        return;
    }

    ASSERT(HIWORD64(_pstr));
    _pstr[0] = 0;
}

//+---------------------------------------------------------------------------
//
//  Member:     CStrOutW::CStrOutW
//
//  Synopsis:   Allocates enough space for an out buffer.
//
//  Arguments:  [pstr]    -- The MBCS buffer to convert to when destroyed.
//                              May be NULL.
//
//              [cchBuf]  -- The size of the buffer in characters.
//
//  Modifies:   [this].
//
//----------------------------------------------------------------------------

CStrOutW::CStrOutW(LPSTR pstr, int cchBuf)
{
    ASSERT(cchBuf >= 0);

    _pstr = pstr;
    _cchBuf = cchBuf;

    if (!pstr)
    {
        // Force cchBuf = 0 because many callers (in particular, registry
        // munging functions) pass garbage as the length because they know
        // it will be ignored.
        _cchBuf = 0;
        _pwstr = NULL;
        return;
    }

    ASSERT(HIWORD64(pstr));

    // Initialize buffer in case Windows API returns an error.
    _awch[0] = 0;

    // Use preallocated buffer if big enough.
    if (cchBuf <= ARRAYSIZE(_awch))
    {
        _pwstr = _awch;
        return;
    }

    // Allocate buffer.
    _pwstr = new WCHAR[cchBuf];
    if (!_pwstr)
    {
        //
        // On failure, the argument will point to a zero-sized buffer initialized
        // to the empty string.  This should cause the Windows API to fail.
        //

        ASSERT(cchBuf > 0);
        _pstr[0] = 0;
        _cchBuf = 0;
        _pwstr = _awch;
        return;
    }

    ASSERT(HIWORD64(_pwstr));
    _pwstr[0] = 0;
}

//+---------------------------------------------------------------------------
//
//  Member:     CStrOut::ConvertIncludingNul
//
//  Synopsis:   Converts the buffer from MBCS to Unicode
//
//  Return:     Character count INCLUDING the trailing '\0'
//
//----------------------------------------------------------------------------

int
CStrOut::ConvertIncludingNul()
{
    int cch;

    if (!_pstr)
        return 0;

    cch = SHAnsiToUnicodeCP(_uCP, _pstr, _pwstr, _cwchBuf);

#if DBG == 1 /* { */
    if (cch == 0 && _cwchBuf > 0)
    {
        int errcode = GetLastError();
        ASSERT(0 && "SHAnsiToUnicode failed in unicode wrapper.");
    }
#endif /* } */

    Free();
    return cch;
}

//+---------------------------------------------------------------------------
//
//  Member:     CStrOutW::ConvertIncludingNul
//
//  Synopsis:   Converts the buffer from Unicode to MBCS
//
//  Return:     Character count INCLUDING the trailing '\0'
//
//----------------------------------------------------------------------------

int
CStrOutW::ConvertIncludingNul()
{
    int cch;

    if (!_pwstr)
        return 0;

    cch = SHUnicodeToAnsi(_pwstr, _pstr, _cchBuf);

#if DBG == 1 /* { */
    if (cch == 0 && _cchBuf > 0)
    {
        int errcode = GetLastError();
        ASSERT(0 && "SHUnicodeToAnsi failed in unicode wrapper.");
    }
#endif /* } */

    Free();
    return cch;
}

//+---------------------------------------------------------------------------
//
//  Member:     CStrOut::ConvertExcludingNul
//
//  Synopsis:   Converts the buffer from MBCS to Unicode
//
//  Return:     Character count EXCLUDING the trailing '\0'
//
//----------------------------------------------------------------------------

int
CStrOut::ConvertExcludingNul()
{
    int ret = ConvertIncludingNul();
    if (ret > 0)
    {
        ret -= 1;
    }
    return ret;
}


//+---------------------------------------------------------------------------
//
//  Member:     CStrOut::~CStrOut
//
//  Synopsis:   Converts the buffer from MBCS to Unicode.
//
//  Note:       Don't inline this function, or you'll increase code size as
//              both ConvertIncludingNul() and CConvertStr::~CConvertStr will be
//              called inline.
//
//----------------------------------------------------------------------------

CStrOut::~CStrOut()
{
    ConvertIncludingNul();
}

//+---------------------------------------------------------------------------
//
//  Member:     CStrOutW::~CStrOutW
//
//  Synopsis:   Converts the buffer from Unicode to MBCS.
//
//  Note:       Don't inline this function, or you'll increase code size as
//              both ConvertIncludingNul() and CConvertStr::~CConvertStr will be
//              called inline.
//
//----------------------------------------------------------------------------

CStrOutW::~CStrOutW()
{
    ConvertIncludingNul();
}

#ifdef NEED_KERNEL32_WRAPPER
//+---------------------------------------------------------------------------
//
//  Class:      CWin32FindDataInOut
//
//  Purpose:    Converts WIN32_FIND_DATA structures from UNICODE to ANSI
//              on the way in, then ANSI to UNICODE on the way out.
//
//----------------------------------------------------------------------------

class CWin32FindDataInOut
{
public:
    operator LPWIN32_FIND_DATAA();
    CWin32FindDataInOut(LPWIN32_FIND_DATAW pfdW);
    ~CWin32FindDataInOut();

protected:
    LPWIN32_FIND_DATAW _pfdW;
    WIN32_FIND_DATAA _fdA;
};

//+---------------------------------------------------------------------------
//
//  Member:     CWin32FindDataInOut::CWin32FindDataInOut
//
//  Synopsis:   Convert the non-string fields to ANSI.  You'd think this
//              isn't necessary, but it is, because Win95 puts secret
//              goo into the dwReserved fields that must be preserved.
//
//----------------------------------------------------------------------------

inline
CWin32FindDataInOut::CWin32FindDataInOut(LPWIN32_FIND_DATAW pfdW) :
    _pfdW(pfdW)
{
    memcpy(&_fdA, _pfdW, FIELD_OFFSET(WIN32_FIND_DATA, cFileName));

    _fdA.cFileName[0]          = '\0';
    _fdA.cAlternateFileName[0] = '\0';
}

//+---------------------------------------------------------------------------
//
//  Member:     CWin32FindDataInOut::~CWin32FindDataInOut
//
//  Synopsis:   Convert all the fields from ANSI back to UNICODE.
//
//----------------------------------------------------------------------------

inline
CWin32FindDataInOut::~CWin32FindDataInOut()
{
    memcpy(_pfdW, &_fdA, FIELD_OFFSET(WIN32_FIND_DATA, cFileName));

    SHAnsiToUnicode(_fdA.cFileName, _pfdW->cFileName, ARRAYSIZE(_pfdW->cFileName));
    SHAnsiToUnicode(_fdA.cAlternateFileName, _pfdW->cAlternateFileName, ARRAYSIZE(_pfdW->cAlternateFileName));
}

//+---------------------------------------------------------------------------
//
//  Member:     CWin32FindDataInOut::operator LPWIN32_FIND_DATAA
//
//  Synopsis:   Returns the WIN32_FIND_DATAA.
//
//----------------------------------------------------------------------------

inline
CWin32FindDataInOut::operator LPWIN32_FIND_DATAA()
{
    return &_fdA;
}
#endif // NEED_KERNEL32_WRAPPER

//+------------------------------------------------------------------------
//
//  Implementation of the wrapped functions
//
//-------------------------------------------------------------------------

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

BOOL WINAPI
AppendMenuWrapW(
        HMENU   hMenu,
        UINT    uFlags,
        UINT_PTR uIDnewItem,
        LPCWSTR lpnewItem)
{
    VALIDATE_PROTOTYPE(AppendMenu);

    // Make the InsertMenu wrapper do all the work
    return InsertMenuWrapW(hMenu, (UINT)-1,
                           uFlags | MF_BYPOSITION, uIDnewItem, lpnewItem);
}

UINT GetLocaleAnsiCodePage(LCID Locale) 
{
    WCHAR szCodePage[7];

    UINT uiRetVal;
    
    if (0 != GetLocaleInfoWrapW(Locale, LOCALE_IDEFAULTANSICODEPAGE, szCodePage, ARRAYSIZE(szCodePage)))    
    {
        uiRetVal = StrToInt(szCodePage);
    }
    else
    {
        uiRetVal = CP_ACP;
    }

    return uiRetVal;
}

#ifdef NEED_USER32_WRAPPER

LRESULT FORWARD_API WINAPI
CallWindowProcWrapW(
    WNDPROC lpPrevWndFunc,
    HWND    hWnd,
    UINT    Msg,
    WPARAM  wParam,
    LPARAM  lParam)
{
    VALIDATE_PROTOTYPE(CallWindowProc);

    // perf: better still would be to have a g_pfnCallWndProc, init it 1x,
    // and then jmp indirect w/o the test.  it would cost us a ptr but we
    // only do it for the top-2 funcs (CallWindowProc and SendMessage)
    FORWARD_AW(CallWindowProc, (lpPrevWndFunc, hWnd, Msg, wParam, lParam));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

STDAPI_(BOOL FORWARD_API) CallMsgFilterWrapW(LPMSG lpMsg, int nCode)
{
    VALIDATE_PROTOTYPE(CallMsgFilter);

    FORWARD_AW(CallMsgFilter, (lpMsg, nCode));
}

#endif // NEED_USER32_WRAPPER



//----------------------------------------------------------------------
//
// function:    CharLowerWrapW( LPWSTR pch )
//
// purpose:     Converts character to lowercase.  Takes either a pointer
//              to a string, or a character masquerading as a pointer.
//              In the later case, the HIWORD must be zero.  This is
//              as spec'd for Win32.
//
// returns:     Lowercased character or string.  In the string case,
//              the lowercasing is done inplace.
//
//----------------------------------------------------------------------

#ifdef NEED_USER32_WRAPPER

LPWSTR WINAPI
CharLowerWrapW( LPWSTR pch )
{
    VALIDATE_PROTOTYPE(CharLower);

    if (g_bRunningOnNT)
    {
        return CharLowerW( pch );
    }

    if (!HIWORD64(pch))
    {
        WCHAR ch = (WCHAR)(LONG_PTR)pch;

        CharLowerBuffWrapW( &ch, 1 );

        pch = (LPWSTR)MAKEINTATOM(ch);
    }
    else
    {
        CharLowerBuffWrapW( pch, lstrlenW(pch) );
    }

    return pch;
}

#endif // NEED_USER32_WRAPPER


//----------------------------------------------------------------------
//
// function:    CharLowerBuffWrapW( LPWSTR pch, DWORD cch )
//
// purpose:     Converts a string to lowercase.  String must be cch
//              characters in length.
//
// returns:     Character count (cch).  The lowercasing is done inplace.
//
//----------------------------------------------------------------------

#ifdef NEED_USER32_WRAPPER

DWORD WINAPI
CharLowerBuffWrapW( LPWSTR pch, DWORD cchLength )
{
    VALIDATE_PROTOTYPE(CharLowerBuff);

    if (g_bRunningOnNT)
    {
        return CharLowerBuffW( pch, cchLength );
    }

    DWORD cch;

    for ( cch = cchLength; cch-- ; pch++ )
    {
        WCHAR ch = *pch;

        if (IsCharUpperWrapW(ch))
        {
            if (ch < 0x0100)
            {
                *pch += 32;             // Get Latin-1 out of the way first
            }
            else if (ch < 0x0531)
            {
                if (ch < 0x0391)
                {
                    if (ch < 0x01cd)
                    {
                        if (ch <= 0x178)
                        {
                            if (ch < 0x0178)
                            {
                                *pch += (ch == 0x0130) ? 0 : 1;
                            }
                            else
                            {
                                *pch -= 121;
                            }
                        }
                        else
                        {
                            static const BYTE abLookup[] =
                            {  // 0/8  1/9  2/a  3/b  4/c  5/d  6/e  7/f
            /* 0x0179-0x17f */           1,   0,   1,   0,   1,   0,   0,
            /* 0x0180-0x187 */      0, 210,   1,   0,   1,   0, 206,   1,
            /* 0x0188-0x18f */      0, 205, 205,   1,   0,   0,  79, 202,
            /* 0x0190-0x197 */    203,   1,   0, 205, 207,   0, 211, 209,
            /* 0x0198-0x19f */      1,   0,   0,   0, 211, 213,   0, 214,
            /* 0x01a0-0x1a7 */      1,   0,   1,   0,   1,   0,   0,   1,
            /* 0x01a8-0x1af */      0, 218,   0,   0,   1,   0, 218,   1,
            /* 0x01b0-0x1b7 */      0, 217, 217,   1,   0,   1,   0, 219,
            /* 0x01b8-0x1bf */      1,   0,   0,   0,   1,   0,   0,   0,
            /* 0x01c0-0x1c7 */      0,   0,   0,   0,   2,   0,   0,   2,
            /* 0x01c8-0x1cb */      0,   0,   2,   0
                            };

                            *pch += abLookup[ch-0x0179];
                        }
                    }
                    else if (ch < 0x0386)
                    {
                        switch (ch)
                        {
                            case 0x01f1: *pch += 2; break;
                            case 0x01f2: break;
                            default: *pch += 1;
                        }
                    }
                    else
                    {
                        static const BYTE abLookup[] =
                            { 38, 0, 37, 37, 37, 0, 64, 0, 63, 63 };

                        *pch += abLookup[ch-0x0386];
                    }
                }
                else
                {
                    if (ch < 0x0410)
                    {
                        if (ch < 0x0401)
                        {
                            if (ch < 0x03e2)
                            {
                                if (!InRange(ch, 0x03d2, 0x03d4) &&
                                    !(InRange(ch, 0x3da, 0x03e0) & !(ch & 1)))
                                {
                                    *pch += 32;
                                }
                            }
                            else
                            {
                                *pch += 1;
                            }
                        }
                        else
                        {
                            *pch += 80;
                        }
                    }
                    else
                    {
                        if (ch < 0x0460)
                        {
                            *pch += 32;
                        }
                        else
                        {
                            *pch += 1;
                        }
                    }
                }
            }
            else
            {
                if (ch < 0x2160)
                {
                    if (ch < 0x1fba)
                    {
                        if (ch < 0x1f08)
                        {
                            if (ch < 0x1e00)
                            {
                                *pch += 48;
                            }
                            else
                            {
                                *pch += 1;
                            }
                        }
                        else if (!(InRange(ch, 0x1f88, 0x1faf) && (ch & 15)>7))
                        {
                            *pch -= 8;
                        }
                    }
                    else
                    {
                        static const BYTE abLookup[] =
                        {  // 8    9    a    b    c    d    e    f
                              0,   0,  74,  74,   0,   0,   0,   0,
                             86,  86,  86,  86,   0,   0,   0,   0,
                              8,   8, 100, 100,   0,   0,   0,   0,
                              8,   8, 112, 112,   7,   0,   0,   0,
                            128, 128, 126, 126,   0,   0,   0,   0
                        };
                        int i = (ch-0x1fb0);

                        *pch -= (int)abLookup[((i>>1) & ~7) | (i & 7)];
                    }
                }
                else
                {
                    if (ch < 0xff21)
                    {
                        if (ch < 0x24b6)
                        {
                            *pch += 16;
                        }
                        else
                        {
                            *pch += 26;
                        }
                    }
                    else
                    {
                        *pch += 32;
                    }
                }
            }
        }
        else
        {
            // These are Unicode Number Forms.  They have lowercase counter-
            // parts, but are not considered uppercase.  Why, I don't know.

            if (InRange(ch, 0x2160, 0x216f))
            {
                *pch += 16;
            }
        }
    }

    return cchLength;
}

#endif // NEED_USER32_WRAPPER

//
// REARCHITECT - Do CharNextWrap and CharPrevWrap need to call the
//          CharNextW, CharPrevW on WinNT?  Couldn't these be MACROS?
//

LPWSTR WINAPI
CharNextWrapW(LPCWSTR lpszCurrent)
{
    VALIDATE_PROTOTYPE(CharNext);

    if (*lpszCurrent)
    {
        return (LPWSTR) lpszCurrent + 1;
    }
    else
    {
        return (LPWSTR) lpszCurrent;
    }
}

LPWSTR WINAPI
CharPrevWrapW(LPCWSTR lpszStart, LPCWSTR lpszCurrent)
{
    VALIDATE_PROTOTYPE(CharPrev);

    if (lpszCurrent == lpszStart)
    {
        return (LPWSTR) lpszStart;
    }
    else
    {
        return (LPWSTR) lpszCurrent - 1;
    }
}


#ifdef NEED_USER32_WRAPPER

BOOL WINAPI
CharToOemWrapW(LPCWSTR lpszSrc, LPSTR lpszDst)
{
    VALIDATE_PROTOTYPE(CharToOem);

    if (g_bRunningOnNT)
    {
        CharToOemW(lpszSrc, lpszDst);
    }

    CStrIn  str(lpszSrc);

    return CharToOemA(str, lpszDst);
}

#endif // NEED_USER32_WRAPPER

//----------------------------------------------------------------------
//
// function:    CharUpperWrapW( LPWSTR pch )
//
// purpose:     Converts character to uppercase.  Takes either a pointer
//              to a string, or a character masquerading as a pointer.
//              In the later case, the HIWORD must be zero.  This is
//              as spec'd for Win32.
//
// returns:     Uppercased character or string.  In the string case,
//              the uppercasing is done inplace.
//
//----------------------------------------------------------------------

#ifdef NEED_USER32_WRAPPER

LPWSTR WINAPI
CharUpperWrapW( LPWSTR pch )
{
    VALIDATE_PROTOTYPE(CharUpper);

    if (g_bRunningOnNT)
    {
        return CharUpperW( pch );
    }

    if (!HIWORD64(pch))
    {
        WCHAR ch = (WCHAR)(LONG_PTR)pch;

        CharUpperBuffWrapW( &ch, 1 );

        pch = (LPWSTR)MAKEINTATOM(ch);
    }
    else
    {
        CharUpperBuffWrapW( pch, lstrlenW(pch) );
    }

    return pch;
}

#endif // NEED_USER32_WRAPPER


//----------------------------------------------------------------------
//
// function:    CharUpperBuffWrapW( LPWSTR pch, DWORD cch )
//
// purpose:     Converts a string to uppercase.  String must be cch
//              characters in length.  Note that this function is
//              is messier that CharLowerBuffWrap, and the reason for
//              this is many Unicode characters are considered uppercase,
//              even when they don't have an uppercase counterpart.
//
// returns:     Character count (cch).  The uppercasing is done inplace.
//
//----------------------------------------------------------------------

#ifdef NEED_USER32_WRAPPER

DWORD WINAPI
CharUpperBuffWrapW( LPWSTR pch, DWORD cchLength )
{
    VALIDATE_PROTOTYPE(CharUpperBuff);

    if (g_bRunningOnNT)
    {
        return CharUpperBuffW( pch, cchLength );
    }

    DWORD cch;

    for ( cch = cchLength; cch-- ; pch++ )
    {
        WCHAR ch = *pch;

        if (IsCharLowerWrapW(ch))
        {
            if (ch < 0x00ff)
            {
                *pch -= ((ch != 0xdf) << 5);
            }
            else if (ch < 0x03b1)
            {
                if (ch < 0x01f5)
                {
                    if (ch < 0x01ce)
                    {
                        if (ch < 0x017f)
                        {
                            if (ch < 0x0101)
                            {
                                *pch += 121;
                            }
                            else
                            {
                                *pch -= (ch != 0x0131 &&
                                         ch != 0x0138 &&
                                         ch != 0x0149);
                            }
                        }
                        else if (ch < 0x01c9)
                        {
                            static const BYTE abMask[] =
                            {                       // 6543210f edcba987
                                0xfc, 0xbf,         // 11111100 10111111
                                0xbf, 0x67,         // 10111111 01100111
                                0xff, 0xef,         // 11111111 11101111
                                0xff, 0xf7,         // 11111111 11110111
                                0xbf, 0xfd          // 10111111 11111101
                            };

                            int i = ch - 0x017f;

                            *pch -= ((abMask[i>>3] >> (i&7)) & 1) +
                                    (ch == 0x01c6);
                        }
                        else
                        {
                            *pch -= ((ch != 0x01cb)<<1);
                        }
                    }
                    else
                    {
                        if (ch < 0x01df)
                        {
                            if (ch < 0x01dd)
                            {
                                *pch -= 1;
                            }
                            else
                            {
                                *pch -= 79;
                            }
                        }
                        else
                        {
                            *pch -= 1 + (ch == 0x01f3) -
                                    InRange(ch,0x01f0,0x01f2);
                        }
                    }
                }
                else if (ch < 0x0253)
                {
                    *pch -= (ch < 0x0250);
                }
                else if (ch < 0x03ac)
                {
                    static const BYTE abLookup[] =
                    {// 0/8  1/9  2/a  3/b  4/c  5/d  6/e  7/f
    /* 0x0253-0x0257 */                210, 206,   0, 205, 205,
    /* 0x0258-0x025f */   0, 202,   0, 203,   0,   0,   0,   0,
    /* 0x0260-0x0267 */ 205,   0,   0, 207,   0,   0,   0,   0,
    /* 0x0268-0x026f */ 209, 211,   0,   0,   0,   0,   0, 211,
    /* 0x0270-0x0277 */   0,   0, 213,   0,   0, 214,   0,   0,
    /* 0x0278-0x027f */   0,   0,   0,   0,   0,   0,   0,   0,
    /* 0x0280-0x0287 */   0,   0,   0, 218,   0,   0,   0,   0,
    /* 0x0288-0x028f */ 218,   0, 217, 217,   0,   0,   0,   0,
    /* 0x0290-0x0297 */   0,   0, 219
                    };

                    if (ch <= 0x0292)
                    {
                        *pch -= abLookup[ch - 0x0253];
                    }
                }
                else
                {
                    *pch -= (ch == 0x03b0) ? 0 : (37 + (ch == 0x03ac));
                }
            }
            else
            {
                if (ch < 0x0561)
                {
                    if (ch < 0x0451)
                    {
                        if (ch < 0x03e3)
                        {
                            if (ch < 0x03cc)
                            {
                                *pch -= 32 - (ch == 0x03c2);
                            }
                            else
                            {
                                int i = (ch < 0x03d0);
                                *pch -= (i<<6) - i + (ch == 0x03cc);
                            }
                        }
                        else if (ch < 0x0430)
                        {
                            *pch -= (ch < 0x03f0);
                        }
                        else
                        {
                            *pch -= 32;
                        }
                    }
                    else if (ch < 0x0461)
                    {
                        *pch -= 80;
                    }
                    else
                    {
                        *pch -= 1;
                    }
                }
                else
                {
                    if (ch < 0x1fb0)
                    {
                        if (ch < 0x1f70)
                        {
                            if (ch < 0x1e01)
                            {
                                int i = ch != 0x0587 && ch < 0x10d0;
                                *pch -= ((i<<5)+(i<<4)); /* 48 */
                            }
                            else if (ch < 0x1f00)
                            {
                                *pch -= !InRange(ch, 0x1e96, 0x1e9a);
                            }
                            else
                            {
                                int i = !InRange(ch, 0x1f50, 0x1f56)||(ch & 1);
                                *pch += (i<<3);
                            }
                        }
                        else
                        {
                            static const BYTE abLookup[] =
                                { 74, 86, 86, 100, 128, 112, 126 };

                            if ( ch <= 0x1f7d )
                            {
                                *pch += abLookup[(ch-0x1f70)>>1];
                            }
                        }
                    }
                    else
                    {
                        if (ch < 0x24d0)
                        {
                            if (ch < 0x1fe5)
                            {
                                *pch += (0x0023 & (1<<(ch&15))) ? 8 : 0;
                            }
                            else if (ch < 0x2170)
                            {
                                *pch += (0x0023 & (1<<(ch&15))) ? 7 : 0;
                            }
                            else
                            {
                                *pch -= ((ch > 0x24b5)<<4);
                            }
                        }
                        else if (ch < 0xff41)
                        {
                            int i = !InRange(ch, 0xfb00, 0xfb17);
                            *pch -= (i<<4)+(i<<3)+(i<<1); /* 26 */
                        }
                        else
                        {
                            *pch -= 32;
                        }
                    }
                }
            }
        }
        else
        {
            int i = InRange(ch, 0x2170, 0x217f);
            *pch -= (i<<4);
        }
    }

    return cchLength;
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

int FORWARD_API WINAPI
CopyAcceleratorTableWrapW(
        HACCEL  hAccelSrc,
        LPACCEL lpAccelDst,
        int     cAccelEntries)
{
    VALIDATE_PROTOTYPE(CopyAcceleratorTable);

    FORWARD_AW(CopyAcceleratorTable, (hAccelSrc, lpAccelDst, cAccelEntries));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

HACCEL FORWARD_API WINAPI
CreateAcceleratorTableWrapW(LPACCEL lpAccel, int cEntries)
{
    VALIDATE_PROTOTYPE(CreateAcceleratorTable);

    FORWARD_AW(CreateAcceleratorTable, (lpAccel, cEntries));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_GDI32_WRAPPER
typedef HDC (*FnCreateHDCA)(LPCSTR, LPCSTR, LPCSTR, CONST DEVMODEA *);

HDC WINAPI
CreateHDCWrapW(
        LPCWSTR             lpszDriver,
        LPCWSTR             lpszDevice,
        LPCWSTR             lpszOutput,
        CONST DEVMODEW *    lpInitData,
        FnCreateHDCA        pfn)
{
    DEVMODEA *  pdevmode = NULL;
    CStrIn      strDriver(lpszDriver);
    CStrIn      strDevice(lpszDevice);
    CStrIn      strOutput(lpszOutput);
    HDC         hdcReturn = 0;

    if (lpInitData)
    {
        pdevmode = (DEVMODEA *) LocalAlloc( LPTR, lpInitData->dmSize + lpInitData->dmDriverExtra );

        if (pdevmode)
        {
            // LPBYTE->LPSTR casts below
            SHUnicodeToAnsi(lpInitData->dmDeviceName, (LPSTR)pdevmode->dmDeviceName, ARRAYSIZE(pdevmode->dmDeviceName));
            memcpy(&pdevmode->dmSpecVersion,
                    &lpInitData->dmSpecVersion,
                    FIELD_OFFSET(DEVMODEW,dmFormName) - FIELD_OFFSET(DEVMODEW,dmSpecVersion));
            SHUnicodeToAnsi(lpInitData->dmFormName, (LPSTR)pdevmode->dmFormName, ARRAYSIZE(pdevmode->dmFormName));
            memcpy(&pdevmode->dmLogPixels,
                    &lpInitData->dmLogPixels,
                    lpInitData->dmDriverExtra + lpInitData->dmSize - FIELD_OFFSET(DEVMODEW, dmLogPixels));

            pdevmode->dmSize -= (sizeof(BCHAR) - sizeof(char)) * (CCHDEVICENAME + CCHFORMNAME);
        }
    }

    hdcReturn = (*pfn)(strDriver, strDevice, strOutput, pdevmode);

    if (pdevmode)
    {
        LocalFree(pdevmode);
    }

    return hdcReturn;
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

HDC WINAPI
CreateDCWrapW(
        LPCWSTR             lpszDriver,
        LPCWSTR             lpszDevice,
        LPCWSTR             lpszOutput,
        CONST DEVMODEW *    lpInitData)
{
    VALIDATE_PROTOTYPE(CreateDC);

    if (g_bRunningOnNT)
    {
        return CreateDCW(lpszDriver, lpszDevice, lpszOutput, lpInitData);
    }
    return CreateHDCWrapW(lpszDriver, lpszDevice, lpszOutput, lpInitData, CreateDCA);
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

HDC WINAPI
CreateICWrapW(
        LPCWSTR             lpszDriver,
        LPCWSTR             lpszDevice,
        LPCWSTR             lpszOutput,
        CONST DEVMODEW *    lpInitData)
{
    VALIDATE_PROTOTYPE(CreateIC);

    if (g_bRunningOnNT)
    {
        return CreateICW(lpszDriver, lpszDevice, lpszOutput, lpInitData);
    }

    return CreateHDCWrapW(lpszDriver, lpszDevice, lpszOutput, lpInitData, CreateICA);
}

#endif // NEED_GDI32_WRAPPER


#undef CreateDialogIndirectParamW

HWND WINAPI
CreateDialogIndirectParamWrapW(
        HINSTANCE       hInstance,
        LPCDLGTEMPLATEW hDialogTemplate,
        HWND            hWndParent,
        DLGPROC         lpDialogFunc,
        LPARAM          dwInitParam)
{
    VALIDATE_PROTOTYPE(CreateDialogIndirectParam);

    if (fDoMungeUI(hInstance))
    {
        return MLCreateDialogIndirectParamI(
                    hInstance,
                    hDialogTemplate,
                    hWndParent,
                    lpDialogFunc,
                    dwInitParam);
    }
    
    if (g_bRunningOnNT)
    {
        return CreateDialogIndirectParamW(
                    hInstance,
                    hDialogTemplate,
                    hWndParent,
                    lpDialogFunc,
                    dwInitParam);
    }

    return CreateDialogIndirectParamA(
                hInstance,
                hDialogTemplate,
                hWndParent,
                lpDialogFunc,
                dwInitParam);
}


#undef CreateDialogParamW

HWND WINAPI
CreateDialogParamWrapW(
        HINSTANCE   hInstance,
        LPCWSTR     lpTemplateName,
        HWND        hWndParent,
        DLGPROC     lpDialogFunc,
        LPARAM      dwInitParam)
{
    VALIDATE_PROTOTYPE(CreateDialogParam);
    ASSERT(HIWORD64(lpTemplateName) == 0);

    if (fDoMungeUI(hInstance))
        return MLCreateDialogParamI(hInstance, lpTemplateName, hWndParent, lpDialogFunc, dwInitParam);
        
    if (g_bRunningOnNT)
    {
        return CreateDialogParamW(hInstance, lpTemplateName, hWndParent, lpDialogFunc, dwInitParam);
    }

    return CreateDialogParamA(hInstance, (LPSTR) lpTemplateName, hWndParent, lpDialogFunc, dwInitParam);
}


#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI
CreateDirectoryWrapW(
        LPCWSTR                 lpPathName,
        LPSECURITY_ATTRIBUTES   lpSecurityAttributes)
{
    VALIDATE_PROTOTYPE(CreateDirectory);

    if (g_bRunningOnNT)
    {
        return CreateDirectoryW(lpPathName, lpSecurityAttributes);
    }

    CStrIn  str(lpPathName);

    ASSERT(!lpSecurityAttributes);
    return CreateDirectoryA(str, lpSecurityAttributes);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

HANDLE WINAPI
CreateEventWrapW(
        LPSECURITY_ATTRIBUTES   lpEventAttributes,
        BOOL                    bManualReset,
        BOOL                    bInitialState,
        LPCWSTR                 lpName)
{
    VALIDATE_PROTOTYPE(CreateEvent);

    //Totally bogus assert.
    //ASSERT(!lpName);

    // cast means we can't use FORWARD_AW
    if (g_bRunningOnNT)
    {
        return CreateEventW(lpEventAttributes, bManualReset, bInitialState, lpName);
    }

    return CreateEventA(lpEventAttributes, bManualReset, bInitialState, (LPCSTR)lpName);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

HANDLE WINAPI
CreateFileWrapW(
        LPCWSTR                 lpFileName,
        DWORD                   dwDesiredAccess,
        DWORD                   dwShareMode,
        LPSECURITY_ATTRIBUTES   lpSecurityAttributes,
        DWORD                   dwCreationDisposition,
        DWORD                   dwFlagsAndAttributes,
        HANDLE                  hTemplateFile)
{
    VALIDATE_PROTOTYPE(CreateFile);

    if (g_bRunningOnNT)
    {
        return CreateFileW(
                lpFileName,
                dwDesiredAccess,
                dwShareMode,
                lpSecurityAttributes,
                dwCreationDisposition,
                dwFlagsAndAttributes,
                hTemplateFile);
    }

    CStrIn  str(lpFileName);

    return CreateFileA(
            str,
            dwDesiredAccess,
            dwShareMode,
            lpSecurityAttributes,
            dwCreationDisposition,
            dwFlagsAndAttributes,
            hTemplateFile);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

HFONT WINAPI
CreateFontIndirectWrapW(CONST LOGFONTW * plfw)
{
    VALIDATE_PROTOTYPE(CreateFontIndirect);

    if (g_bRunningOnNT)
    {
        return CreateFontIndirectW(plfw);
    }

    LOGFONTA  lfa;
    HFONT     hFont;

    memcpy(&lfa, plfw, FIELD_OFFSET(LOGFONTA, lfFaceName));
    SHUnicodeToAnsi(plfw->lfFaceName, lfa.lfFaceName, ARRAYSIZE(lfa.lfFaceName));
    hFont = CreateFontIndirectA(&lfa);

    return hFont;
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_USER32_WRAPPER

HWND WINAPI
CreateWindowExWrapW(
        DWORD       dwExStyle,
        LPCWSTR     lpClassName,
        LPCWSTR     lpWindowName,
        DWORD       dwStyle,
        int         X,
        int         Y,
        int         nWidth,
        int         nHeight,
        HWND        hWndParent,
        HMENU       hMenu,
        HINSTANCE   hInstance,
        LPVOID      lpParam)
{
    VALIDATE_PROTOTYPE(CreateWindowEx);

    if (g_bRunningOnNT)
    {
        return CreateWindowExW(
                dwExStyle,
                lpClassName,
                lpWindowName,
                dwStyle,
                X,
                Y,
                nWidth,
                nHeight,
                hWndParent,
                hMenu,
                hInstance,
                lpParam);
    }

    CStrIn  strClass(lpClassName);
    CStrIn  strWindow(lpWindowName);

    return CreateWindowExA(
            dwExStyle,
            strClass,
            strWindow,
            dwStyle,
            X,
            Y,
            nWidth,
            nHeight,
            hWndParent,
            hMenu,
            hInstance,
            lpParam);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

LRESULT FORWARD_API WINAPI DefWindowProcWrapW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    VALIDATE_PROTOTYPE(DefWindowProc);

    FORWARD_AW(DefWindowProc, (hWnd, msg, wParam, lParam));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI DeleteFileWrapW(LPCWSTR pwsz)
{
    VALIDATE_PROTOTYPE(DeleteFile);

    if (g_bRunningOnNT)
    {
        return DeleteFileW(pwsz);
    }

    CStrIn  str(pwsz);

    return DeleteFileA(str);
}

#endif // NEED_KERNEL32_WRAPPER

#undef DialogBoxIndirectParamW

INT_PTR WINAPI
DialogBoxIndirectParamWrapW(
        HINSTANCE       hInstance,
        LPCDLGTEMPLATEW hDialogTemplate,
        HWND            hWndParent,
        DLGPROC         lpDialogFunc,
        LPARAM          dwInitParam)
{
    VALIDATE_PROTOTYPE(DialogBoxIndirectParam);

    if (fDoMungeUI(hInstance))
        return MLDialogBoxIndirectParamI(hInstance, hDialogTemplate, hWndParent, lpDialogFunc, dwInitParam);

    if (g_bRunningOnNT)
    {
        return DialogBoxIndirectParamW(
                    hInstance,
                    hDialogTemplate,
                    hWndParent,
                    lpDialogFunc,
                    dwInitParam);
    }

    return DialogBoxIndirectParamA(
                hInstance,
                hDialogTemplate,
                hWndParent,
                lpDialogFunc,
                dwInitParam);
}


#undef DialogBoxParamW

INT_PTR WINAPI
DialogBoxParamWrapW(
        HINSTANCE   hInstance,
        LPCWSTR     lpszTemplate,
        HWND        hWndParent,
        DLGPROC     lpDialogFunc,
        LPARAM      dwInitParam)
{
    VALIDATE_PROTOTYPE(DialogBoxParam);
    ASSERT(HIWORD64(lpszTemplate) == 0);

    if (fDoMungeUI(hInstance))
        return MLDialogBoxParamI(hInstance, lpszTemplate, hWndParent, lpDialogFunc, dwInitParam);

    if (g_bRunningOnNT)
    {
        return DialogBoxParamW(
                    hInstance,
                    lpszTemplate,
                    hWndParent,
                    lpDialogFunc,
                    dwInitParam);
    }

    return DialogBoxParamA(hInstance, (LPCSTR) lpszTemplate, hWndParent, lpDialogFunc, dwInitParam);
}

#ifdef NEED_USER32_WRAPPER

LRESULT FORWARD_API WINAPI
DispatchMessageWrapW(CONST MSG * lpMsg)
{
    VALIDATE_PROTOTYPE(DispatchMessage);

    FORWARD_AW(DispatchMessage, (lpMsg));
}

#endif // NEED_USER32_WRAPPER

BOOL _MayNeedFontLinking(LPCWSTR lpStr, UINT cch)
{
#ifndef UNIX
    //
    // Scan the string to see if we might need to use font linking to draw it.
    // If you've got a better way of doing this, I'd love to hear it.
    //
    BOOL fRet = FALSE;

    int cChars = ((cch == -1) ? lstrlenW(lpStr) : cch);

    for (int i=0; i < cChars; i++)
    {
        if (lpStr[i] > 127)
        {
            fRet = TRUE;
            break;
        }
    }
    return fRet;
#else
    return FALSE;
#endif
}

#ifdef NEED_USER32_WRAPPER

int WINAPI
DrawTextWrapW(
        HDC     hDC,
        LPCWSTR lpString,
        int     nCount,
        LPRECT  lpRect,
        UINT    uFormat)
{
    VALIDATE_PROTOTYPE(DrawText);

    if (_MayNeedFontLinking(lpString, nCount))
    {
        return DrawTextFLW(hDC, lpString, nCount, lpRect, uFormat);
    }
    else if (g_bRunningOnNT)
    {
        return DrawTextW(hDC, lpString, nCount, lpRect, uFormat);
    }
    CStrIn  str(lpString, nCount);
    return DrawTextA(hDC, str, str.strlen(), lpRect, uFormat);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

struct EFFSTAT
{
    LPARAM          lParam;
    FONTENUMPROC    lpEnumFontProc;
    BOOL            fFamilySpecified;
};

int CALLBACK
EnumFontFamiliesCallbackWrap(
        ENUMLOGFONTA *  lpelf,
        NEWTEXTMETRIC * lpntm,
        DWORD           FontType,
        LPARAM          lParam)
{
    ENUMLOGFONTW    elf;

    //  Convert strings from ANSI to Unicode
    if (((EFFSTAT *)lParam)->fFamilySpecified && (FontType & TRUETYPE_FONTTYPE) )
    {
        // LPBYTE->LPCSTR cast below
        SHAnsiToUnicode((LPCSTR)lpelf->elfFullName, elf.elfFullName, ARRAYSIZE(elf.elfFullName));
        SHAnsiToUnicode((LPCSTR)lpelf->elfStyle, elf.elfStyle, ARRAYSIZE(elf.elfStyle));
    }
    else
    {
        elf.elfStyle[0] = L'\0';
        elf.elfFullName[0] = L'\0';
    }

    SHAnsiToUnicode(lpelf->elfLogFont.lfFaceName, elf.elfLogFont.lfFaceName, ARRAYSIZE(elf.elfLogFont.lfFaceName));

    //  Copy the non-string data
    memcpy(
            &elf.elfLogFont,
            &lpelf->elfLogFont,
            FIELD_OFFSET(LOGFONTA, lfFaceName));

    //  Chain to the original callback function
    return (*((EFFSTAT *) lParam)->lpEnumFontProc)(
            (const LOGFONTW *)&elf,
            (const TEXTMETRICW *) lpntm,
            FontType,
            ((EFFSTAT *) lParam)->lParam);
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

int WINAPI
EnumFontFamiliesWrapW(
        HDC          hdc,
        LPCWSTR      lpszFamily,
        FONTENUMPROC lpEnumFontProc,
        LPARAM       lParam)
{
    VALIDATE_PROTOTYPE(EnumFontFamilies);

    if (g_bRunningOnNT)
    {
        return EnumFontFamiliesW(
                hdc,
                lpszFamily,
                lpEnumFontProc,
                lParam);
    }

    CStrIn  str(lpszFamily);
    EFFSTAT effstat;

    effstat.lParam = lParam;
    effstat.lpEnumFontProc = lpEnumFontProc;
    effstat.fFamilySpecified = lpszFamily != NULL;

    return EnumFontFamiliesA(
            hdc,
            str,
            (FONTENUMPROCA) EnumFontFamiliesCallbackWrap,
            (LPARAM) &effstat);
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

int WINAPI
EnumFontFamiliesExWrapW(
        HDC          hdc,
        LPLOGFONTW   lplfw,
        FONTENUMPROC lpEnumFontProc,
        LPARAM       lParam,
        DWORD        dwFlags )
{
    VALIDATE_PROTOTYPE(EnumFontFamiliesEx);

    if (g_bRunningOnNT)
    {
        return EnumFontFamiliesExW(
                hdc,
                lplfw,
                lpEnumFontProc,
                lParam,
                dwFlags);
    }

    LOGFONTA lfa;
    CStrIn   str(lplfw->lfFaceName);
    EFFSTAT  effstat;

    ASSERT( FIELD_OFFSET(LOGFONTW, lfFaceName) == FIELD_OFFSET(LOGFONTA, lfFaceName) );

    memcpy( &lfa, lplfw, sizeof(LOGFONTA) - FIELD_OFFSET(LOGFONTA, lfFaceName) );
    memcpy( lfa.lfFaceName, str, LF_FACESIZE );
    lfa.lfFaceName[LF_FACESIZE - 1] = 0x00;

    effstat.lParam = lParam;
    effstat.lpEnumFontProc = lpEnumFontProc;
    effstat.fFamilySpecified = lplfw->lfFaceName != NULL;

    return EnumFontFamiliesExA(
            hdc,
            &lfa,
            (FONTENUMPROCA) EnumFontFamiliesCallbackWrap,
            (LPARAM) &effstat,
            dwFlags );
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI
EnumResourceNamesWrapW(
        HINSTANCE        hModule,
        LPCWSTR          lpType,
        ENUMRESNAMEPROCW lpEnumFunc,
        LPARAM           lParam)
{
    VALIDATE_PROTOTYPE(EnumResourceNames);
    ASSERT(HIWORD64(lpType) == 0);

    if (g_bRunningOnNT)
    {
        return EnumResourceNamesW(hModule, lpType, lpEnumFunc, lParam);
    }

    return EnumResourceNamesA(hModule, (LPCSTR) lpType, (ENUMRESNAMEPROCA)lpEnumFunc, lParam);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

HANDLE WINAPI
FindFirstFileWrapW(
        LPCWSTR             lpFileName,
        LPWIN32_FIND_DATAW  pwszFd)
{
    VALIDATE_PROTOTYPE(FindFirstFile);

    if (g_bRunningOnNT)
    {
        return FindFirstFileW(lpFileName, pwszFd);
    }

    CStrIn              str(lpFileName);
    CWin32FindDataInOut fd(pwszFd);

    return FindFirstFileA(str, fd);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

HRSRC WINAPI
FindResourceWrapW(HINSTANCE hModule, LPCWSTR lpName, LPCWSTR lpType)
{
    VALIDATE_PROTOTYPE(FindResource);

    if (g_bRunningOnNT)
    {
        return FindResourceW(hModule, lpName, lpType);
    }

    // NOTE: The FindResourceW exists on Win9x, but Win95 and Win98
    // have a bug where they make two bogus call to LocalFree to try
    // to free up the passed-in buffers that they did not allocate.
    //
    // We work around by always wrapping.  Another solution that Godot uses:
    //
    // For win95/98 only:
    // 1) Copy the two params to stack-alloc'ed variables
    // 2) Call the "W" function with the variables from step #1
    // 3) Clear out last error when it is ERROR_INVALID_HANDLE (the error that the 
    //    bogus LocalFree call in the OS will cause
    // This gives proper Unicode behavior to both functions on all Win9x platforms.

    CStrIn  strName(lpName);
    CStrIn strType(lpType);

    return FindResourceA(hModule, strName, strType);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

HWND WINAPI
FindWindowWrapW(LPCWSTR lpClassName, LPCWSTR lpWindowName)
{
    VALIDATE_PROTOTYPE(FindWindow);

    if (g_bRunningOnNT)
    {
        return FindWindowW(lpClassName, lpWindowName);
    }

    // Let FindWindowExWrapW do the thunking
    return FindWindowExWrapW(NULL, NULL, lpClassName, lpWindowName);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

HWND WINAPI
FindWindowExWrapW(HWND hwndParent, HWND hwndChildAfter, LPCWSTR pwzClassName, LPCWSTR pwzWindowName)
{
    VALIDATE_PROTOTYPE(FindWindowEx);

    if (g_bRunningOnNT)
        return FindWindowExW(hwndParent, hwndChildAfter, pwzClassName, pwzWindowName);


    CStrIn  strClass(pwzClassName);
    CStrIn  strWindow(pwzWindowName);

    return FindWindowExA(hwndParent, hwndChildAfter, strClass, strWindow);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

//
// FindNextArgInfo - Finds the argument format and argument number from a FormatMessage like template
//
// Returns - True if arg is wide string, False if other
//
BOOL FindNextArgInfo(LPCWSTR *pszTemplate, UINT *puiNum, LPWSTR pszFormat, UINT cchFormat)
{
    BOOL fRet = FALSE;
    
    if(*pszTemplate == NULL)
        return(FALSE);

    LPCWSTR psz = *pszTemplate;
    LPCWSTR pszT1 = NULL;
    LPCWSTR pszT2 = NULL;
    LPWSTR pszFmt = pszFormat;
    BOOL fHaveStart = FALSE;
    UINT cchFmt;

    *puiNum = 0;

    if(cchFormat > 0)
    {
        *pszFmt = TEXT('%');
        pszFmt++;
        cchFormat--;
    }

    if(*psz >= TEXT('1') && *psz <= TEXT('9'))
    {
        for(; *psz; psz++)
        {
            if(*psz == TEXT('!'))
            {
                if(fHaveStart)  // Done
                {
                    pszT2 = ++psz;  // Mark end of type
                    break;
                }
                else
                {
                    fHaveStart = TRUE;
                    psz++;
                    if(pszT1 == NULL)
                        pszT1 = psz;  // Mark start of arg format string
                }
            }

            if(!fHaveStart) // Get arg number
            {
                if(*psz >= TEXT('0') && *psz <= TEXT('9'))
                    *puiNum = (*puiNum * 10) + (*psz - TEXT('0'));
                else
                    break;
            }
        }

        if(*puiNum == 0)
            return(FALSE);
    }
    else    // Special format specifier
    {
        pszT1 = psz;
        pszT2 = psz+1;
    }

    if(pszT1 != NULL)    // We have an arg format string
    {
        cchFmt = cchFormat;
        if(cchFmt > (UINT)(pszT2 - pszT1))
            cchFmt = (pszT2 - pszT1); 
        StrCpyN(pszFmt, pszT1, cchFmt);

        // Is argument type a string
        if(StrChrI(pszFormat, TEXT('s')))
        {
            fRet = TRUE;
        }
        else
        {
            cchFmt = cchFormat;
            if(cchFmt > (UINT)(pszT2 - *pszTemplate)+1)
                cchFmt = (pszT2 - *pszTemplate)+1; 
            StrCpyN(pszFmt, *pszTemplate, cchFmt);
        }
    }
    else    // No arg format string, FormatMessage default is !s!
    {
        pszT2 = psz;
        fRet = TRUE;
        cchFmt = cchFormat;
        if(cchFmt > 3)
            cchFmt = 3;
        StrCpyN(pszFmt, TEXT("ws"), cchFmt);
    }

    *pszTemplate = pszT2;   // Move template string beyond this format specifier

    return(fRet);
}

//
// The following is used to work around Win9x FormatMessage problems related to converting 
// DBCS strings to Unicode.
//                     
#define FML_BUFFER_SIZE 1024
#define FML_BUFFER_INC   256

DWORD
FormatMessageLiteW(
    DWORD       dwFlags,
    LPCWSTR     lpSource,
    PVOID *     pDest,
    DWORD       nSize,
    va_list *   Arguments)
{
    BOOL fIsStr;
    UINT uiNum;
    UINT uiDataCnt = 0;
    va_list pArgList = *Arguments;
    va_list pArgList2;
    WCHAR *pszBuf2;
    WCHAR szFmt[256];
    VOID *pData[10];
    LPCWSTR psz = lpSource;
    LPCWSTR psz1 = NULL;
    LPWSTR lpBuffer;
    INT cchBufUsed = 0;
    INT cchBuf2;
    INT cch;

    if(lpSource == NULL || pDest == NULL || Arguments == NULL)
        return(0);

    if(dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
    {
        nSize = max(nSize, FML_BUFFER_SIZE);
        if((lpBuffer = (WCHAR *)LocalAlloc(LPTR, nSize * sizeof(WCHAR))) == NULL)
            return(0);
    }
    else
    {
        if(nSize == 0)
            return(0);
        lpBuffer = (LPWSTR)pDest;
    }

    cchBuf2 = nSize + lstrlen(lpSource);
    if((pszBuf2 = (WCHAR *)LocalAlloc(LPTR, cchBuf2 * sizeof(WCHAR))) != NULL)
    {
        *lpBuffer = TEXT('\0');
        while(*psz)
        {
            if(*psz == TEXT('%'))
            {
                psz++;
                if(psz1 != NULL)    // Copy any previous text to the buffer
                {
                    if((cch = psz - psz1) > (INT)(nSize - cchBufUsed))
                    {
                        if(dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
                        {
                            LPWSTR lpRealloc;
                            nSize = max(FML_BUFFER_INC, cch);
                            if((lpRealloc = (WCHAR *)LocalReAlloc(lpBuffer, nSize * sizeof(WCHAR), LMEM_ZEROINIT | LMEM_MOVEABLE)) == NULL)
                            {
                                LocalFree(lpBuffer);
                                LocalFree(pszBuf2);
                                return(0);
                            }
                            lpBuffer = lpRealloc;
                        }
                        else
                        {
                            RIPMSG(FALSE, "Output buffer not large enough.  Truncating.");
                            cch = nSize - cchBufUsed;
                        }
                    }
                    StrCpyNW(lpBuffer+cchBufUsed, psz1, cch);
                    cchBufUsed += cch-1;
                    psz1 = NULL;
                }

                fIsStr = FindNextArgInfo(&psz, &uiNum, szFmt, ARRAYSIZE(szFmt));

                if(fIsStr)
                {
                    if(uiNum > uiDataCnt)
                    {
                        for(UINT i = uiDataCnt; i < uiNum; i++)    // Find the iTH argument
                        {
                            pData[i] = va_arg(pArgList, VOID *);
                            uiDataCnt++;
                        }
                    }
                        
                    cch = wnsprintfW(pszBuf2, cchBuf2, szFmt, pData[uiNum-1]);
                    if(cch == cchBuf2)
                    {
                        RIPMSG(FALSE, "Param buffer may be too small. Output string may be truncated.");
                    }
                }
                else
                {
                    // Call FormatMessage on non-string arguments
                    pArgList2 = *Arguments;
                    CStrIn strSource(CP_ACP, (LPCWSTR)szFmt, -1);
                    CStrOut str(pszBuf2, cchBuf2);
                    FormatMessageA(FORMAT_MESSAGE_FROM_STRING, strSource, 0, 0, str, str.BufSize(), &pArgList2);
                    cch = str.ConvertExcludingNul();
                }

                if(cch > 0)
                {
                    if(cch > (INT)(nSize - cchBufUsed))
                    {
                        if(dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
                        {
                            LPWSTR lpRealloc;
                            nSize += max(FML_BUFFER_INC, cch);
                            if((lpRealloc = (WCHAR *)LocalReAlloc(lpBuffer, nSize * sizeof(WCHAR), LMEM_ZEROINIT | LMEM_MOVEABLE)) == NULL)
                            {
                                LocalFree(lpBuffer);
                                LocalFree(pszBuf2);
                                return(0);
                            }
                            lpBuffer = lpRealloc;
                        }
                        else
                        {
                            RIPMSG(FALSE, "Output buffer not large enough.  Truncating.");
                            cch = nSize - cchBufUsed;
                        }
                    }
                    StrCpyW(lpBuffer+cchBufUsed, pszBuf2);
                    cchBufUsed += cch;
                }
                else
                {
                    RIPMSG(FALSE, "Argument string conversion failed.  Argument not copied to output buffer");
                }
                continue;
            }

            if(psz1 == NULL)
                psz1 = psz;    // Start of text block

            psz++;
        }

        if(psz1)    // Copy any remaining text to the output buffer
        {
            if((cch = (psz - psz1)+1) > (INT)(nSize - cchBufUsed))
            {
                if(dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
                {
                    LPWSTR lpRealloc;
                    nSize = max(FML_BUFFER_INC, cch);
                    if((lpRealloc = (WCHAR *)LocalReAlloc(lpBuffer, nSize * sizeof(WCHAR), LMEM_ZEROINIT | LMEM_MOVEABLE)) == NULL)
                    {
                        LocalFree(lpBuffer);
                        LocalFree(pszBuf2);
                        return(0);
                    }
                    lpBuffer = lpRealloc;
                }
                else
                {
                    RIPMSG(FALSE, "Output buffer not large enough.  Truncating.");
                    cch = nSize - cchBufUsed;
                }
            }
            StrCpyNW(lpBuffer+cchBufUsed, psz1, cch);
            cchBufUsed += cch - 1;  // substract the null from the count
        }

        LocalFree(pszBuf2);
        pszBuf2 = NULL;
    }

    if(dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER)
    {
        *pDest = (PVOID *)lpBuffer;
    }
    
    return((DWORD)cchBufUsed);
}

DWORD WINAPI
FormatMessageWrapW(
    DWORD       dwFlags,
    LPCVOID     lpSource,
    DWORD       dwMessageId,
    DWORD       dwLanguageId,
    LPWSTR      lpBuffer,
    DWORD       nSize,
    va_list *   Arguments)
{
    VALIDATE_PROTOTYPE(FormatMessage);
    if (!(dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER))
    {
        VALIDATE_OUTBUF(lpBuffer, nSize);
    }

    DWORD dwResult;

    if (g_bRunningOnNT)
    {
        dwResult = FormatMessageW(
                       dwFlags,
                       lpSource,
                       dwMessageId,
                       dwLanguageId,
                       lpBuffer,
                       nSize,
                       Arguments);
    }
    else
    {
        #ifdef DEBUG
        // If a source string is passed, make sure that all string insertions
        // have explicit character set markers.  Otherwise, you get random
        // behavior depending on whether we need to thunk to ANSI or not.
        // (We are not clever enough to thunk the inserts; that's the caller's
        // responsibility.)
        //
        if (dwFlags & FORMAT_MESSAGE_FROM_STRING)
        {
            LPCWSTR pwsz;
            for (pwsz = (LPCWSTR)lpSource; *pwsz; pwsz++)
            {
                if (*pwsz == L'%')
                {
                    pwsz++;
                    // Found an insertion.  Get the digit or two.
                    if (*pwsz == L'0')
                        continue;       // "%0" is special
                    if (*pwsz < L'0' || *pwsz > L'9')
                        continue;        // skip % followed by nondigit
                    pwsz++;            // Skip the digit
                    if (*pwsz >= L'0' && *pwsz <= L'9')
                        pwsz++;        // Skip the optional second digit
                    // The next character MUST be an exclamation point!
                    ASSERT(*pwsz == L'!' &&
                           "FormatMessageWrapW: All string insertions must have explicit character sets.");
                    // I'm not going to validate that the insertion contains
                    // an explicit character set override because if you went
                    // so far as to do a %n!...!, you'll get the last bit right too.
                }
            }
        }
        #endif

        //
        //  FORMAT_MESSAGE_FROM_STRING means that the source is a string.
        //  Otherwise, it's an opaque LPVOID (aka, an atom).
        //
        if(((dwFlags == FORMAT_MESSAGE_FROM_STRING) || 
            (dwFlags == (FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ALLOCATE_BUFFER))) &&
           (dwMessageId == 0) && (dwLanguageId == 0))
        {
            TraceMsg(TF_WARNING, "This is a lite version of FormatMessage.  It may not act as you expect.");
            dwResult = FormatMessageLiteW(dwFlags, (LPCWSTR)lpSource, (PVOID *)lpBuffer, nSize, Arguments);
        }
        else
        {
            CStrIn strSource((dwFlags & FORMAT_MESSAGE_FROM_STRING) ? CP_ACP : CP_ATOM,
                             (LPCWSTR)lpSource, -1);

            if (!(dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER))
            {
                CStrOut str(lpBuffer, nSize);
                FormatMessageA(
                        dwFlags,
                        strSource,
                        dwMessageId,
                        dwLanguageId,
                        str,
                        str.BufSize(),
                        Arguments);         // We don't handle Arguments != NULL

                dwResult = str.ConvertExcludingNul();
            }
            else
            {
                LPSTR pszBuffer = NULL;
                LPWSTR * ppwzOut = (LPWSTR *)lpBuffer;

                *ppwzOut = NULL;
                FormatMessageA(
                        dwFlags,
                        strSource,
                        dwMessageId,
                        dwLanguageId,
                        (LPSTR)&pszBuffer,
                        0,
                        Arguments);

                if (pszBuffer)
                {
                    DWORD cchSize = (lstrlenA(pszBuffer) + 1);
                    LPWSTR pszThunkedBuffer;

                    if (cchSize < nSize)
                        cchSize = nSize;

                    pszThunkedBuffer = (LPWSTR) LocalAlloc(LPTR, cchSize * sizeof(WCHAR));
                    if (pszThunkedBuffer)
                    {
                        *ppwzOut = pszThunkedBuffer;
                        SHAnsiToUnicode(pszBuffer, pszThunkedBuffer, cchSize);
                    }

                    LocalFree(pszBuffer);
                }
                dwResult = (*ppwzOut ? lstrlenW(*ppwzOut) : 0);
            }
        }
    }

    return dwResult;
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL WINAPI
GetClassInfoWrapW(HINSTANCE hModule, LPCWSTR lpClassName, LPWNDCLASSW lpWndClassW)
{
    VALIDATE_PROTOTYPE(GetClassInfo);

    if (g_bRunningOnNT)
    {
        return GetClassInfoW(hModule, lpClassName, lpWndClassW);
    }

    BOOL    ret;

    CStrIn  strClassName(lpClassName);

    ASSERT(sizeof(WNDCLASSA) == sizeof(WNDCLASSW));

    ret = GetClassInfoA(hModule, strClassName, (LPWNDCLASSA) lpWndClassW);

    lpWndClassW->lpszMenuName = NULL;
    lpWndClassW->lpszClassName = NULL;
    return ret;
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

DWORD FORWARD_API WINAPI
GetClassLongWrapW(HWND hWnd, int nIndex)
{
    VALIDATE_PROTOTYPE(GetClassLong);

    FORWARD_AW(GetClassLong, (hWnd, nIndex));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

int WINAPI
GetClassNameWrapW(HWND hWnd, LPWSTR lpClassName, int nMaxCount)
{
    VALIDATE_PROTOTYPE(GetClassName);
    VALIDATE_OUTBUF(lpClassName, nMaxCount);

    if (g_bRunningOnNT)
    {
        return GetClassNameW(hWnd, lpClassName, nMaxCount);
    }

    CStrOut strClassName(lpClassName, nMaxCount);

    GetClassNameA(hWnd, strClassName, strClassName.BufSize());
    return strClassName.ConvertIncludingNul();
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

int WINAPI
GetClipboardFormatNameWrapW(UINT format, LPWSTR lpFormatName, int cchFormatName)
{
    VALIDATE_PROTOTYPE(GetClipboardFormatName);
    VALIDATE_OUTBUF(lpFormatName, cchFormatName);

    if (g_bRunningOnNT)
    {
        return GetClipboardFormatNameW(format, lpFormatName, cchFormatName);
    }

    CStrOut strFormatName(lpFormatName, cchFormatName);

    GetClipboardFormatNameA(format, strFormatName, strFormatName.BufSize());
    return strFormatName.ConvertIncludingNul();
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

DWORD WINAPI
GetCurrentDirectoryWrapW(DWORD nBufferLength, LPWSTR lpBuffer)
{
    VALIDATE_PROTOTYPE(GetCurrentDirectory);
    VALIDATE_OUTBUF(lpBuffer, nBufferLength);

    if (g_bRunningOnNT)
    {
        return GetCurrentDirectoryW(nBufferLength, lpBuffer);
    }

    CStrOut str(lpBuffer, nBufferLength);

    GetCurrentDirectoryA(str.BufSize(), str);
    return str.ConvertExcludingNul();
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

UINT WINAPI
GetDlgItemTextWrapW(
        HWND    hWndDlg,
        int     idControl,
        LPWSTR  lpsz,
        int     cchMax)
{
    VALIDATE_PROTOTYPE(GetDlgItemText);
    VALIDATE_OUTBUF(lpsz, cchMax);

    HWND hWnd = GetDlgItem(hWndDlg, idControl);

    if (hWnd)
        return GetWindowTextWrapW(hWnd, lpsz, cchMax);

    /*
     * If we couldn't find the window, just null terminate lpch so that the
     * app doesn't gp fault if it tries to run through the text.
      */
    if (cchMax)
        *lpsz = (WCHAR)0;

    return 0;
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

DWORD WINAPI
GetFileAttributesWrapW(LPCWSTR lpFileName)
{
    VALIDATE_PROTOTYPE(GetFileAttributes);

    if (g_bRunningOnNT)
    {
        return GetFileAttributesW(lpFileName);
    }

    CStrIn  str(lpFileName);

    return GetFileAttributesA(str);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

int WINAPI
GetLocaleInfoWrapW(LCID Locale, LCTYPE LCType, LPWSTR lpsz, int cchData)
{
    VALIDATE_PROTOTYPE(GetLocaleInfo);
    VALIDATE_OUTBUF(lpsz, cchData);

    if (g_bRunningOnNT)
    {
        return GetLocaleInfoW(Locale, LCType, lpsz, cchData);
    }

    CStrOut str(lpsz, cchData);

    GetLocaleInfoA(Locale, LCType, str, str.BufSize());
    return str.ConvertIncludingNul();
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL FORWARD_API WINAPI
GetMessageWrapW(
        LPMSG   lpMsg,
        HWND    hWnd,
        UINT    wMsgFilterMin,
        UINT    wMsgFilterMax)
{
    VALIDATE_PROTOTYPE(GetMessage);

    FORWARD_AW(GetMessage, (lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

DWORD WINAPI
GetModuleFileNameWrapW(HINSTANCE hModule, LPWSTR pwszFilename, DWORD nSize)
{
    VALIDATE_PROTOTYPE(GetModuleFileName);
    VALIDATE_OUTBUF(pwszFilename, nSize);

    if (g_bRunningOnNT)
    {
        return GetModuleFileNameW(hModule, pwszFilename, nSize);
    }

    CStrOut str(pwszFilename, nSize);

    GetModuleFileNameA(hModule, str, str.BufSize());
    return str.ConvertIncludingNul();
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

UINT WINAPI
GetSystemDirectoryWrapW(LPWSTR lpBuffer, UINT uSize)
{
    VALIDATE_PROTOTYPE(GetSystemDirectory);
    VALIDATE_OUTBUF(lpBuffer, uSize);

    if (g_bRunningOnNT)
    {
        return GetSystemDirectoryW(lpBuffer, uSize);
    }

    CStrOut str(lpBuffer, uSize);

    GetSystemDirectoryA(str, str.BufSize());
    return str.ConvertExcludingNul();
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

DWORD WINAPI
SearchPathWrapW(
        LPCWSTR lpPathName,
        LPCWSTR lpFileName,
        LPCWSTR lpExtension,
        DWORD   cchReturnBuffer,
        LPWSTR  lpReturnBuffer,
        LPWSTR *  plpfilePart)
{
    VALIDATE_PROTOTYPE(SearchPath);
    VALIDATE_OUTBUF(lpReturnBuffer, cchReturnBuffer);

    if (g_bRunningOnNT)
    {
        return SearchPathW(
            lpPathName,
            lpFileName,
            lpExtension,
            cchReturnBuffer,
            lpReturnBuffer,
            plpfilePart);
    }

    CStrIn  strPath(lpPathName);
    CStrIn  strFile(lpFileName);
    CStrIn  strExtension(lpExtension);
    CStrOut strReturnBuffer(lpReturnBuffer, cchReturnBuffer);

    DWORD dwLen = SearchPathA(
            strPath,
            strFile,
            strExtension,
            strReturnBuffer.BufSize(),
            strReturnBuffer,
            (LPSTR *)plpfilePart);

    //
    // Getting the correct value for plpfilePart requires
    // a strrchr on the converted string.  If this value
    // is needed, just add the code to do it here.
    //

    *plpfilePart = NULL;

    if (cchReturnBuffer == 0)
        dwLen = 2*dwLen;
    else
        dwLen = strReturnBuffer.ConvertExcludingNul();

    return dwLen;
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

HMODULE WINAPI
GetModuleHandleWrapW(LPCWSTR lpModuleName)
{
    VALIDATE_PROTOTYPE(GetModuleHandle);

    if (g_bRunningOnNT)
    {
        return GetModuleHandleW(lpModuleName);
    }

    CStrIn  str(lpModuleName);
    return GetModuleHandleA(str);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

int WINAPI
GetObjectWrapW(HGDIOBJ hgdiObj, int cbBuffer, LPVOID lpvObj)
{
    VALIDATE_PROTOTYPE(GetObject);

    if (g_bRunningOnNT)
    {
        return GetObjectW(hgdiObj, cbBuffer, lpvObj);
    }

    int nRet;

    if(cbBuffer != sizeof(LOGFONTW))
    {
        nRet = GetObjectA(hgdiObj, cbBuffer, lpvObj);
    }
    else
    {
        LOGFONTA lfa;

        nRet = GetObjectA(hgdiObj, sizeof(lfa), &lfa);
        if (nRet > 0)
        {
            memcpy(lpvObj, &lfa, FIELD_OFFSET(LOGFONTW, lfFaceName));
            SHAnsiToUnicode(lfa.lfFaceName, ((LOGFONTW*)lpvObj)->lfFaceName, ARRAYSIZE(((LOGFONTW*)lpvObj)->lfFaceName));
            nRet = sizeof(LOGFONTW);
        }
    }

    return nRet;
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_SHELL32_WRAPPER

LPITEMIDLIST WINAPI SHBrowseForFolderWrapW(LPBROWSEINFOW pbiW)
{
    if (UseUnicodeShell32())
    {
        return SHBrowseForFolderW(pbiW);
    }

    LPITEMIDLIST pidl = NULL;
    ASSERT(pbiW);
    if (pbiW)
    {
        CStrIn strTitle(pbiW->lpszTitle);
        CStrOut strDisplayName(pbiW->pszDisplayName, MAX_PATH);
        BROWSEINFOA biA;

        biA = * (LPBROWSEINFOA) pbiW;
        biA.lpszTitle = strTitle;
        biA.pszDisplayName = strDisplayName;

        pidl = SHBrowseForFolderA(&biA);
        if (pidl)
        {
            strDisplayName.ConvertIncludingNul();
        }
    }

    return pidl;
}

#endif // NEED_SHELL32_WRAPPER

#ifdef NEED_SHELL32_WRAPPER

BOOL WINAPI SHGetPathFromIDListWrapW(LPCITEMIDLIST pidl, LPWSTR pwzPath)
{
    if (UseUnicodeShell32())
    {
        return SHGetPathFromIDListW(pidl, pwzPath);
    }

    CStrOut strPathOut(pwzPath, MAX_PATH);
    BOOL fResult = SHGetPathFromIDListA(pidl, strPathOut);
    if (fResult)
    {
        strPathOut.ConvertIncludingNul();
    }

    return fResult;
}

#endif // NEED_SHELL32_WRAPPER

#ifdef NEED_SHELL32_WRAPPER
BOOL WINAPI ShellExecuteExWrapW(LPSHELLEXECUTEINFOW pExecInfoW)
{
    if (g_bRunningOnNT)
    {
        return ShellExecuteExW(pExecInfoW);
    }

    BOOL fResult = FALSE;
    ASSERT(pExecInfoW);
    if (pExecInfoW)
    {
        SHELLEXECUTEINFOA ExecInfoA;

        CStrIn strVerb(pExecInfoW->lpVerb);
        CStrIn strParameters(pExecInfoW->lpParameters);
        CStrIn strDirectory(pExecInfoW->lpDirectory);
        CStrIn strClass(pExecInfoW->lpClass);
        CHAR szFile[MAX_PATH + INTERNET_MAX_URL_LENGTH + 2];


        ExecInfoA = *(LPSHELLEXECUTEINFOA) pExecInfoW;
        ExecInfoA.lpVerb = strVerb;
        ExecInfoA.lpParameters = strParameters;
        ExecInfoA.lpDirectory = strDirectory;
        ExecInfoA.lpClass = strClass;

        if (pExecInfoW->lpFile)
        {
            ExecInfoA.lpFile = szFile;
            SHUnicodeToAnsi(pExecInfoW->lpFile, szFile, ARRAYSIZE(szFile));

            // SEE_MASK_FILEANDURL passes "file\0url".  What a hack!
            if (pExecInfoW->fMask & SEE_MASK_FILEANDURL)
            {
                // We are so lucky that Win9x implements lstrlenW
                int cch = lstrlenW(pExecInfoW->lpFile) + 1;
                cch += lstrlenW(pExecInfoW->lpFile + cch) + 1;
                if (!WideCharToMultiByte(CP_ACP, 0, pExecInfoW->lpFile, cch, szFile, ARRAYSIZE(szFile), NULL, NULL))
                {
                    // Return a completely random error code
                    pExecInfoW->hInstApp = (HINSTANCE)SE_ERR_OOM;
                    SetLastError(ERROR_INVALID_PARAMETER);
                    return FALSE;
                }
            }
        }

        fResult = ShellExecuteExA(&ExecInfoA);

        // Out parameters
        pExecInfoW->hInstApp = ExecInfoA.hInstApp;
        pExecInfoW->hProcess = ExecInfoA.hProcess;
    }
    else
    {
        SetLastError(ERROR_INVALID_PARAMETER);
    }

    return fResult;
}

#endif // NEED_SHELL32_WRAPPER

#ifdef NEED_SHELL32_WRAPPER

int WINAPI SHFileOperationWrapW(LPSHFILEOPSTRUCTW pFileOpW)
{
    // We don't thunk multiple files.
    ASSERT(pFileOpW);
    ASSERT(!(pFileOpW->fFlags & FOF_MULTIDESTFILES));

    if (UseUnicodeShell32())
    {
        return SHFileOperationW(pFileOpW);
    }

    int nResult = 1;    // non-Zero is failure.
    if (pFileOpW)
    {
        SHFILEOPSTRUCTA FileOpA;
        CStrIn strTo(pFileOpW->pTo);
        CStrIn strFrom(pFileOpW->pFrom);
        CStrIn strProgressTitle(pFileOpW->lpszProgressTitle);

        FileOpA = *(LPSHFILEOPSTRUCTA) pFileOpW;
        FileOpA.pFrom = strFrom;
        FileOpA.pTo = strTo;
        FileOpA.lpszProgressTitle = strProgressTitle;


        nResult = SHFileOperationA(&FileOpA);
    }

    return nResult;
}

#endif // NEED_SHELL32_WRAPPER

#ifdef NEED_SHELL32_WRAPPER

UINT WINAPI ExtractIconExWrapW(LPCWSTR pwzFile, int nIconIndex, HICON FAR *phiconLarge, HICON FAR *phiconSmall, UINT nIcons)
{
    if (UseUnicodeShell32())
    {
        return ExtractIconExW(pwzFile, nIconIndex, phiconLarge, phiconSmall, nIcons);
    }

    CStrIn str(pwzFile);
    return ExtractIconExA(str, nIconIndex, phiconLarge, phiconSmall, nIcons);
}

#endif // NEED_SHELL32_WRAPPER


#ifdef NEED_SHELL32_WRAPPER
//
// Shell_GetCachedImageIndexWrapA/W are exported from shlwapi.
// Only the W version should be in NEED_SHELL32_WRAPPER
int WINAPI Shell_GetCachedImageIndexWrapW(LPCWSTR pszIconPath, int iIconIndex, UINT uIconFlags)
{
    if (UseUnicodeShell32())
    {
        return Shell_GetCachedImageIndex(pszIconPath, iIconIndex, uIconFlags);
    }

    CStrIn  strIconPath(pszIconPath);
    return Shell_GetCachedImageIndex((LPCWSTR)((void*)(strIconPath)), iIconIndex, uIconFlags);
}
#endif // NEED_SHELL32_WRAPPER

int WINAPI Shell_GetCachedImageIndexWrapA(LPCSTR pszIconPath, int iIconIndex, UINT uIconFlags)
{
    if (UseUnicodeShell32())
    {
        WCHAR szIconPath[MAX_PATH];
        SHAnsiToUnicode(pszIconPath, szIconPath, ARRAYSIZE(szIconPath));
        return Shell_GetCachedImageIndex(szIconPath, iIconIndex, uIconFlags);
    }
    
    return Shell_GetCachedImageIndex((LPCWSTR)((void*)(pszIconPath)), iIconIndex, uIconFlags);
}


#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI SetFileAttributesWrapW(LPCWSTR pwzFile, DWORD dwFileAttributes)
{
    VALIDATE_PROTOTYPE(SetFileAttributes);

    if (g_bRunningOnNT)
        return SetFileAttributesW(pwzFile, dwFileAttributes);

    CStrIn  str(pwzFile);
    return SetFileAttributesA(str, dwFileAttributes);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

int WINAPI GetNumberFormatWrapW(LCID Locale, DWORD dwFlags, LPCWSTR pwzValue, CONST NUMBERFMTW * pFormatW, LPWSTR pwzNumberStr, int cchNumber)
{
    VALIDATE_PROTOTYPE(GetNumberFormat);

    if (g_bRunningOnNT)
        return GetNumberFormatW(Locale, dwFlags, pwzValue, pFormatW, pwzNumberStr, cchNumber);

    int nResult;
    NUMBERFMTA FormatA;
    CStrIn  strValue(pwzValue);
    CStrIn  strDecimalSep(pFormatW ? pFormatW->lpDecimalSep : NULL);
    CStrIn  strThousandSep(pFormatW ? pFormatW->lpThousandSep : NULL);
    CStrOut strNumberStr(pwzNumberStr, cchNumber);

    if (pFormatW)
    {
        FormatA = *(NUMBERFMTA *) pFormatW;
        FormatA.lpDecimalSep = strDecimalSep;
        FormatA.lpThousandSep = strThousandSep;
    }

    nResult = GetNumberFormatA(Locale, dwFlags, strValue, (pFormatW ? &FormatA : NULL), strNumberStr, strNumberStr.BufSize());
    if (ERROR_SUCCESS == nResult)
        strNumberStr.ConvertIncludingNul();

    return nResult;
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

int WINAPI MessageBoxWrapW(HWND hwnd, LPCWSTR pwzText, LPCWSTR pwzCaption, UINT uType)
{
    VALIDATE_PROTOTYPE(MessageBox);

    if (g_bRunningOnNT)
        return MessageBoxW(hwnd, pwzText, pwzCaption, uType);

    CStrIn  strCaption(pwzCaption);
    CStrIn  strText(pwzText);
    return MessageBoxA(hwnd, strText, strCaption, uType);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI FindNextFileWrapW(HANDLE hSearchHandle, LPWIN32_FIND_DATAW pFindFileDataW)
{
    VALIDATE_PROTOTYPE(FindNextFile);

    if (g_bRunningOnNT)
        return FindNextFileW(hSearchHandle, pFindFileDataW);

    CWin32FindDataInOut fd(pFindFileDataW);
    return FindNextFileA(hSearchHandle, fd);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

//--------------------------------------------------------------
//      GetFullPathNameWrap
//--------------------------------------------------------------

DWORD
WINAPI
GetFullPathNameWrapW( LPCWSTR lpFileName,
                     DWORD  nBufferLength,
                     LPWSTR lpBuffer,
                     LPWSTR *lpFilePart)
{
    VALIDATE_PROTOTYPE(GetFullPathName);
    VALIDATE_OUTBUF(lpBuffer, nBufferLength);

    if (g_bRunningOnNT)
    {
        return GetFullPathNameW(lpFileName, nBufferLength, lpBuffer, lpFilePart);
    }

    CStrIn  strIn(lpFileName);
    CStrOut  strOut(lpBuffer,nBufferLength);
    LPSTR   pFile;
    DWORD   dwRet;

    dwRet = GetFullPathNameA(strIn, nBufferLength, strOut, &pFile);
    strOut.ConvertIncludingNul();
    // REARCHITECT raymondc - This is wrong if we had to do DBCS or related goo
    *lpFilePart = lpBuffer + (pFile - strOut);
    return dwRet;
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

DWORD WINAPI
GetShortPathNameWrapW(
    LPCWSTR lpszLongPath,
    LPWSTR  lpszShortPath,
    DWORD   cchBuffer)
{
    VALIDATE_PROTOTYPE(GetShortPathName);

    if (g_bRunningOnNT)
    {
        return GetShortPathNameW(lpszLongPath, lpszShortPath, cchBuffer);

    }

    CStrIn strLongPath(lpszLongPath);
    CStrOut strShortPath(lpszShortPath, cchBuffer);

    return GetShortPathNameA(strLongPath, strShortPath, strShortPath.BufSize());
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI
GetStringTypeExWrapW(LCID lcid, DWORD dwInfoType, LPCTSTR lpSrcStr, int cchSrc, LPWORD lpCharType)
{
    VALIDATE_PROTOTYPE(GetStringTypeEx);

    if (g_bRunningOnNT)
    {
        return GetStringTypeExW(lcid, dwInfoType, lpSrcStr, cchSrc, lpCharType);
    }

    CStrIn  str(lpSrcStr, cchSrc);
    return GetStringTypeExA(lcid, dwInfoType, str, str.strlen(), lpCharType);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

UINT WINAPI
GetPrivateProfileIntWrapW(
        LPCWSTR lpAppName,
        LPCWSTR lpKeyName,
        INT     nDefault,
        LPCWSTR lpFileName)
{
    VALIDATE_PROTOTYPE(GetPrivateProfileInt);

    if (g_bRunningOnNT)
    {
        return GetPrivateProfileIntW(lpAppName, lpKeyName, nDefault, lpFileName);
    }

    CStrIn  strApp(lpAppName);
    CStrIn  strKey(lpKeyName);
    CPPFIn  strFile(lpFileName); // PrivateProfile filename needs special class

    return GetPrivateProfileIntA(strApp, strKey, nDefault, strFile);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

DWORD WINAPI
GetProfileStringWrapW(
        LPCWSTR lpAppName,
        LPCWSTR lpKeyName,
        LPCWSTR lpDefault,
        LPWSTR  lpBuffer,
        DWORD   dwBuffersize)
{
    VALIDATE_PROTOTYPE(GetProfileString);
    VALIDATE_OUTBUF(lpBuffer, dwBuffersize);

    if (g_bRunningOnNT)
    {
        return GetProfileStringW(lpAppName, lpKeyName, lpDefault, lpBuffer, dwBuffersize);
    }

    CStrIn  strApp(lpAppName);
    CStrIn  strKey(lpKeyName);
    CStrIn  strDefault(lpDefault);
    CStrOut strBuffer(lpBuffer, dwBuffersize);

    GetProfileStringA(strApp, strKey, strDefault, strBuffer, dwBuffersize);
    return strBuffer.ConvertIncludingNul();
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

HANDLE WINAPI
GetPropWrapW(HWND hWnd, LPCWSTR lpString)
{
    VALIDATE_PROTOTYPE(GetProp);

    if (g_bRunningOnNT)
    {
        return GetPropW(hWnd, lpString);
    }

    CStrIn  str(lpString);

    return GetPropA(hWnd, str);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

UINT WINAPI
GetTempFileNameWrapW(
        LPCWSTR lpPathName,
        LPCWSTR lpPrefixString,
        UINT    uUnique,
        LPWSTR  lpTempFileName)
{
    VALIDATE_PROTOTYPE(GetTempFileName);
    VALIDATE_OUTBUF(lpTempFileName, MAX_PATH);

    if (g_bRunningOnNT)
    {
        return GetTempFileNameW(lpPathName, lpPrefixString, uUnique, lpTempFileName);
    }


    CStrIn  strPath(lpPathName);
    CStrIn  strPrefix(lpPrefixString);
    CStrOut strFileName(lpTempFileName, MAX_PATH);

    return GetTempFileNameA(strPath, strPrefix, uUnique, strFileName);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

DWORD WINAPI
GetTempPathWrapW(DWORD nBufferLength, LPWSTR lpBuffer)
{
    VALIDATE_PROTOTYPE(GetTempPath);
    VALIDATE_OUTBUF(lpBuffer, nBufferLength);

    if (g_bRunningOnNT)
    {
        return GetTempPathW(nBufferLength, lpBuffer);
    }


    CStrOut str(lpBuffer, nBufferLength);

    GetTempPathA(str.BufSize(), str);
    return str.ConvertExcludingNul();
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

BOOL APIENTRY
GetTextExtentPoint32WrapW(
        HDC     hdc,
        LPCWSTR pwsz,
        int     cb,
        LPSIZE  pSize)
{
    VALIDATE_PROTOTYPE(GetTextExtentPoint32);

    if (_MayNeedFontLinking(pwsz, cb))
    {
        return GetTextExtentPointFLW(hdc, pwsz, cb, pSize);
    }
    else if (g_bRunningOnNT)
    {
        return GetTextExtentPoint32W(hdc, pwsz, cb, pSize);
    }

    CStrIn str(pwsz,cb);

    return GetTextExtentPoint32A(hdc, str, str.strlen(), pSize);
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

int WINAPI
GetTextFaceWrapW(
        HDC    hdc,
        int    cch,
        LPWSTR lpFaceName)
{
    VALIDATE_PROTOTYPE(GetTextFace);
    VALIDATE_OUTBUF(lpFaceName, cch);

    if (g_bRunningOnNT)
    {
        return GetTextFaceW(hdc, cch, lpFaceName);
    }


    CStrOut str(lpFaceName, cch);

    GetTextFaceA(hdc, str.BufSize(), str);
    return str.ConvertIncludingNul();
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

BOOL WINAPI
GetTextMetricsWrapW(HDC hdc, LPTEXTMETRICW lptm)
{
    VALIDATE_PROTOTYPE(GetTextMetrics);

    if (g_bRunningOnNT)
    {
        return GetTextMetricsW(hdc, lptm);
    }


   BOOL         ret;
   TEXTMETRICA  tm;

    ret = GetTextMetricsA(hdc, &tm);

    if (ret)
    {
        lptm->tmHeight              = tm.tmHeight;
        lptm->tmAscent              = tm.tmAscent;
        lptm->tmDescent             = tm.tmDescent;
        lptm->tmInternalLeading     = tm.tmInternalLeading;
        lptm->tmExternalLeading     = tm.tmExternalLeading;
        lptm->tmAveCharWidth        = tm.tmAveCharWidth;
        lptm->tmMaxCharWidth        = tm.tmMaxCharWidth;
        lptm->tmWeight              = tm.tmWeight;
        lptm->tmOverhang            = tm.tmOverhang;
        lptm->tmDigitizedAspectX    = tm.tmDigitizedAspectX;
        lptm->tmDigitizedAspectY    = tm.tmDigitizedAspectY;
        lptm->tmItalic              = tm.tmItalic;
        lptm->tmUnderlined          = tm.tmUnderlined;
        lptm->tmStruckOut           = tm.tmStruckOut;
        lptm->tmPitchAndFamily      = tm.tmPitchAndFamily;
        lptm->tmCharSet             = tm.tmCharSet;

        // LPBYTE -> LPCSTR casts below
        MultiByteToWideChar(CP_ACP, 0, (LPCSTR)&tm.tmFirstChar, 1, &lptm->tmFirstChar, 1);
        MultiByteToWideChar(CP_ACP, 0, (LPCSTR)&tm.tmLastChar, 1, &lptm->tmLastChar, 1);
        MultiByteToWideChar(CP_ACP, 0, (LPCSTR)&tm.tmDefaultChar, 1, &lptm->tmDefaultChar, 1);
        MultiByteToWideChar(CP_ACP, 0, (LPCSTR)&tm.tmBreakChar, 1, &lptm->tmBreakChar, 1);
    }

    return ret;
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

BOOL WINAPI GetUserNameWrapW(LPWSTR pszBuffer, LPDWORD pcch)
{
    VALIDATE_PROTOTYPE(GetUserName);

    BOOL fRet;

    if (UseUnicodeShell32())
    {
        fRet = GetUserNameW(pszBuffer, pcch);
    }
    else
    {
        CStrOut stroBuffer(pszBuffer, *pcch);

        fRet = GetUserNameA(stroBuffer, pcch);

        if (fRet)
            *pcch = stroBuffer.ConvertIncludingNul();
    }

    return fRet;
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_USER32_WRAPPER

LONG FORWARD_API WINAPI
GetWindowLongWrapW(HWND hWnd, int nIndex)
{
    VALIDATE_PROTOTYPE(GetWindowLong);

    FORWARD_AW(GetWindowLong, (hWnd, nIndex));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

int WINAPI
GetWindowTextWrapW(HWND hWnd, LPWSTR lpString, int nMaxCount)
{
    VALIDATE_PROTOTYPE(GetWindowText);
    VALIDATE_OUTBUF(lpString, nMaxCount);

    if (MLIsEnabled(hWnd))
        return MLGetControlTextI(hWnd, lpString, nMaxCount);

    if (g_bRunningOnNT)
    {
        return GetWindowTextW(hWnd, lpString, nMaxCount);
    }


    CStrOut str(lpString, nMaxCount);

    GetWindowTextA(hWnd, str, str.BufSize());
    return str.ConvertExcludingNul();
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

int WINAPI
GetWindowTextLengthWrapW(HWND hWnd)
{
    VALIDATE_PROTOTYPE(GetWindowTextLength);

    if (g_bRunningOnNT)
    {
        return GetWindowTextLengthW(hWnd);
    }

    return GetWindowTextLengthA(hWnd);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

UINT WINAPI
GetWindowsDirectoryWrapW(LPWSTR lpWinPath, UINT cch)
{
    VALIDATE_PROTOTYPE(GetWindowsDirectory);
    VALIDATE_OUTBUF(lpWinPath, cch);

    if (g_bRunningOnNT)
    {
        return GetWindowsDirectoryW(lpWinPath, cch);
    }

    CStrOut str(lpWinPath, cch);

    GetWindowsDirectoryA(str, str.BufSize());

    return str.ConvertExcludingNul();
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

STDAPI_(DWORD) WINAPI
GetEnvironmentVariableWrapW(LPCWSTR lpName, LPWSTR lpBuffer, DWORD nSize)
{
    VALIDATE_PROTOTYPE(GetEnvironmentVariable);
    VALIDATE_OUTBUF(lpBuffer, nSize);

    if (g_bRunningOnNT)
    {
        return GetEnvironmentVariableW(lpName,lpBuffer, nSize);
    }

    CStrOut str(lpBuffer, nSize);
    CStrIn  strName(lpName);

    GetEnvironmentVariableA(strName, str, str.BufSize());
    return str.ConvertExcludingNul();

}


#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL FORWARD_API WINAPI
IsDialogMessageWrapW(HWND hWndDlg, LPMSG lpMsg)
{
    VALIDATE_PROTOTYPE(IsDialogMessage);

    FORWARD_AW(IsDialogMessage, (hWndDlg, lpMsg));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

HACCEL WINAPI
LoadAcceleratorsWrapW(HINSTANCE hInstance, LPCWSTR lpTableName)
{
    VALIDATE_PROTOTYPE(LoadAccelerators);
    ASSERT(HIWORD64(lpTableName) == 0);

    if (g_bRunningOnNT)
    {
        return LoadAcceleratorsW(hInstance, lpTableName);
    }

    return LoadAcceleratorsA(hInstance, (LPCSTR) lpTableName);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

HBITMAP WINAPI
LoadBitmapWrapW(HINSTANCE hInstance, LPCWSTR lpBitmapName)
{
    VALIDATE_PROTOTYPE(LoadBitmap);
    ASSERT(HIWORD64(lpBitmapName) == 0);

    if (g_bRunningOnNT)
    {
        return LoadBitmapW(hInstance, lpBitmapName);
    }

    return LoadBitmapA(hInstance, (LPCSTR) lpBitmapName);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

HCURSOR WINAPI
LoadCursorWrapW(HINSTANCE hInstance, LPCWSTR lpCursorName)
{
    VALIDATE_PROTOTYPE(LoadCursor);
    ASSERT(HIWORD64(lpCursorName) == 0);

    if (g_bRunningOnNT)
    {
        return LoadCursorW(hInstance, lpCursorName);
    }

    return LoadCursorA(hInstance, (LPCSTR) lpCursorName);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

HICON WINAPI
LoadIconWrapW(HINSTANCE hInstance, LPCWSTR lpIconName)
{
    VALIDATE_PROTOTYPE(LoadIcon);
    ASSERT(HIWORD64(lpIconName) == 0);

    if (g_bRunningOnNT)
    {
        return LoadIconW(hInstance, lpIconName);
    }

    return LoadIconA(hInstance, (LPCSTR) lpIconName);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

//+-------------------------------------------------------------------------
// Returns an icon from a dll/exe loaded as a datafile.  Note that this is 
// very similar to the standard LoadImage API.  However, LoadImage does
// not work properly (returns bogus icons) on win95/98 when the dll is
// loaded as a datafile.  (Stevepro 18-Sept-98)
//--------------------------------------------------------------------------
HICON _LoadIconFromInstanceA
(
    HINSTANCE hInstance,    // instance containing the icon
    LPCSTR pszName,         // name or id of icon
    int cxDesired,
    int cyDesired,
    UINT fuLoad             // load flags, LR_DEFAULTCOLOR | LR_MONOCHROME
) 
{ 
    HICON hIcon;

    UINT fUnsupportedFlags = ~(LR_DEFAULTCOLOR | LR_MONOCHROME);
    if (fuLoad & fUnsupportedFlags)
    {
        // We saw a flag that we don't support, so default to the standard API
        hIcon = (HICON)LoadImageA(hInstance, pszName, IMAGE_ICON, cxDesired, cyDesired, fuLoad);
    }
    else
    {
        // Find the group icon 
        HRSRC hRsrc; 
        HGLOBAL hGlobal; 
        PBYTE pIconDir;

        if ((hRsrc = FindResourceA(hInstance, pszName, (LPCSTR)RT_GROUP_ICON)) == NULL ||
            (hGlobal = LoadResource(hInstance, hRsrc)) == NULL || 
            (pIconDir = (PBYTE)LockResource(hGlobal)) == NULL)
        {
            // Note that FreeResource and UnlockResource are obsolete APIs, so we just exit.
            return NULL; 
        }
 
        // Find the icon that best matches the desired size 
        int nID = LookupIconIdFromDirectoryEx(pIconDir, TRUE, cxDesired, cyDesired, fuLoad); 

        PBYTE pbRes; 
        if (0 == nID ||
            (hRsrc = FindResourceA(hInstance, MAKEINTRESOURCEA(nID), (LPCSTR)RT_ICON)) == NULL ||
            (hGlobal = LoadResource(hInstance, hRsrc)) == NULL || 
            (pbRes = (PBYTE)LockResource(hGlobal)) == NULL)
        {
            return NULL; 
        }

        // Let the OS make us an icon 
        LPBITMAPINFOHEADER pbmh = (LPBITMAPINFOHEADER)pbRes;
        hIcon = CreateIconFromResourceEx(pbRes, SizeofResource(hInstance, hRsrc), TRUE, 0x00030000,  
                pbmh->biWidth, pbmh->biHeight/2, fuLoad); 
     
        // It failed, odds are good we're on NT so try the non-Ex way 
        if (hIcon == NULL) 
        { 
            // We would break on NT if we try with a 16bpp image 
            if(pbmh->biBitCount != 16) 
            { 
                hIcon = CreateIconFromResource(pbRes, SizeofResource(hInstance, hRsrc), TRUE, 0x00030000); 
            } 
        } 
    }

    return hIcon; 
}

HANDLE WINAPI
LoadImageWrapA(
        HINSTANCE hInstance,
        LPCSTR lpName,
        UINT uType,
        int cxDesired,
        int cyDesired,
        UINT fuLoad)
{
    VALIDATE_PROTOTYPE(LoadImage);

    //
    // If the hInstance is loaded as a datafile, LoadImage returns the 
    // wrong icons on win95/98.  (Kernel can't find the datafile and uses
    // null instead)
    //
    // APPCOMPAT: Should also fix loading other resource types from datafiles
    //
    if (!g_bRunningOnNT && uType == IMAGE_ICON)
    {
        return _LoadIconFromInstanceA(
                hInstance,
                lpName,
                cxDesired,
                cyDesired,
                fuLoad);
    }

    return LoadImageA(
        hInstance,
        lpName,
        uType,
        cxDesired,
        cyDesired,
        fuLoad);
}


HANDLE WINAPI
LoadImageWrapW(
        HINSTANCE hInstance,
        LPCWSTR lpName,
        UINT uType,
        int cxDesired,
        int cyDesired,
        UINT fuLoad)
{
    VALIDATE_PROTOTYPE(LoadImage);

    if (g_bRunningOnNT)
    {
        return LoadImageW(
            hInstance,
            lpName,
            uType,
            cxDesired,
            cyDesired,
            fuLoad);
    }

    CStrIn  str(lpName);

    return LoadImageWrapA(
            hInstance,
            str,
            uType,
            cxDesired,
            cyDesired,
            fuLoad);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

HINSTANCE WINAPI
LoadLibraryExWrapW(
        LPCWSTR lpLibFileName,
        HANDLE  hFile,
        DWORD   dwFlags)
{
    VALIDATE_PROTOTYPE(LoadLibraryEx);

    if (g_bRunningOnNT)
        return LoadLibraryExW(lpLibFileName, hFile, dwFlags);

    CStrIn  str(lpLibFileName);

    // Win9X will crash if the pathname is longer than MAX_PATH bytes.

    if (str.strlen() >= MAX_PATH)
    {
        SetLastError( ERROR_BAD_PATHNAME );
        return NULL;
    }
    else
    {
        return LoadLibraryExA(str, hFile, dwFlags);
    }
}

#endif // NEED_KERNEL32_WRAPPER

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

BOOL fDoMungeLangId(LANGID lidUI);

BOOL NeedMenuOwnerDraw(void)
{
    // FEATURE: need to be improved
    //         It checks whether current UI language is cross codepage or not
    if (g_bRunningOnNT5OrHigher)
        return FALSE;

    return fDoMungeLangId(MLGetUILanguage());
}

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

#define PUI_OWNERDRAW_SIG   0xFFFF0000

// NOTE: This structure is only visible from subclassed OwnerDrawSubclassProc
//       with WM_DRAWITEM and WM_MEASUREITEM. All other places won't see this
//       structure. We restore original format MENUITEMINFO structure without
//       MFT_OWNERDRAW flag unless it's a real owner draw item.
//       For this, we do/undo munge in Get/SetMenuItemInfo and InsertMenuItem.

typedef struct tagPUIMENUITEM
{
    DWORD  dwSig;       // signature for this structure
    HMENU  hmenu;       // menu handle
    UINT   fMask;       // original MENUITEMINFO fMask value
    UINT   fType;       // original MENUITEMINFO fType value
    DWORD_PTR dwItemData; // original MENUITEMINFO dwItemData value
    LPWSTR lpwz;        // unicode menu string
    UINT   cch;         // number of character for menu string
    UINT   iTabLength;
} PUIMENUITEM, *LPPUIMENUITEM;

void MungeMenuItem(HMENU hMenu, LPCMENUITEMINFOW lpmiiW, LPMENUITEMINFOW lpmiiNewW)
{
    if (lpmiiW && lpmiiNewW)
    {
        *lpmiiNewW = *lpmiiW;

        if ((MIIM_TYPE & lpmiiW->fMask) && !(MFT_NONSTRING & lpmiiW->fType))
        {
            LPWSTR lpmenuText = (LPWSTR)lpmiiW->dwTypeData;

            if (lpmenuText)
            {
                LPPUIMENUITEM lpItem = (LPPUIMENUITEM)LocalAlloc(LPTR, sizeof(PUIMENUITEM));

                if (lpItem)
                {
                    lpItem->dwSig = PUI_OWNERDRAW_SIG;
                    lpItem->hmenu = hMenu;
                    lpItem->fMask = lpmiiW->fMask;
                    lpItem->fType = lpmiiW->fType;
                    lpItem->dwItemData = lpmiiW->dwItemData;
                    lpItem->cch = lstrlenW(lpmenuText);
                    lpItem->lpwz = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (lpItem->cch + 1));
                    if (lpItem->lpwz)
                        StrCpyNW(lpItem->lpwz, lpmenuText, lpItem->cch + 1);
                    else
                        lpItem->cch = 0;

                    lpmiiNewW->fType |= MFT_OWNERDRAW;
                    lpmiiNewW->fMask |= MIIM_DATA;
                    lpmiiNewW->dwItemData = (ULONG_PTR)lpItem;
                    lpmiiNewW->dwTypeData = 0;
                    lpmiiNewW->cch = 0;
                }
            }
        }
    }
}

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

void DeleteOwnerDrawMenuItem(HMENU hMenu, UINT uItem, BOOL fByPosition);

void DeleteOwnerDrawMenu(HMENU hMenu)
{
    int i, nItem = GetMenuItemCount(hMenu);

    for (i = 0; i < nItem; i++)
        DeleteOwnerDrawMenuItem(hMenu, i, TRUE);
}

void DeleteOwnerDrawMenuItem(HMENU hMenu, UINT uItem, BOOL fByPosition)
{
    MENUITEMINFOA miiA;

    miiA.cbSize = sizeof(miiA);
    miiA.fMask = MIIM_TYPE | MIIM_ID | MIIM_SUBMENU | MIIM_DATA;
    miiA.cch = 0;
    if (GetMenuItemInfoA(hMenu, uItem, fByPosition, &miiA))
    {
        if (miiA.hSubMenu)
            DeleteOwnerDrawMenu(miiA.hSubMenu);
        else
        {
            if ((MIIM_TYPE & miiA.fMask) && (MFT_OWNERDRAW & miiA.fType))
            {
                LPPUIMENUITEM lpItem = (LPPUIMENUITEM)miiA.dwItemData;

                if (!IsBadReadPtr(lpItem, sizeof(lpItem->dwSig)) && PUI_OWNERDRAW_SIG == lpItem->dwSig && !(MFT_OWNERDRAW & lpItem->fType))
                {
                    if (lpItem->lpwz && lpItem->cch)
                        LocalFree(lpItem->lpwz);
                    LocalFree(lpItem);
                }
            }
        }
    }
}

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

BOOL WINAPI
DeleteMenuWrap(HMENU hMenu, UINT uPosition, UINT uFlags)
{
    VALIDATE_PROTOTYPEX(DeleteMenu);

    static BOOL bOwnerDraw = NeedMenuOwnerDraw();

    if (bOwnerDraw)
        DeleteOwnerDrawMenuItem(hMenu, uPosition, (MF_BYPOSITION & uFlags)? TRUE: FALSE);

    return DeleteMenu(hMenu, uPosition, uFlags);
}


// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

BOOL WINAPI
DestroyMenuWrap(HMENU hMenu)
{
    VALIDATE_PROTOTYPEX(DestroyMenu);

    static BOOL bOwnerDraw = NeedMenuOwnerDraw();

    if (bOwnerDraw)
        DeleteOwnerDrawMenu(hMenu);

    return DestroyMenu(hMenu);
}

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

LPBYTE MenuLoadMENUTemplates(LPBYTE lpMenuTemplate, HMENU *phMenu)
{
    HMENU hMenu;
    UINT menuFlags = 0;
    ULONG_PTR menuId = 0;
    LPWSTR lpmenuText;
    MENUITEMINFO mii;

    if (!(hMenu = CreateMenu()))
        goto memoryerror;

    do
    {
        // Get the menu flags.
        menuFlags = (UINT)(*(WORD *)lpMenuTemplate);
        lpMenuTemplate += 2;
        if (menuFlags & ~MF_VALID) {
            goto memoryerror;
        }

        if (!(menuFlags & MF_POPUP))
        {
            menuId = *(WORD *)lpMenuTemplate;
            lpMenuTemplate += 2;
        }

        lpmenuText = (LPWSTR)lpMenuTemplate;

        if (*lpmenuText)    // If a string exists, then skip to the end of it.
            lpMenuTemplate += lstrlenW(lpmenuText) * sizeof(WCHAR);
        else
            lpmenuText = NULL;

        // Skip over terminating NULL of the string (or the single NULL if empty string).
        lpMenuTemplate += sizeof(WCHAR);
        lpMenuTemplate = (BYTE *)(((ULONG_PTR)lpMenuTemplate + 1) & ~1);    // word align

        ZeroMemory(&mii, sizeof(mii));
        mii.cbSize = sizeof(MENUITEMINFO);
        mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;

        if (menuFlags & MF_POPUP)
        {
            mii.fMask |= MIIM_SUBMENU;
            lpMenuTemplate = MenuLoadMENUTemplates(lpMenuTemplate, (HMENU *)&menuId);
            if (!lpMenuTemplate)
                goto memoryerror;
            mii.hSubMenu = (HMENU)menuId;
        }

        // Don't allow bitmaps from the resource file.
        if (menuFlags & MF_BITMAP)
            menuFlags = (UINT)((menuFlags | MFT_RIGHTJUSTIFY) & ~MF_BITMAP);

        // We have to take out MFS_HILITE since that bit marks the end of a menu in
        // a resource file.  Since we shouldn't have any pre hilited items in the
        // menu anyway, this is no big deal.
        mii.fState = (menuFlags & MFS_OLDAPI_MASK) & ~MFS_HILITE;
        mii.fType = (menuFlags & MFT_OLDAPI_MASK);
        if (menuFlags & MFT_OWNERDRAW)
        {
            mii.fMask |= MIIM_DATA;
            mii.dwItemData = (ULONG_PTR) lpmenuText;
            lpmenuText = 0;
        }

        mii.dwTypeData = (LPTSTR)lpmenuText;
        mii.cch = (UINT)-1;
        mii.wID = (UINT)menuId;

        if (!InsertMenuItemWrapW(hMenu, 0xFFFFFFFF, TRUE, &mii))
        {
            if (menuFlags & MF_POPUP)
                DestroyMenuWrap(mii.hSubMenu);
            goto memoryerror;
        }
    } while (!(menuFlags & MF_END));

    *phMenu = hMenu;
    return lpMenuTemplate;

memoryerror:
    if (hMenu != NULL)
        DestroyMenuWrap(hMenu);
    *phMenu = NULL;
    return NULL;
}

PMENUITEMTEMPLATE2 MenuLoadMENUEXTemplates(PMENUITEMTEMPLATE2 lpMenuTemplate, HMENU *phMenu, WORD wResInfo)
{
    HMENU hMenu;
    HMENU hSubMenu;
    long menuId = 0;
    LPWSTR lpmenuText;
    MENUITEMINFO mii;
    UINT cch = 0;
    DWORD dwHelpID;

    if (!(hMenu = CreateMenu()))
        goto memoryerror;

    do
    {
        if (!(wResInfo & MFR_POPUP))
        {
            // If the PREVIOUS wResInfo field was not a POPUP, the
            // dwHelpID field is not there.  Back up so things fit.
            lpMenuTemplate = (PMENUITEMTEMPLATE2)(((LPBYTE)lpMenuTemplate) - sizeof(lpMenuTemplate->dwHelpID));
            dwHelpID = 0;
        }
        else
            dwHelpID = lpMenuTemplate->dwHelpID;

        menuId = lpMenuTemplate->menuId;

        ZeroMemory(&mii, sizeof(mii));
        mii.cbSize = sizeof(MENUITEMINFO);
        mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;

        mii.fType = lpMenuTemplate->fType;
        if (mii.fType & ~MFT_MASK)
            goto memoryerror;

        mii.fState  = lpMenuTemplate->fState;
        if (mii.fState & ~MFS_MASK)
            goto memoryerror;

        wResInfo = lpMenuTemplate->wResInfo;
        if (wResInfo & ~(MF_END | MFR_POPUP))
            goto memoryerror;

        if (dwHelpID)
            SetMenuContextHelpId(hMenu,dwHelpID);

        if (lpMenuTemplate->mtString[0])
            lpmenuText = lpMenuTemplate->mtString;
        else
            lpmenuText = NULL;

        cch = lstrlenW(lpmenuText);
        mii.dwTypeData = (LPTSTR) lpmenuText;

        // skip to next menu item template (DWORD boundary)
        lpMenuTemplate = (PMENUITEMTEMPLATE2)
                (((LPBYTE)lpMenuTemplate) +
                sizeof(MENUITEMTEMPLATE2) +
                ((cch * sizeof(WCHAR) + 3) & ~3));

        if (mii.fType & MFT_OWNERDRAW)
        {
            mii.fMask |= MIIM_DATA;
            mii.dwItemData = (ULONG_PTR) mii.dwTypeData;
            mii.dwTypeData = 0;
        }

        if (wResInfo & MFR_POPUP)
        {
            mii.fMask |= MIIM_SUBMENU;
            lpMenuTemplate = MenuLoadMENUEXTemplates(lpMenuTemplate, &hSubMenu, MFR_POPUP);
            if (lpMenuTemplate == NULL)
                goto memoryerror;
            mii.hSubMenu = hSubMenu;
        }

        // Don't allow bitmaps from the resource file.
        if (mii.fType & MFT_BITMAP)
            mii.fType = (mii.fType | MFT_RIGHTJUSTIFY) & ~MFT_BITMAP;

        mii.cch = (UINT)-1;
        mii.wID = menuId;
        if (!InsertMenuItemWrapW(hMenu, 0xFFFFFFFF, TRUE, &mii))
        {
            if (wResInfo & MFR_POPUP)
                DestroyMenuWrap(mii.hSubMenu);
            goto memoryerror;
        }
        wResInfo &= ~MFR_POPUP;
    } while (!(wResInfo & MFR_END));

    *phMenu = hMenu;
    return lpMenuTemplate;

memoryerror:
    if (hMenu != NULL)
        DestroyMenuWrap(hMenu);
    *phMenu = NULL;
    return NULL;
}

HMENU CreateMenuFromResource(LPBYTE lpMenuTemplate)
{
    HMENU hMenu = NULL;
    UINT menuTemplateVersion;
    UINT menuTemplateHeaderSize;

    // menu resource: First, strip version number word out of the menu
    // template.  This value should be 0 for MENU, 1 for MENUEX.
    menuTemplateVersion = *(WORD *)lpMenuTemplate;
    lpMenuTemplate += 2;
    if (menuTemplateVersion > 1)
        return NULL;

    menuTemplateHeaderSize = *(WORD *)lpMenuTemplate;
    lpMenuTemplate += 2;
    lpMenuTemplate += menuTemplateHeaderSize;
    switch (menuTemplateVersion)
    {
        case 0:
            MenuLoadMENUTemplates(lpMenuTemplate, &hMenu);
            break;

        case 1:
            MenuLoadMENUEXTemplates((PMENUITEMTEMPLATE2)lpMenuTemplate, &hMenu, 0);
            break;
    }
    return hMenu;
}

HMENU WINAPI
LoadMenuWrapW(HINSTANCE hInstance, LPCWSTR lpMenuName)
{
    VALIDATE_PROTOTYPE(LoadMenu);
    ASSERT(HIWORD64(lpMenuName) == 0);

    if (NeedMenuOwnerDraw() && MLIsMLHInstance(hInstance))
    {
        HRSRC hRes;

        if (hRes = FindResourceWrapW(hInstance, lpMenuName, RT_MENU))
        {
            LPBYTE lpMenuTemplate = (LPBYTE)LoadResource(hInstance, hRes);

            if (lpMenuTemplate)
            {
                HMENU hmenu = CreateMenuFromResource(lpMenuTemplate);

                if (hmenu)
                    return hmenu;
            }
        }
    }

    if (g_bRunningOnNT)
    {
        return LoadMenuW(hInstance, lpMenuName);
    }

    return LoadMenuA(hInstance, (LPCSTR) lpMenuName);
}

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

#define CX_MENUCHECK   8
#define CY_MENUCHECK   8

#define CHK_RADIO      0x006C
#define CHK_NORMAL     0x00FC

int FindCharPosition(LPCWSTR lpwStr, WCHAR wch)
{
    int iPos;
    int iLength = lstrlenW(lpwStr);

    for (iPos = 0; iPos < iLength; iPos++)
    {
        if (lpwStr[iPos] == wch)
            break;
    }

    return (iPos < iLength) ? iPos : -1;
}

int CalculateMenuTabString(HWND hWnd, HMENU hMenu)
{
    int   i;
    int   iMenuCount = GetMenuItemCount(hMenu);
    int   iLength;
    int   iTabPos = 0;
    int   iMaxTabLength = 0;
    RECT  rc;
    HDC   hdc;
    HFONT hfont, hfontSav;
    NONCLIENTMETRICSA ncm;
    MENUITEMINFOA miiA;

    ncm.cbSize = sizeof(ncm);
    if (!SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
        return 0;

    hfont = CreateFontIndirectA(&ncm.lfMenuFont);
    if (NULL == hfont)
        return 0;

    hdc = GetDC(hWnd);
    hfontSav = (HFONT)SelectObject(hdc, hfont);

    for (i = 0; i < iMenuCount; i++)
    {
        miiA.cbSize = sizeof(miiA);
        miiA.fMask = MIIM_TYPE | MIIM_ID | MIIM_SUBMENU | MIIM_DATA;
        miiA.cch = 0;

        if (GetMenuItemInfoA(hMenu, i, MF_BYPOSITION, &miiA))
        {
            if (miiA.hSubMenu)
                CalculateMenuTabString(hWnd, miiA.hSubMenu);

            if ((MIIM_TYPE & miiA.fMask) && (MFT_OWNERDRAW & miiA.fType))
            {
                LPPUIMENUITEM lpItem = (LPPUIMENUITEM)miiA.dwItemData;

                if (!IsBadReadPtr(lpItem, sizeof(lpItem->dwSig)) && PUI_OWNERDRAW_SIG == lpItem->dwSig && !(MFT_OWNERDRAW & lpItem->fType))
                {
                    if (lpItem->lpwz && lpItem->cch)
                    {
                        iTabPos = FindCharPosition(lpItem->lpwz, L'\t');

                        if (iTabPos >= 0)
                        {
                            DrawTextFLW(hdc, lpItem->lpwz + iTabPos + 1,
                                        lstrlenW(lpItem->lpwz) - iTabPos - 1, &rc, DT_SINGLELINE | DT_CALCRECT);

                            iLength = rc.right - rc.left;
                            if (iLength > iMaxTabLength)
                                iMaxTabLength = iLength;
                        }
                    }
                }
            }
        }
    }

    for (i = 0; i < iMenuCount; i++)
    {
        miiA.cbSize = sizeof(miiA);
        miiA.fMask = MIIM_TYPE | MIIM_ID | MIIM_SUBMENU | MIIM_DATA;
        miiA.cch = 0;

        if (GetMenuItemInfoA(hMenu, i, MF_BYPOSITION, &miiA))
        {
            if ((MIIM_TYPE & miiA.fMask) && (MFT_OWNERDRAW & miiA.fType))
            {
                LPPUIMENUITEM lpItem = (LPPUIMENUITEM)miiA.dwItemData;

                if (!IsBadReadPtr(lpItem, sizeof(lpItem->dwSig)) && PUI_OWNERDRAW_SIG == lpItem->dwSig && !(MFT_OWNERDRAW & lpItem->fType))
                {
                    if (lpItem->lpwz && lpItem->cch)
                    {
                        lpItem->iTabLength = iMaxTabLength;
                    }
                }
            }
        }
    }

    SelectObject(hdc, hfontSav);
    DeleteObject(hfont);
    ReleaseDC(hWnd, hdc);

    return iMaxTabLength;
}

void DrawMenuItemCheckMark(HDC hdc, LPCSTR lpChkSym, LPRECT lprc, UINT uFormat)
{
    HFONT   hFont, hFontSave;
    NONCLIENTMETRICSA ncm;

    ncm.cbSize = sizeof(ncm);
    if (SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
    {
        StrCpyNA(ncm.lfMenuFont.lfFaceName, "Wingdings", LF_FACESIZE);
        ncm.lfMenuFont.lfCharSet = SYMBOL_CHARSET;
        hFont = CreateFontIndirectA(&ncm.lfMenuFont);
        if ( hFont == NULL )
            return;

        hFontSave = (HFONT)SelectObject(hdc, hFont);

        DrawTextA(hdc, lpChkSym, 1, lprc, uFormat); 
        hFont = (HFONT)SelectObject(hdc, hFontSave);
        DeleteObject(hFont);
    }
}

void DrawMenuItemText(HDC hdc, LPCWSTR lpwMenuStr, LPRECT lprc, UINT uiRefData)
{
    UINT uFormat    = DT_VCENTER | DT_SINGLELINE | DT_LEFT  | DT_EXPANDTABS;
    int  iTabPos;

    iTabPos = FindCharPosition(lpwMenuStr, L'\t');
    if(iTabPos < 0)
    {
        // No TAB string is in the menu string
        DrawTextFLW(hdc, lpwMenuStr, lstrlenW(lpwMenuStr), lprc, uFormat);
    }
    else
    {
        RECT rc;

        // Draw Text before TAB
        DrawTextFLW(hdc, lpwMenuStr, iTabPos, lprc, uFormat);

        // Adjust RC for after TAB string.
        CopyRect(&rc, lprc);
        rc.left = rc.right - uiRefData - GetSystemMetrics(SM_CXMENUCHECK);
        DrawTextFLW(hdc, lpwMenuStr + iTabPos + 1, lstrlenW(lpwMenuStr) - iTabPos - 1, &rc, uFormat);
    }

}

void DrawMenuItem(LPDRAWITEMSTRUCT lpdis, LPRECT lprc)
{
    LPPUIMENUITEM lpItem = (LPPUIMENUITEM)lpdis->itemData;

    if (lpItem && lpItem->lpwz)
    {
        HDC   hdc = lpdis->hDC;
        UINT  uFormat = DT_VCENTER | DT_SINGLELINE | DT_LEFT | DT_EXPANDTABS;
        CHAR  ChkSym[2] = {(CHAR)CHK_NORMAL, 0x00}; 
        COLORREF clrTxt;
        int   iBkMode;
        RECT  rc;

        if (ODS_CHECKED & lpdis->itemState)
        {
            MENUITEMINFOA miiA;

            miiA.cbSize = sizeof(miiA);
            miiA.fMask = MIIM_TYPE;
            miiA.cch = 0;
            if (GetMenuItemInfoA(lpItem->hmenu, lpdis->itemID, FALSE, &miiA))
            {
                if (MFT_RADIOCHECK & miiA.fType)
                    ChkSym[0] = CHK_RADIO; 
            }
        }

        iBkMode = SetBkMode(hdc, TRANSPARENT);

        if (ODS_GRAYED & lpdis->itemState)
        {
            clrTxt = SetTextColor(hdc, GetSysColor(COLOR_BTNHIGHLIGHT));

            if (ODS_CHECKED & lpdis->itemState)
            {
                CopyRect(&rc, &(lpdis->rcItem));
                OffsetRect(&rc, 1, 1);
                DrawMenuItemCheckMark(hdc, ChkSym, &rc, uFormat);
            }

            CopyRect(&rc, lprc);
            OffsetRect(&rc, 1, 1);
            DrawMenuItemText(hdc, lpItem->lpwz, &rc, lpItem->iTabLength);

            SetTextColor(hdc, GetSysColor(COLOR_BTNSHADOW));
        }

        if (ODS_CHECKED & lpdis->itemState)
            DrawMenuItemCheckMark(hdc, ChkSym, &(lpdis->rcItem), uFormat);
        DrawMenuItemText(hdc, lpItem->lpwz, lprc, lpItem->iTabLength);

        if (ODS_GRAYED & lpdis->itemState)
            SetTextColor(hdc, clrTxt);
        SetBkMode(hdc, iBkMode);
    }
}

LRESULT OwnerDrawSubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
        case WM_DRAWITEM:
            if (0 == wParam)    // This is a menu
            {
                LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
                LPPUIMENUITEM lpItem = (LPPUIMENUITEM)lpdis->itemData;

                if (lpdis->CtlType == ODT_MENU
                    && !IsBadReadPtr(lpItem, sizeof(lpItem->dwSig)) && PUI_OWNERDRAW_SIG == lpItem->dwSig
                    && !(MFT_OWNERDRAW & lpItem->fType))
                {
                    int cyTemp;
                    RECT rc;
                    HDC hdc = lpdis->hDC;
                    HFONT hfont, hfontSav;
                    TEXTMETRICA tm;
                    NONCLIENTMETRICSA ncm;

                    ncm.cbSize = sizeof(ncm);
                    if (!SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
                        break;

                    hfont = CreateFontIndirectA(&ncm.lfMenuFont);
                    if (NULL == hfont)
                        break;

                    hfontSav = (HFONT)SelectObject(hdc, hfont);

                    GetTextMetricsA(hdc, &tm);
                    CopyRect(&rc, &lpdis->rcItem);
                    rc.left += GetSystemMetrics(SM_CXMENUCHECK);
                    rc.top += tm.tmExternalLeading;

                    cyTemp = (lpdis->rcItem.bottom - lpdis->rcItem.top) - (tm.tmHeight + tm.tmExternalLeading + GetSystemMetrics(SM_CYBORDER));
                    if (cyTemp > 0)
                        rc.top += (cyTemp / 2);

                    switch (lpdis->itemAction)
                    {
                        case ODA_DRAWENTIRE:
                            DrawMenuItem(lpdis, &rc);
                            break;

                        case ODA_FOCUS:
                            break;

                        case ODA_SELECT:
                        {
                            COLORREF clrTxt, clrBk, clrTxtSav, clrBkSav;

                            if (ODS_SELECTED & lpdis->itemState)
                            {
                                clrTxt = GetSysColor(COLOR_HIGHLIGHTTEXT);
                                clrBk = GetSysColor(COLOR_HIGHLIGHT);
                            }
                            else
                            {
                                clrTxt = GetSysColor(COLOR_MENUTEXT);
                                clrBk = GetSysColor(COLOR_MENU);
                            }
                            clrTxtSav = SetTextColor(hdc, clrTxt);
                            clrBkSav = SetBkColor(hdc, clrBk);
                            ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &lpdis->rcItem, TEXT(""), 0, NULL);
                            DrawMenuItem(lpdis, &rc);
                            SetTextColor(hdc, clrTxtSav);
                            SetBkColor(hdc, clrBkSav);
                            break;
                        }
                    }
                    SelectObject(hdc, hfontSav);
                    DeleteObject(hfont);
                    return TRUE;
                }
            }
            break;

        case WM_MEASUREITEM:
            if (0 == wParam)    // This is a menu
            {
                LPMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT)lParam;
                LPPUIMENUITEM lpItem = (LPPUIMENUITEM)lpmis->itemData;

                if (lpmis->CtlType == ODT_MENU
                    && !IsBadReadPtr(lpItem, sizeof(lpItem->dwSig)) && PUI_OWNERDRAW_SIG == lpItem->dwSig
                    && !(MFT_OWNERDRAW & lpItem->fType) && lpItem->lpwz)
                {
                    RECT rc;
                    HDC hdc;
                    HFONT hfont, hfontSav;
                    NONCLIENTMETRICSA ncm;
                    int    iTabPos;

                    ncm.cbSize = sizeof(ncm);
                    if (!SystemParametersInfoA(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0))
                        break;

                    hfont = CreateFontIndirectA(&ncm.lfMenuFont);
                    if (NULL == hfont)
                        break;

                    hdc = GetDC(hWnd);
                    hfontSav = (HFONT)SelectObject(hdc, hfont);

                    iTabPos = FindCharPosition(lpItem->lpwz, L'\t');
                    if (iTabPos < 0)
                        iTabPos = lstrlenW(lpItem->lpwz);

                    DrawTextFLW(hdc, lpItem->lpwz, iTabPos, &rc, DT_SINGLELINE | DT_CALCRECT | DT_EXPANDTABS);
                    SelectObject(hdc, hfontSav);
                    DeleteObject(hfont);
                    ReleaseDC(hWnd, hdc);

                    lpmis->itemWidth = rc.right - rc.left + GetSystemMetrics(SM_CXMENUCHECK) +
                                       GetSystemMetrics(SM_CXEDGE) * 2 + lpItem->iTabLength;
                    lpmis->itemHeight = rc.bottom - rc.top + GetSystemMetrics(SM_CYEDGE) * 2;
                    return TRUE;
                }
            }
            break;

        case WM_MENUCHAR:
            // FEATURE: need to be implemented
            break;
    }
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

int WINAPI
GetMenuStringWrapW(
        HMENU   hMenu,
        UINT    uIDItem,
        LPWSTR  lpString,
        int     nMaxCount,
        UINT    uFlag)
{
    VALIDATE_PROTOTYPE(GetMenuString);
    VALIDATE_OUTBUF(lpString, nMaxCount);

    static BOOL bOwnerDraw = NeedMenuOwnerDraw();

    if (bOwnerDraw)
    {
        MENUITEMINFOA miiA;

        miiA.cbSize = sizeof(miiA);
        miiA.fMask = MIIM_TYPE | MIIM_ID | MIIM_DATA;
        miiA.cch = 0;
        if (GetMenuItemInfoA(hMenu, uIDItem, (uFlag & MF_BYPOSITION)? TRUE: FALSE, &miiA))
        {
            if ((MIIM_TYPE & miiA.fMask) && (MFT_OWNERDRAW & miiA.fType))
            {
                LPPUIMENUITEM lpItem = (LPPUIMENUITEM)miiA.dwItemData;

                if (!IsBadReadPtr(lpItem, sizeof(lpItem->dwSig)) && PUI_OWNERDRAW_SIG == lpItem->dwSig && !(MFT_OWNERDRAW & lpItem->fType))
                {
                    if (lpItem->lpwz && lpItem->cch)
                    {
                        StrCpyNW(lpString, lpItem->lpwz, nMaxCount);
                        return min((UINT)(nMaxCount - 1), lpItem->cch);
                    }
                }
            }
        }
    }

    if (g_bRunningOnNT)
    {
        return GetMenuStringW(hMenu, uIDItem, lpString, nMaxCount, uFlag);
    }

    CStrOut str(lpString, nMaxCount);

    GetMenuStringA(hMenu, uIDItem, str, str.BufSize(), uFlag);
    return str.ConvertExcludingNul();
}

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

BOOL WINAPI
InsertMenuWrapW(
        HMENU   hMenu,
        UINT    uPosition,
        UINT    uFlags,
        UINT_PTR uIDNewItem,
        LPCWSTR lpNewItem)
{
    VALIDATE_PROTOTYPE(InsertMenu);

    static BOOL bOwnerDraw = NeedMenuOwnerDraw();

    if (bOwnerDraw)
    {
        if (!((MF_BITMAP | MF_OWNERDRAW) & uFlags)) // if MF_STRING is set. MF_STRING is 0
        {
            MENUITEMINFOW miiW;

            miiW.cbSize = sizeof(miiW);
            miiW.fMask = MIIM_TYPE | MIIM_STATE;
            miiW.fType = MFT_STRING;
            miiW.fState = uFlags & (MF_DEFAULT | MF_CHECKED | MF_UNCHECKED | MF_HILITE | MF_UNHILITE| MF_ENABLED | MF_DISABLED | MF_GRAYED);
            if (uFlags & (MF_GRAYED | MF_DISABLED))
                miiW.fState |= MFS_GRAYED;
            if (MF_POPUP & uFlags)
            {
                miiW.fMask |= MIIM_SUBMENU;
                miiW.hSubMenu = (HMENU)uIDNewItem;
            }
            else
            {
                miiW.fMask |= MIIM_ID;
                miiW.wID = (UINT)uIDNewItem;
            }
            miiW.dwTypeData = (LPWSTR)lpNewItem;
            miiW.cch = lstrlenW(lpNewItem);
            return InsertMenuItemWrapW(hMenu, uPosition, (MF_BYPOSITION & uFlags)? TRUE: FALSE, &miiW);
        }
    }

    if (g_bRunningOnNT)
    {
        return InsertMenuW(hMenu, uPosition, uFlags, uIDNewItem, lpNewItem);
    }

    //
    //  You can't test for MFT_STRING because MFT_STRING is zero!
    //  So instead you have to check for everything *other* than
    //  a string.
    //
    //  The presence of any non-string menu type turns lpnewItem into
    //  an atom.
    //
    CStrIn str((uFlags & MFT_NONSTRING) ? CP_ATOM : CP_ACP, lpNewItem);

    return InsertMenuA(hMenu, uPosition, uFlags, uIDNewItem, str);
}

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

BOOL IsWindowOnCurrentThread(HWND hWnd)
{
    DWORD foo;

    if (!IsWindow(hWnd))
        // bail if the window is dead so we dont bogusly rip
        return TRUE;

    if (GetCurrentThreadId() != GetWindowThreadProcessId(hWnd, &foo))
        return FALSE;
    else
        return TRUE;
}

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

BOOL WINAPI
TrackPopupMenuWrap(HMENU hMenu, UINT uFlags, int x, int y, int nReserved, HWND hWnd, CONST RECT *prcRect)
{
    VALIDATE_PROTOTYPEX(TrackPopupMenu);

    static BOOL bOwnerDraw = NeedMenuOwnerDraw();
    BOOL        bIsWindowOnCurrentThread, bRet;

    if (bOwnerDraw && (bIsWindowOnCurrentThread = IsWindowOnCurrentThread(hWnd)))
    {
        CalculateMenuTabString(hWnd, hMenu);
        SetWindowSubclass(hWnd, OwnerDrawSubclassProc, 0, (DWORD_PTR)NULL);
    }

    bRet = TrackPopupMenu(hMenu, uFlags, x, y, nReserved, hWnd, prcRect);

    if (bOwnerDraw && bIsWindowOnCurrentThread)
        RemoveWindowSubclass(hWnd, OwnerDrawSubclassProc,  0);

    return bRet;
}

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

BOOL WINAPI
TrackPopupMenuExWrap(HMENU hMenu, UINT uFlags, int x, int y, HWND hWnd, LPTPMPARAMS lptpm)
{
    VALIDATE_PROTOTYPEX(TrackPopupMenuEx);

    static BOOL bOwnerDraw = NeedMenuOwnerDraw();
    BOOL        bIsWindowOnCurrentThread, bRet;

    if (bOwnerDraw && (bIsWindowOnCurrentThread = IsWindowOnCurrentThread(hWnd)))
    {
        CalculateMenuTabString(hWnd, hMenu);
        SetWindowSubclass(hWnd, OwnerDrawSubclassProc, 0, (DWORD_PTR)NULL);
    }

    bRet = TrackPopupMenuEx(hMenu, uFlags, x, y, hWnd, lptpm);
    
    if (bOwnerDraw && bIsWindowOnCurrentThread)
        RemoveWindowSubclass(hWnd, OwnerDrawSubclassProc,  0);

    return bRet;
}

#ifdef NEED_USER32_WRAPPER

int WINAPI
LoadStringWrapW(HINSTANCE hInstance, UINT uID, LPWSTR lpBuffer, int nBufferMax)
{
    VALIDATE_PROTOTYPE(LoadString);

    if (g_bRunningOnNT)
    {
        return LoadStringW(hInstance, uID, lpBuffer, nBufferMax);
    }

    //
    //  Do it manually.  The old code used to call LoadStringA and then
    //  convert it up to unicode, which is bad, since resources are
    //  physically already Unicode!  Just copy it out directly.
    //
    //  The old code was also buggy in the case where the loaded string
    //  contains embedded NULLs.
    //

    if (nBufferMax <= 0) return 0;                  // sanity check

    PWCHAR pwch;

    /*
     *  String tables are broken up into "bundles" of 16 strings each.
     */
    HRSRC hrsrc;
    int cwch = 0;

    hrsrc = FindResourceA(hInstance, (LPSTR)(LONG_PTR)(1 + uID / 16), (LPSTR)RT_STRING);
    if (hrsrc) {
        pwch = (PWCHAR)LoadResource(hInstance, hrsrc);
        if (pwch) {
            /*
             *  Now skip over the strings in the resource until we
             *  hit the one we want.  Each entry is a counted string,
             *  just like Pascal.
             */
            for (uID %= 16; uID; uID--) {
                pwch += *pwch + 1;
            }
            cwch = min(*pwch, nBufferMax - 1);
            memcpy(lpBuffer, pwch+1, cwch * sizeof(WCHAR)); /* Copy the goo */
        }
    }
    lpBuffer[cwch] = L'\0';                 /* Terminate the string */
    return cwch;
}

#endif // NEED_USER32_WRAPPER

//----------------------------------------------------------------------
//
// function:    TransformCharNoOp1( WCHAR **ppch )
//
// purpose:     Stand-in for TransformCharWidth.  Used by the function
//              CompareStringString.
//
// returns:     Character at *ppch.  The value *ppch is incremented.
//
//----------------------------------------------------------------------

static WCHAR
TransformCharNoOp1( LPCWSTR *ppch, int )
{
    WCHAR ch = **ppch;

    (*ppch)++;

    return ch;
}

//----------------------------------------------------------------------
//
// function:    TransformCharWidth( WCHAR **ppch, cchRemaining )
//
// purpose:     Converts halfwidth characters to fullwidth characters.
//              Also combines voiced (dakuon) and semi-voiced (handakuon)
//              characters.  *pch is advanced by one, unless there is a
//              (semi)voiced character, in which case it is advanced by
//              two characters.
//
//              Note that unlike the full widechar version, we do not
//              combine other characters, notably the combining Hiragana
//              characters (U+3099 and U+309A.)  This is to keep the
//              tables from getting unnecessarily large.
//
//              cchRemaining is passed so as to not include the voiced
//              marks if it's passed the end of the specified buffer.
//
// returns:     Full width character. *pch is incremented.
//
//----------------------------------------------------------------------

static WCHAR
TransformCharWidth( LPCWSTR *ppch, int cchRemaining )
{
    WCHAR ch = **ppch;

    (*ppch)++;

    if (ch == 0x0020)
    {
        ch = 0x3000;
    }
    else if (ch == 0x005c)
    {
        // REVERSE SOLIDUS (aka BACKSLASH) maps to itself
    }
    else if (InRange(ch, 0x0021, 0x07e))
    {
        ch += 65248;
    }
    else if (InRange(ch, 0x00a2, 0x00af))
    {
        static const WCHAR achFull[] =
        {
            0xffe0, 0xffe1, 0x00a4, 0xffe5, 0xffe4, 0x00a7, 0x00a8, // 0xa2-0xa8
            0x00a9, 0x00aa, 0x00ab, 0xffe2, 0x00ad, 0x00ae, 0xffe3  // 0xa9-0xaf
        };

        ch = achFull[ch - 0x00a2];
    }
    else if (ch == 0x20a9) // WON SIGN
    {
        ch = 0xffe6;
    }
    else if (InRange(ch, 0xff61, 0xffdc))
    {
        WCHAR chNext = (cchRemaining > 1) ? **ppch : 0;

        if (chNext == 0xff9e && InRange(ch, 0xff73, 0xff8e))
        {
            if (cchRemaining != 1)
            {
                static const WCHAR achFull[] =
                {
/* 0xff73-0xff79 */  0xb0f4, 0x30a8, 0x30aa, 0xb0ac, 0xb0ae, 0xb0b0, 0xb0b2,
/* 0xff7a-0xff80 */  0xb0b4, 0xb0b6, 0xb0b8, 0xb0ba, 0xb0bc, 0xb0be, 0xb0c0,
/* 0xff81-0xff87 */  0xb0c2, 0xb0c5, 0xb0c7, 0xb0c9, 0x30ca, 0x30cb, 0x30cc,
/* 0xff88-0xff8e */  0x30cd, 0x30ce, 0xb0d0, 0xb0d3, 0xb0d6, 0xb0d9, 0xb0dc
                };

                // HALFWIDTH KATAKANA VOICED SOUND MARK

                WCHAR chTemp = achFull[ch - 0xff73];

                // Some in the range absorb the sound mark.
                // These are indicated by the set high-bit.

                ch = chTemp & 0x7fff;

                if (chTemp & 0x8000)
                {
                    (*ppch)++;
                }
            }
        }
        else if (chNext == 0xff9f && InRange(ch, 0xff8a, 0xff8e))
        {
            // HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK

            ch = 0x30d1 + (ch - 0xff8a) * 3;
            (*ppch)++;
        }
        else
        {
            static const WCHAR achMapFullFFxx[] =
            {
                0x3002, 0x300c, 0x300d, 0x3001, 0x30fb, 0x30f2, 0x30a1,  // 0xff61-0xff67
                0x30a3, 0x30a5, 0x30a7, 0x30a9, 0x30e3, 0x30e5, 0x30e7,  // 0xff68-0xff6e
                0x30c3, 0x30fc, 0x30a2, 0x30a4, 0x30a6, 0x30a8, 0x30aa,  // 0xff6f-0xff75
                0x30ab, 0x30ad, 0x30af, 0x30b1, 0x30b3, 0x30b5, 0x30b7,  // 0xff76-0xff7c
                0x30b9, 0x30bb, 0x30bd, 0x30bf, 0x30c1, 0x30c4, 0x30c6,  // 0xff7d-0xff83
                0x30c8, 0x30ca, 0x30cb, 0x30cc, 0x30cd, 0x30ce, 0x30cf,  // 0xff84-0xff8a
                0x30d2, 0x30d5, 0x30d8, 0x30db, 0x30de, 0x30df, 0x30e0,  // 0xff8b-0xff91
                0x30e1, 0x30e2, 0x30e4, 0x30e6, 0x30e8, 0x30e9, 0x30ea,  // 0xff92-0xff98
                0x30eb, 0x30ec, 0x30ed, 0x30ef, 0x30f3, 0x309b, 0x309c,  // 0xff99-0xff9f
                0x3164, 0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136,  // 0xffa0-0xffa6
                0x3137, 0x3138, 0x3139, 0x313a, 0x313b, 0x313c, 0x313d,  // 0xffa7-0xffad
                0x313e, 0x313f, 0x3140, 0x3141, 0x3142, 0x3143, 0x3144,  // 0xffae-0xffb4
                0x3145, 0x3146, 0x3147, 0x3148, 0x3149, 0x314a, 0x314b,  // 0xffb5-0xffbb
                0x314c, 0x314d, 0x314e, 0xffbf, 0xffc0, 0xffc1, 0x314f,  // 0xffbc-0xffc2
                0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0xffc8, 0xffc9,  // 0xffc3-0xffc9
                0x3155, 0x3156, 0x3157, 0x3158, 0x3159, 0x315a, 0xffd0,  // 0xffca-0xffd0
                0xffd1, 0x315b, 0x315c, 0x315d, 0x315e, 0x315f, 0x3160,  // 0xffd1-0xffd7
                0xffd8, 0xffd9, 0x3161, 0x3162, 0x3163                   // 0xffd8-0xffac
            };

            ch = achMapFullFFxx[ch - 0xff61];
        }
    }

    return ch;
}

//----------------------------------------------------------------------
//
// function:    TransformaCharNoOp2( WCHAR ch )
//
// purpose:     Stand-in for CharLowerBuffWrap.  Used by the function
//              CompareStringString.
//
// returns:     Original character
//
//----------------------------------------------------------------------

static WCHAR
TransformCharNoOp2( WCHAR ch )
{
    return ch;
}

//----------------------------------------------------------------------
//
// function:    TransformaCharKana( WCHAR ch )
//
// purpose:     Converts Hiragana characters to Katakana characters
//
// returns:     Original character if not Hiragana,
//              Katanaka character if Hiragana
//
//----------------------------------------------------------------------

static WCHAR
TransformCharKana( WCHAR ch )
{
    if (((ch & 0xff00) == 0x3000) &&
        (InRange(ch, 0x3041, 0x3094) || InRange(ch, 0x309d, 0x309e)))
    {
        ch += 0x060;
    }

    return ch;
}

//----------------------------------------------------------------------
//
// function:    TransformCharNoOp3( LPWSTR pch, DWORD cch )
//
// purpose:     Stand-in for CharLowerBuffWrap.  Used by the function
//              CompareStringString.
//
// returns:     Character count (cch).
//
//----------------------------------------------------------------------

static DWORD
TransformCharNoOp3( LPWSTR, DWORD cch )
{
    return cch;
}

//----------------------------------------------------------------------
//
// function:    TransformaCharFinal( WCHAR ch )
//
// purpose:     Converts "final" forms to regular forms
//
// returns:     Original character if not Hiragana,
//              Katanaka character if Hiragana
//
//----------------------------------------------------------------------

// REARCHITECT (cthrash) We do not fold Presentation Forms (Alphabetic or Arabic)

static WCHAR
TransformCharFinal( WCHAR ch )
{
    WCHAR chRet = ch;

    if (ch >= 0x3c2)                    // short-circuit ASCII +
    {
        switch (ch)
        {
            case 0x03c2:                // GREEK SMALL LETTER FINAL SIGMA
            case 0x05da:                // HEBREW LETTER FINAL KAF
            case 0x05dd:                // HEBREW LETTER FINAL MEM
            case 0x05df:                // HEBREW LETTER FINAL NUN
            case 0x05e3:                // HEBREW LETTER FINAL PE
            case 0x05e5:                // HEBREW LETTER FINAL TSADI
            case 0xfb26:                // HEBREW LETTER WIDE FINAL MEM
            case 0xfb3a:                // HEBREW LETTER FINAL KAF WITH DAGESH
            case 0xfb43:                // HEBREW LETTER FINAL PE WITH DAGESH
                chRet++;
                break;
        }
    }

    return ch;
}

//----------------------------------------------------------------------
//
// function:    CompareStringString( ... )
//
// purpose:     Helper for CompareStringWrap.
//
//              We handle the string comparsion for CompareStringWrap.
//              We can convert each character to (1) fullwidth,
//              (2) Katakana, and (3) lowercase, as necessary.
//
// returns:     1 - string A is less in lexical value as string B
//              2 - string B is equal in lexical value as string B
//              3 - string B is greater in lexical value as string B
//
//----------------------------------------------------------------------

static int
CompareStringString(
    DWORD   dwFlags,
    LPCWSTR lpA,
    int     cchA,
    LPCWSTR lpB,
    int     cchB )
{
    int nRet = 0;
    WCHAR wchIgnoreNulA = cchA == -1 ? 0 : -1;
    WCHAR wchIgnoreNulB = cchB == -1 ? 0 : -1;
    WCHAR (*pfnTransformWidth)(LPCWSTR *, int);
    WCHAR (*pfnTransformKana)(WCHAR);
    DWORD (*pfnTransformLower)(LPWSTR, DWORD);
    WCHAR (*pfnTransformFinal)(WCHAR);


    pfnTransformWidth = (dwFlags & NORM_IGNOREWIDTH)
                        ? TransformCharWidth : TransformCharNoOp1;
    pfnTransformKana  = (dwFlags & NORM_IGNOREKANATYPE)
                        ? TransformCharKana : TransformCharNoOp2;
    pfnTransformLower = (dwFlags & NORM_IGNORECASE)
                        ? CharLowerBuffWrap : TransformCharNoOp3;
    pfnTransformFinal = (dwFlags & NORM_IGNORECASE)
                        ? TransformCharFinal : TransformCharNoOp2;

    while (   !nRet
           && cchA
           && cchB
           && (*lpA | wchIgnoreNulA)
           && (*lpB | wchIgnoreNulB) )
    {
        WCHAR chA, chB;
        LPCWSTR lpAOld = lpA;
        LPCWSTR lpBOld = lpB;

        chA = (*pfnTransformWidth)(&lpA, cchA);
        chA = (*pfnTransformKana)(chA);
        (*pfnTransformLower)(&chA, 1);
        chA = (*pfnTransformFinal)(chA);

        chB = (*pfnTransformWidth)(&lpB, cchB);
        chB = (*pfnTransformKana)(chB);
        (*pfnTransformLower)(&chB, 1);
        chB = (*pfnTransformFinal)(chB);

        nRet = (int)chA - (int)chB;
        cchA -= (int) (lpA - lpAOld);
        cchB -= (int) (lpB - lpBOld);
    }

    if (!nRet)
    {
        nRet = cchA - cchB;
    }

    if (nRet)
    {
        nRet = nRet > 0 ? 1 : -1;
    }

    return nRet + 2;
}

//----------------------------------------------------------------------
//
// function:    CompareStringWord( ... )
//
// purpose:     Helper for CompareStringWrap.
//
//              We handle the word comparsion for CompareStringWrap.
//
// returns:     1 - string A is less in lexical value as string B
//              2 - string B is equal in lexical value as string B
//              3 - string B is greater in lexical value as string B
//
//----------------------------------------------------------------------

static int
CompareStringWord(
    LCID    lcid,
    DWORD   dwFlags,
    LPCWSTR lpA,
    int     cchA,
    LPCWSTR lpB,
    int     cchB )
{
    // FEATURE (cthrash) We won't properly support word compare for the
    // time being.  Do the same old CP_ACP trick, which should cover
    // enough cases.

    // fail if either string is NULL, as it causes assert on debug windows
    if (!lpA || !lpB)
        return 0;

    CStrIn strA(lpA, cchA);
    CStrIn strB(lpB, cchB);

    cchA = strA.strlen();
    cchB = strB.strlen();

    return CompareStringA(lcid, dwFlags, strA, cchA, strB, cchB);
}

//----------------------------------------------------------------------
//
// function:    CompareStringWrapW( ... )
//
// purpose:     Unicode wrapper of CompareString for Win95.
//
//              Note not all bits in dwFlags are honored; specifically,
//              since we don't do a true widechar word compare, we
//              won't properly handle NORM_IGNORENONSPACE or
//              NORM_IGNORESYMBOLS for arbitrary widechar strings.
//
// returns:     1 - string A is less in lexical value as string B
//              2 - string B is equal in lexical value as string B
//              3 - string B is greater in lexical value as string B
//
//----------------------------------------------------------------------

LWSTDAPI_(int)
CompareStringAltW(
    LCID    lcid,
    DWORD   dwFlags,
    LPCWSTR lpA,
    int     cchA,
    LPCWSTR lpB,
    int     cchB )
{
    if (g_bRunningOnNT)
    {
        return CompareStringW(lcid, dwFlags, lpA, cchA, lpB, cchB);
    }

    int nRet;

    if (dwFlags & SORT_STRINGSORT)
    {
        nRet = CompareStringString(dwFlags, lpA, cchA, lpB, cchB);
    }
    else
    {
        nRet = CompareStringWord(lcid, dwFlags, lpA, cchA, lpB, cchB);
    }

    return nRet;
}

#ifdef NEED_KERNEL32_WRAPPER

int StrLenN(LPCWSTR psz, int cchMin)
{
    LPCWSTR pszEnd = psz;

    while (((pszEnd - psz) < cchMin) && *pszEnd)
    {
        pszEnd++;
    }
    return pszEnd - psz;
}

int WINAPI CompareStringWrapW(LCID Locale, DWORD dwFlags,
                              LPCWSTR psz1, int cch1,
                              LPCWSTR psz2, int cch2)
{
    VALIDATE_PROTOTYPE(CompareString);

    if (g_bRunningOnNT)
    {
        return CompareStringW(Locale, dwFlags, psz1, cch1, psz2, cch2);
    }
    else if (psz1 && psz2)
    {
        if (dwFlags & NORM_STOP_ON_NULL)
        {
            cch1 = StrLenN(psz1, cch1);
            cch2 = StrLenN(psz2, cch2);
            dwFlags &= ~NORM_STOP_ON_NULL;
        }

        CStrIn strString1(psz1, cch1);
        CStrIn strString2(psz2, cch2);

        cch1 = strString1.strlen();
        cch2 = strString2.strlen();

        return CompareStringA(Locale, dwFlags, strString1, cch1, strString2, cch2);
    }
    else
        return 0;   // fail if either string is NULL, as it causes assert on debug windows
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

#ifndef UNIX
BOOL WINAPI
MessageBoxIndirectWrapW(CONST MSGBOXPARAMS *pmbp)
#else
int WINAPI
MessageBoxIndirectWrapW(LPMSGBOXPARAMS pmbp)
#endif /* UNIX */
{
    VALIDATE_PROTOTYPE(MessageBoxIndirect);
    ASSERT(HIWORD64(pmbp->lpszIcon) == 0);

    if (g_bRunningOnNT)
    {
        return MessageBoxIndirectW(pmbp);
    }

    CStrIn        strText(pmbp->lpszText);
    CStrIn        strCaption(pmbp->lpszCaption);
    MSGBOXPARAMSA mbp;

    memcpy(&mbp, pmbp, sizeof(mbp));
    mbp.lpszText = strText;
    mbp.lpszCaption = strCaption;

    return MessageBoxIndirectA(&mbp);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

DWORD GetCharacterPlacementWrapW(
    HDC hdc,            // handle to device context
    LPCTSTR lpString,   // pointer to string
    int nCount,         // number of characters in string
    int nMaxExtent,     // maximum extent for displayed string
    LPGCP_RESULTS lpResults, // pointer to buffer for placement result
    DWORD dwFlags       // placement flags
   )
{
    VALIDATE_PROTOTYPE(GetCharacterPlacement);
    // Leave for someone else.
    ASSERT (lpResults->lpOutString == NULL);
    ASSERT (lpResults->lpClass == NULL);

    if (g_bRunningOnNT)
    {
        return GetCharacterPlacementW (hdc,
                                       lpString,
                                       nCount,
                                       nMaxExtent,
                                       lpResults,
                                       dwFlags);
    }

    CStrIn strText(lpString);
    DWORD dwRet;

    dwRet = GetCharacterPlacementA (hdc, strText, nCount, nMaxExtent,
                                    (LPGCP_RESULTSA)lpResults,
                                    dwFlags);
    return dwRet;
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

//
// Note that we're calling get GetCharWidthA instead of GetCharWidth32A
// because the 32 version doesn't exist under Win95.
BOOL WINAPI GetCharWidth32WrapW(
     HDC hdc,
     UINT iFirstChar,
     UINT iLastChar,
     LPINT lpBuffer)
{
    VALIDATE_PROTOTYPE(GetCharWidth32);

    if (g_bRunningOnNT)
    {
         return GetCharWidth32W (hdc, iFirstChar, iLastChar, lpBuffer);
    }

    // Note that we expect to do only one character at a time for anything but
    // ISO Latin 1.
    if (iFirstChar > 255)
    {
        UINT mbChar=0;
        WCHAR ch;

        // Convert string
        ch = (WCHAR)iFirstChar;
        WideCharToMultiByte(CP_ACP, 0, &ch, 1,
                            (char *)&mbChar, 2, NULL, NULL);
    }

    return (GetCharWidthA (hdc, iFirstChar, iLastChar, lpBuffer));
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

//
//  Note:  Win95 does support ExtTextOutW.  This thunk is not for
//  ANSI/UNICODE wrapping.  It's to work around an ISV app bug.
//
//  Y'see, there's an app that patches Win95 GDI and their ExtTextOutW handler
//  is broken.  It always dereferences the lpStr parameter, even if
//  cch is zero.  Consequently, any time we are about to pass NULL as
//  the lpStr, we have to change our mind and pass a null UNICODE string
//  instead.
//
//  The name of this app:  Lotus SmartSuite ScreenCam 97.
//
LWSTDAPI_(BOOL)
ExtTextOutWrapW(HDC hdc, int x, int y, UINT fuOptions, CONST RECT *lprc, LPCWSTR lpStr, UINT cch, CONST INT *lpDx)
{
    VALIDATE_PROTOTYPE(ExtTextOut);
    if (lpStr == NULL)              // workaround
        lpStr = L"";                // for ScreenCam 97

    if (_MayNeedFontLinking(lpStr, cch))
    {
        return ExtTextOutFLW(hdc, x, y, fuOptions, lprc, lpStr, cch, lpDx);
    }

    return ExtTextOutW(hdc, x, y, fuOptions, lprc, lpStr, cch, lpDx);
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL WINAPI
ModifyMenuWrapW(
        HMENU   hMenu,
        UINT    uPosition,
        UINT    uFlags,
        UINT_PTR uIDNewItem,
        LPCWSTR lpNewItem)
{
    VALIDATE_PROTOTYPE(ModifyMenu);
    ASSERT(!(uFlags & MF_BITMAP) && !(uFlags & MF_OWNERDRAW));

    if (g_bRunningOnNT)
    {
        return ModifyMenuW(hMenu, uPosition, uFlags, uIDNewItem, lpNewItem);
    }

    CStrIn  str(lpNewItem);

    return ModifyMenuA(hMenu, uPosition, uFlags, uIDNewItem, str);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI
CopyFileWrapW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, BOOL bFailIfExists)
{
    VALIDATE_PROTOTYPE(CopyFile);

    if (g_bRunningOnNT)
    {
        return CopyFileW(lpExistingFileName, lpNewFileName, bFailIfExists);
    }

    CStrIn  strOld(lpExistingFileName);
    CStrIn  strNew(lpNewFileName);

    return CopyFileA(strOld, strNew, bFailIfExists);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI
MoveFileWrapW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName)
{
    VALIDATE_PROTOTYPE(MoveFile);

    if (g_bRunningOnNT)
    {
        return MoveFileW(lpExistingFileName, lpNewFileName);
    }

    CStrIn  strOld(lpExistingFileName);
    CStrIn  strNew(lpNewFileName);

    return MoveFileA(strOld, strNew);
}


#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL WINAPI
OemToCharWrapW(LPCSTR lpszSrc, LPWSTR lpszDst)
{
    VALIDATE_PROTOTYPE(OemToChar);
    VALIDATE_OUTBUF(lpszDst, lstrlenA(lpszSrc));

    if (g_bRunningOnNT)
    {
        return OemToCharW(lpszSrc, lpszDst);
    }

    CStrOut strDst(lpszDst, lstrlenA(lpszSrc));

    return OemToCharA(lpszSrc, strDst);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

HANDLE WINAPI
OpenEventWrapW(
        DWORD                   fdwAccess,
        BOOL                    fInherit,
        LPCWSTR                 lpszEventName)
{
    VALIDATE_PROTOTYPE(OpenEvent);

    if (g_bRunningOnNT)
    {
        return OpenEventW(fdwAccess, fInherit, lpszEventName);
    }

    CStrIn strEventName(lpszEventName);

    return OpenEventA(fdwAccess, fInherit, strEventName);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

VOID WINAPI
OutputDebugStringWrapW(LPCWSTR lpOutputString)
{
    VALIDATE_PROTOTYPE(OutputDebugString);

    if (g_bRunningOnNT)
    {
        OutputDebugStringW(lpOutputString);
        return;
    }

    CStrIn  str(lpOutputString);

    OutputDebugStringA(str);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL FORWARD_API WINAPI
PeekMessageWrapW(
        LPMSG   lpMsg,
        HWND    hWnd,
        UINT    wMsgFilterMin,
        UINT    wMsgFilterMax,
        UINT    wRemoveMsg)
{
    VALIDATE_PROTOTYPE(PeekMessage);

    FORWARD_AW(PeekMessage, (lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_WINMM_WRAPPER

LWSTDAPI_(BOOL)
PlaySoundWrapW(
        LPCWSTR pszSound,
        HMODULE hmod,
        DWORD fdwSound)
{
    if (g_bRunningOnNT)
    {
        return PlaySoundW(pszSound, hmod, fdwSound);
    }

    CStrIn strSound(pszSound);

    return PlaySoundA(strSound, hmod, fdwSound);
}

#endif // NEED_WINMM_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL FORWARD_API WINAPI
PostMessageWrapW(
        HWND    hWnd,
        UINT    Msg,
        WPARAM  wParam,
        LPARAM  lParam)
{
    VALIDATE_PROTOTYPE(PostMessage);

    FORWARD_AW(PostMessage, (hWnd, Msg, wParam, lParam));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL FORWARD_API WINAPI
PostThreadMessageWrapW(
        DWORD idThread,
        UINT Msg,
        WPARAM wParam,
        LPARAM lParam)
{
    VALIDATE_PROTOTYPE(PostThreadMessage);

    FORWARD_AW(PostThreadMessage, (idThread, Msg, wParam, lParam));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

LONG APIENTRY
RegCreateKeyWrapW(HKEY hKey, LPCWSTR lpSubKey, PHKEY phkResult)
{
    VALIDATE_PROTOTYPE(RegCreateKey);

    if (g_bRunningOnNT)
    {
        return RegCreateKeyW(hKey, lpSubKey, phkResult);
    }

    CStrIn  str(lpSubKey);

    return RegCreateKeyA(hKey, str, phkResult);
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

LONG APIENTRY
RegCreateKeyExWrapW(HKEY hKey, LPCTSTR lpSubKey, DWORD Reserved, LPTSTR lpClass, DWORD dwOptions, REGSAM samDesired, LPSECURITY_ATTRIBUTES lpSecurityAttributes, PHKEY phkResult, LPDWORD lpdwDisposition)
{
    VALIDATE_PROTOTYPE(RegCreateKeyEx);

    if (g_bRunningOnNT)
    {
        return RegCreateKeyExW(hKey, lpSubKey, Reserved, lpClass, dwOptions, samDesired, lpSecurityAttributes,  phkResult, lpdwDisposition);
    }

    CStrIn strSubKey(lpSubKey);
    CStrIn strClass(lpClass);

    return RegCreateKeyExA(hKey, strSubKey, Reserved, strClass, dwOptions, samDesired, lpSecurityAttributes,  phkResult, lpdwDisposition);
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

//
//  Subtle difference:  RegDeleteKey on Win9x will recursively delete subkeys.
//  On NT, it fails if the key being deleted has subkeys.  If you need to
//  force NT-style behavior, use SHDeleteEmptyKey.  To force 95-style
//  behavior, use SHDeleteKey.
//
LONG APIENTRY
RegDeleteKeyWrapW(HKEY hKey, LPCWSTR pwszSubKey)
{
    VALIDATE_PROTOTYPE(RegDeleteKey);

    if (g_bRunningOnNT)
    {
        return RegDeleteKeyW(hKey, pwszSubKey);
    }

    CStrIn  str(pwszSubKey);

    return RegDeleteKeyA(hKey, str);
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

LONG APIENTRY
RegDeleteValueWrapW(HKEY hKey, LPCWSTR pwszSubKey)
{
    VALIDATE_PROTOTYPE(RegDeleteValue);

    if (g_bRunningOnNT)
    {
        return RegDeleteValueW(hKey, pwszSubKey);
    }

    CStrIn  str(pwszSubKey);

    return RegDeleteValueA(hKey, str);
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

LONG APIENTRY
RegEnumKeyWrapW(
        HKEY    hKey,
        DWORD   dwIndex,
        LPWSTR  lpName,
        DWORD   cbName)
{
    VALIDATE_PROTOTYPE(RegEnumKey);
    VALIDATE_OUTBUF(lpName, cbName);

    if (g_bRunningOnNT)
    {
        return RegEnumKeyW(hKey, dwIndex, lpName, cbName);
    }

    CStrOut str(lpName, cbName);

    return RegEnumKeyA(hKey, dwIndex, str, str.BufSize());
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

LONG APIENTRY
RegEnumKeyExWrapW(
        HKEY        hKey,
        DWORD       dwIndex,
        LPWSTR      lpName,
        LPDWORD     lpcbName,
        LPDWORD     lpReserved,
        LPWSTR      lpClass,
        LPDWORD     lpcbClass,
        PFILETIME   lpftLastWriteTime)
{
    VALIDATE_PROTOTYPE(RegEnumKeyEx);
    if (lpcbName) {VALIDATE_OUTBUF(lpName, *lpcbName);}
    if (lpcbClass) {VALIDATE_OUTBUF(lpClass, *lpcbClass);}

    if (g_bRunningOnNT)
    {
        return RegEnumKeyExW(
            hKey,
            dwIndex,
            lpName,
            lpcbName,
            lpReserved,
            lpClass,
            lpcbClass,
            lpftLastWriteTime);
    }

    long    ret;
    DWORD   dwClass = 0;

    if (!lpcbClass)
    {
        lpcbClass = &dwClass;
    }

    CStrOut strName(lpName, *lpcbName);
    CStrOut strClass(lpClass, *lpcbClass);

    ret = RegEnumKeyExA(
            hKey,
            dwIndex,
            strName,
            lpcbName,
            lpReserved,
            strClass,
            lpcbClass,
            lpftLastWriteTime);

    *lpcbName = strName.ConvertExcludingNul();
    *lpcbClass = strClass.ConvertExcludingNul();

    return ret;
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

LONG APIENTRY
RegOpenKeyWrapW(HKEY hKey, LPCWSTR pwszSubKey, PHKEY phkResult)
{
    VALIDATE_PROTOTYPE(RegOpenKey);

    if (g_bRunningOnNT)
    {
        return RegOpenKeyW(hKey, pwszSubKey, phkResult);
    }

    CStrIn  str(pwszSubKey);

    return RegOpenKeyA(hKey, str, phkResult);
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

LONG APIENTRY
RegOpenKeyExWrapW(
        HKEY    hKey,
        LPCWSTR lpSubKey,
        DWORD   ulOptions,
        REGSAM  samDesired,
        PHKEY   phkResult)
{
    VALIDATE_PROTOTYPE(RegOpenKeyEx);

    if (g_bRunningOnNT)
    {
        return RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult);
    }

    CStrIn  str(lpSubKey);

    return RegOpenKeyExA(hKey, str, ulOptions, samDesired, phkResult);
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

LONG APIENTRY
RegQueryInfoKeyWrapW(
        HKEY hKey,
        LPWSTR lpClass,
        LPDWORD lpcbClass,
        LPDWORD lpReserved,
        LPDWORD lpcSubKeys,
        LPDWORD lpcbMaxSubKeyLen,
        LPDWORD lpcbMaxClassLen,
        LPDWORD lpcValues,
        LPDWORD lpcbMaxValueNameLen,
        LPDWORD lpcbMaxValueLen,
        LPDWORD lpcbSecurityDescriptor,
        PFILETIME lpftLastWriteTime)
{
    VALIDATE_PROTOTYPE(RegQueryInfoKey);

    if (g_bRunningOnNT)
    {
            return RegQueryInfoKeyW(
                hKey,
                lpClass,
                lpcbClass,
                lpReserved,
                lpcSubKeys,
                lpcbMaxSubKeyLen,
                lpcbMaxClassLen,
                lpcValues,
                lpcbMaxValueNameLen,
                lpcbMaxValueLen,
                lpcbSecurityDescriptor,
                lpftLastWriteTime);

    }

    CStrIn  str(lpClass);

    return RegQueryInfoKeyA(
                hKey,
                str,
                lpcbClass,
                lpReserved,
                lpcSubKeys,
                lpcbMaxSubKeyLen,
                lpcbMaxClassLen,
                lpcValues,
                lpcbMaxValueNameLen,
                lpcbMaxValueLen,
                lpcbSecurityDescriptor,
                lpftLastWriteTime);
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

LONG APIENTRY
RegQueryValueWrapW(
        HKEY    hKey,
        LPCWSTR pwszSubKey,
        LPWSTR  pwszValue,
        PLONG   lpcbValue)
{
    VALIDATE_PROTOTYPE(RegQueryValue);
    if (lpcbValue) {VALIDATE_OUTBUF(pwszValue, *lpcbValue);}

    if (g_bRunningOnNT)
    {
        return RegQueryValueW(hKey, pwszSubKey, pwszValue, lpcbValue);
    }

    long    ret;
    long    cb;
    CStrIn  strKey(pwszSubKey);
    CStrOut strValue(pwszValue, (lpcbValue ? ((*lpcbValue) / sizeof(WCHAR)) : 0));

    cb = strValue.BufSize();
    ret = RegQueryValueA(hKey, strKey, strValue, (lpcbValue ? &cb : NULL));
    if (ret != ERROR_SUCCESS)
        goto Cleanup;

    if (strValue)
    {
        cb = strValue.ConvertIncludingNul();
    }

    if (lpcbValue)
        *lpcbValue = cb * sizeof(WCHAR);

Cleanup:
    return ret;
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

LONG APIENTRY
RegQueryValueExWrapW(
        HKEY    hKey,
        LPCWSTR lpValueName,
        LPDWORD lpReserved,
        LPDWORD lpType,
        LPBYTE  lpData,
        LPDWORD lpcbData)
{
    VALIDATE_PROTOTYPE(RegQueryValueEx);
    if (lpcbData) {VALIDATE_OUTBUF(lpData, *lpcbData);}

    LONG    ret;
    DWORD   dwTempType;

    if (g_bRunningOnNT)
    {
#ifdef DEBUG
        if (lpType == NULL)
            lpType = &dwTempType;
#endif
        ret = RegQueryValueExW(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData);

        return ret;
    }

    CStrIn  strValueName(lpValueName);
    DWORD   cb;

    //
    // Determine the type of buffer needed
    //

    ret = RegQueryValueExA(hKey, strValueName, lpReserved, &dwTempType, NULL, (lpcbData ? &cb : NULL));
    if (ret != ERROR_SUCCESS)
        goto Cleanup;

    ASSERT(dwTempType != REG_MULTI_SZ);

    switch (dwTempType)
    {
    case REG_EXPAND_SZ:
    case REG_SZ:
        {
            CStrOut strData((LPWSTR) lpData, (lpcbData ? ((*lpcbData) / sizeof(WCHAR)) : 0));

            cb = strData.BufSize();
            ret = RegQueryValueExA(hKey, strValueName, lpReserved, lpType, (LPBYTE)(LPSTR)strData, (lpcbData ? &cb : NULL));
            if (ret != ERROR_SUCCESS)
                break;

            if (strData)
            {
                cb = strData.ConvertIncludingNul();
            }

            if (lpcbData)
                *lpcbData = cb * sizeof(WCHAR);
            break;
        }

    default:
        {
            ret = RegQueryValueExA(
                    hKey,
                    strValueName,
                    lpReserved,
                    lpType,
                    lpData,
                    lpcbData);

            break;
        }
    }

Cleanup:
    return ret;
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

LONG APIENTRY
RegSetValueWrapW(
        HKEY    hKey,
        LPCWSTR lpSubKey,
        DWORD   dwType,
        LPCWSTR lpData,
        DWORD   cbData)
{
    VALIDATE_PROTOTYPE(RegSetValue);

    if (g_bRunningOnNT)
    {
        return RegSetValueW(hKey, lpSubKey, dwType, lpData, cbData);
    }

    CStrIn  strKey(lpSubKey);
    CStrIn  strValue(lpData);

    return RegSetValueA(hKey, strKey, dwType, strValue, cbData);
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_ADVAPI32_WRAPPER

LONG APIENTRY
RegSetValueExWrapW(
        HKEY        hKey,
        LPCWSTR     lpValueName,
        DWORD       Reserved,
        DWORD       dwType,
        CONST BYTE* lpData,
        DWORD       cbData)
{
    VALIDATE_PROTOTYPE(RegSetValueEx);
    ASSERT(dwType != REG_MULTI_SZ);

    if (g_bRunningOnNT)
    {
        return RegSetValueExW(
            hKey,
            lpValueName,
            Reserved,
            dwType,
            lpData,
            cbData);
    }


    CStrIn      strKey(lpValueName);
    CStrIn      strSZ((dwType == REG_SZ || dwType == REG_EXPAND_SZ) ? (LPCWSTR) lpData : NULL);

    if (strSZ)
    {
        lpData = (LPBYTE) (LPSTR) strSZ;
        cbData = strSZ.strlen() + 1;
    }

    return RegSetValueExA(
            hKey,
            strKey,
            Reserved,
            dwType,
            lpData,
            cbData);
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_USER32_WRAPPER

ATOM WINAPI
RegisterClassWrapW(CONST WNDCLASSW * lpWndClass)
{
    VALIDATE_PROTOTYPE(RegisterClass);

    if (g_bRunningOnNT)
    {
        return RegisterClassW(lpWndClass);
    }

    WNDCLASSA   wc;
    CStrIn      strMenuName(lpWndClass->lpszMenuName);
    CStrIn      strClassName(lpWndClass->lpszClassName);

    ASSERT(sizeof(wc) == sizeof(*lpWndClass));
    memcpy(&wc, lpWndClass, sizeof(wc));

    wc.lpszMenuName = strMenuName;
    wc.lpszClassName = strClassName;

    return RegisterClassA(&wc);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

UINT WINAPI
RegisterClipboardFormatWrapW(LPCWSTR lpString)
{
    VALIDATE_PROTOTYPE(RegisterClipboardFormat);

    if (g_bRunningOnNT)
    {
        return RegisterClipboardFormatW(lpString);
    }

    CStrIn  str(lpString);

    return RegisterClipboardFormatA(str);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

UINT WINAPI
RegisterWindowMessageWrapW(LPCWSTR lpString)
{
    VALIDATE_PROTOTYPE(RegisterWindowMessage);

    if (g_bRunningOnNT)
    {
        return RegisterWindowMessageW(lpString);
    }

    CStrIn  str(lpString);

    return RegisterWindowMessageA(str);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI
RemoveDirectoryWrapW(LPCWSTR lpszDir)
{
    VALIDATE_PROTOTYPE(RemoveDirectory);

    if (g_bRunningOnNT)
    {
        return RemoveDirectoryW(lpszDir);
    }

    CStrIn  strDir(lpszDir);

    return RemoveDirectoryA(strDir);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

HANDLE WINAPI
RemovePropWrapW(
        HWND    hWnd,
        LPCWSTR lpString)
{
    VALIDATE_PROTOTYPE(RemoveProp);

    if (g_bRunningOnNT)
    {
        return RemovePropW(hWnd, lpString);
    }

    CStrIn  str(lpString);

    return RemovePropA(hWnd, str);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

LRESULT WINAPI SendMessageWrapW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
//  NOTE (SumitC) Instead of calling SendDlgItemMessageA below, I'm forwarding to
//       SendMessageWrap so as not to have to re-do the special-case processing.
LRESULT WINAPI
SendDlgItemMessageWrapW(
        HWND    hDlg,
        int     nIDDlgItem,
        UINT    Msg,
        WPARAM  wParam,
        LPARAM  lParam)
{
    VALIDATE_PROTOTYPE(SendDlgItemMessage);

    if (g_bRunningOnNT)
    {
        return SendDlgItemMessageW(hDlg, nIDDlgItem, Msg, wParam, lParam);
    }

    HWND hWnd;

    hWnd = GetDlgItem(hDlg, nIDDlgItem);

    return SendMessageWrapW(hWnd, Msg, wParam, lParam);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

int CharIndexToByteIndex(LPCSTR psz, int cch)
{
    if (cch <= 0)
        return cch;

    LPCSTR pszTemp = psz;
    while (*pszTemp && cch-- > 0)
    {
        pszTemp = CharNextA(pszTemp);
    }

    return (int)(pszTemp - psz);
}

int ByteIndexToCharIndex(LPCSTR psz, int cb)
{
    if (cb <=0)
        return cb;

    LPCSTR pszTemp = psz;
    LPCSTR pszEnd = &psz[cb];
    int cch = 0;

    while (*pszTemp && pszTemp < pszEnd)
    {
        pszTemp = CharNextA(pszTemp);
        ++cch;
    }

    return cch;
}

//
// Translate between byte positions and "character" positions
//
void TranslateCharPos(HWND hwnd, BOOL fByteIndexToCharIndex, DWORD* pdwPos, int cPos)
{
    int cch = GetWindowTextLengthA(hwnd);
    if (cch > 0)
    {
        char szBuf[MAX_PATH];
        LPSTR pszBuf = szBuf;

        if (cch >= ARRAYSIZE(szBuf))
        {
            pszBuf = new char[cch + 1];
        }

        if (pszBuf)
        {
            GetWindowTextA(hwnd, (LPSTR)pszBuf, cch+1);

            // Translate Each value passed in
            while (cPos--)
            {
                if (fByteIndexToCharIndex)
                {
                    *pdwPos = (DWORD)ByteIndexToCharIndex(pszBuf, (int)*pdwPos);
                }
                else
                {
                    *pdwPos = (DWORD)CharIndexToByteIndex(pszBuf, (int)*pdwPos);
                }
                ++pdwPos;
            }

            if (pszBuf != szBuf)
            {
                delete [] pszBuf;
            }
        }
    }
}

//
//  There is no WM_GETOBJECT/OBJID_QUERYCLASSNAMEIDX code for comboex,
//  so we will just send CCM_GETUNICODEFORMAT and hope for the best.
//  We are relying on the fact that Win95's combo box returns 0 in response
//  to messages it doesn't understand (like CCM_GETUNICODEFORMAT).
//
#define IsUnicodeComboEx(hWnd) SendMessageA(hWnd, CCM_GETUNICODEFORMAT, 0, 0)

#ifdef DEBUG
int g_cSMTot, g_cSMHit;
int g_cSMMod = 100;
#endif

LRESULT WINAPI
SendMessageAThunk(
        HWND    hWnd,
        UINT    Msg,
        WPARAM  wParam,
        LPARAM  lParam)
{
#ifdef DEBUG
    if ((g_cSMTot % g_cSMMod) == 0)
        TraceMsg(DM_PERF, "sm: tot=%d hit=%d", g_cSMTot, g_cSMHit);
#endif
    DBEXEC(TRUE, g_cSMTot++);
    // todo: perf? seems to be pretty common case, at least for now...
    DBEXEC(Msg > WM_USER, g_cSMHit++);
#if 0
    if (Msg > WM_USER)
        goto Ldef;
#endif

    switch (Msg)
    {
        case EM_GETLINE:
        {
            LRESULT nLen;

            CStrOut str((LPWSTR) lParam, (* (SHORT *) lParam) + 1);
            * (SHORT *) (LPSTR) str = * (SHORT *) lParam;
            nLen = SendMessageA(hWnd, Msg, (WPARAM) wParam, (LPARAM) (LPSTR) str);
        
            if(nLen > 0)
                ((LPSTR) str)[nLen] = '\0';

            return nLen;
        }

        case EM_SETPASSWORDCHAR:
        {
            WPARAM  wp = 0;

            ASSERT(HIWORD64(wParam) == 0);
            SHUnicodeToAnsi((LPWSTR) &wParam, (LPSTR) &wp, sizeof(wp));
            ASSERT(HIWORD64(wp) == 0);

            return SendMessageA(hWnd, Msg, wp, lParam);
        }

        case EM_SETWORDBREAKPROC:
        {
            // There is a bug with how USER handles WH_CALLWNDPROC global hooks in Win95/98 that
            // causes us to blow up if one is installed and a wordbreakproc is set.  Thus,
            // if an app is running that has one of these hooks installed (intellipoint 1.1 etc.) then
            // if we install our wordbreakproc the app will fault when the proc is called.  There
            // does not appear to be any way for us to work around it since USER's thunking code
            // trashes the stack so this API is disabled for Win9x.
            TraceMsg(TF_WARNING, "EM_SETWORDBREAKPROC can fault on Win9x; see unicwrap.cpp for details");
            return FALSE;
        }

        case EM_GETSEL:
        {
            //
            // Convert multibyte indicies into unicode indicies 
            //
            DWORD dw[2];
            SendMessageA(hWnd, Msg, (WPARAM)&dw[0], (LPARAM)&dw[1]);
            TranslateCharPos(hWnd, TRUE, dw, ARRAYSIZE(dw));

            // Return the results
            DWORD* pdwStart = (DWORD*)wParam;
            DWORD* pdwEnd   = (DWORD*)lParam;
            if (pdwStart)
            {
                *pdwStart = dw[0];
            }
            if (pdwEnd) 
            {
                *pdwEnd = dw[1];
            }

            LRESULT lr = (LRESULT)-1;

            if (dw[0] <= 0xffff && dw[1] <= 0xffff)
            {
                lr = MAKELONG(dw[0], dw[1]);
            }
            return lr;
        }

        case EM_SETSEL:
        {
            //
            // Convert unicode char indicies into multibyte indicies
            //
            DWORD dw[2];
            dw[0] = (DWORD)wParam;  // start
            dw[1] = (DWORD)lParam;  // end

            TranslateCharPos(hWnd, FALSE, dw, ARRAYSIZE(dw));

            return SendMessageA(hWnd, Msg, (WPARAM)dw[0], (LPARAM)dw[1]);
        }

        case EM_CHARFROMPOS:
        {
            DWORD dwPos =  SendMessageA(hWnd, Msg, wParam, lParam);
            if (HIWORD(dwPos) == 0)
            {
                TranslateCharPos(hWnd, TRUE, &dwPos, 1);
            }
            return dwPos;
        }

        case WM_SETTEXT:
        case LB_ADDSTRING:
        case CB_ADDSTRING:
        case EM_REPLACESEL:
            RIPMSG(wParam == 0, "wParam should be 0 for these messages");
            // fall through
        case LB_FINDSTRING:
        case LB_FINDSTRINGEXACT:
        case LB_INSERTSTRING:
        case LB_SELECTSTRING:
        case CB_FINDSTRING:
        case CB_FINDSTRINGEXACT:
        case CB_INSERTSTRING:
        case CB_SELECTSTRING:
        {
            if (MLIsEnabled(hWnd))
            {
                UINT uiMsgTx;

                switch (Msg)
                {
                    case WM_SETTEXT:
                        uiMsgTx = g_ML_SETTEXT;
                        break;
                    case LB_ADDSTRING:
                        uiMsgTx = g_ML_LB_ADDSTRING;
                        break;
                    case CB_ADDSTRING:
                        uiMsgTx = g_ML_CB_ADDSTRING;
                        break;
                    case LB_FINDSTRING:
                        uiMsgTx = g_ML_LB_FINDSTRING;
                        break;
                    case LB_FINDSTRINGEXACT:
                        uiMsgTx = g_ML_LB_FINDSTRINGEXACT;
                        break;
                    case LB_INSERTSTRING:
                        uiMsgTx = g_ML_LB_INSERTSTRING;
                        break;
                    case LB_SELECTSTRING:
                        uiMsgTx = g_ML_LB_SELECTSTRING;
                        break;
                    case CB_FINDSTRING:
                        uiMsgTx = g_ML_CB_FINDSTRING;
                        break;
                    case CB_FINDSTRINGEXACT:
                        uiMsgTx = g_ML_CB_FINDSTRINGEXACT;
                        break;
                    case CB_INSERTSTRING:
                        uiMsgTx = g_ML_CB_INSERTSTRING;
                        break;
                    case CB_SELECTSTRING:
                        uiMsgTx = g_ML_CB_SELECTSTRING;
                        break;

                    default:
                        ASSERT(0);
                }

                return SendMessageA(hWnd, uiMsgTx, wParam, lParam);
            }
            else if (Msg == CB_FINDSTRINGEXACT && IsUnicodeComboEx(hWnd))
            {
                // ComboEx is in UNICODE mode so we can send it the
                // unicode string
                goto Ldef;
            }
            else
            {
                // WARNING: in the ADDSTRING and INSERTSTRING cases for OWNERDRAW !HASSTRING
                // then this is a pointer to a structure and not a string!!!  (Seems that only
                // code that does this is comctl32's comboex, which currently doesn't come here.)
                //
                CStrIn  str((LPWSTR) lParam);

                return SendMessageA(hWnd, Msg, wParam, (LPARAM) (LPSTR) str);
            }
        }

        case WM_GETTEXT:
        case LB_GETTEXT:
        case CB_GETLBTEXT:
        {
            if (MLIsEnabled(hWnd))
            {
                UINT uiMsgTx;

                switch (Msg)
                {
                    case WM_GETTEXT:
                        uiMsgTx = g_ML_GETTEXT;
                        break;
                    case LB_GETTEXT:
                        uiMsgTx = g_ML_LB_GETTEXT;
                        break;
                    case CB_GETLBTEXT:
                        uiMsgTx = g_ML_CB_GETLBTEXT;
                        break;

                    default:
                        ASSERT(0);
                }

                return SendMessageA(hWnd, uiMsgTx, wParam, lParam);
            }
            else if (Msg == CB_GETLBTEXT && IsUnicodeComboEx(hWnd))
            {
                // ComboEx is in UNICODE mode so we can send it the
                // unicode string
                goto Ldef;
            }
            else
            {
                int iStrLen;

                switch (Msg)
                {
                    case WM_GETTEXT:
                        iStrLen = (int)wParam;
                        break;
                    case LB_GETTEXT:
                        iStrLen = SendMessageA(hWnd, LB_GETTEXTLEN, wParam, lParam);
                        if (iStrLen == LB_ERR)
                            iStrLen = 255;
                        break;
                    case CB_GETLBTEXT:
                        iStrLen = SendMessageA(hWnd, CB_GETLBTEXTLEN, wParam, lParam);
                        if (iStrLen == CB_ERR)
                            iStrLen = 255;
                        break;

                    default:
                        ASSERT(0);
                }

                CStrOut str((LPWSTR)lParam, (iStrLen + 1));
                SendMessageA(hWnd, Msg, wParam, (LPARAM) (LPSTR) str);

                return str.ConvertExcludingNul();
            }
        }

        case WM_GETTEXTLENGTH:
        case LB_GETTEXTLEN:
        case CB_GETLBTEXTLEN:
        {
            UINT uiMsgTx;

            if (MLIsEnabled(hWnd))
            {
                switch (Msg)
                {
                    case WM_GETTEXTLENGTH:
                        uiMsgTx = g_ML_GETTEXTLENGTH;
                        break;
                    case LB_GETTEXTLEN:
                        uiMsgTx = g_ML_LB_GETTEXTLEN;
                        break;
                    case CB_GETLBTEXTLEN:
                        uiMsgTx = g_ML_CB_GETLBTEXTLEN;
                        break;

                    default:
                        ASSERT(0);
                }


                return SendMessageA(hWnd, uiMsgTx, wParam, lParam);
            }
            else
            {
                // Bug: #70280
                // we can not just return the size of ANSI character back, it breaks some localized
                // version of Win98 with localized IE/OE scenario: some apps rely on the returned size
                // in characters to show the string and may cause the garbage displayed beyond the end of
                // the actual string.
                //
                LPWSTR lpwszTemp = NULL;
                int iWCharLen = 0;
                int iAnsiCharLen = SendMessageA(hWnd, Msg, wParam, lParam);
                if ((iAnsiCharLen <= 0) || (LB_ERR == iAnsiCharLen) || (CB_ERR == iAnsiCharLen))
                {
                    iWCharLen = iAnsiCharLen;   // return error if we can not get the ANSI string length
                    goto L_Rtn;
                }

                // we always allocate the wide string buffer in the size of ANSI string length plus 1,
                // it should be big enough to hold the returned wide string.
                lpwszTemp = (LPWSTR)LocalAlloc(LPTR, ((iAnsiCharLen + 1) * sizeof(WCHAR)));
                if (!lpwszTemp)
                    goto L_Rtn;

                switch (Msg)
                {
                    case WM_GETTEXTLENGTH:
                        uiMsgTx = WM_GETTEXT;
                        break;
                    case LB_GETTEXTLEN:
                        uiMsgTx = LB_GETTEXT;
                        break;
                    case CB_GETLBTEXTLEN:
                        uiMsgTx = CB_GETLBTEXT;
                        break;

                    default:
                        ASSERT(0);
                }

                iWCharLen = SendMessageAThunk(hWnd, uiMsgTx,
                                              ((uiMsgTx == WM_GETTEXT) ? (WPARAM)(iAnsiCharLen + 1) : wParam),
                                              (LPARAM)lpwszTemp);

L_Rtn:
                if (lpwszTemp)
                    LocalFree(lpwszTemp);

                // if error occured, we'll return the error (WM_, LB_, CB_),
                // if fail to allocate memory, we'll return 0, the initial value of iWCharlen.
                return (LRESULT)iWCharLen;
            }
        }

        case WM_SETTINGCHANGE:
        {
            if (lParam)
            {
                CStrIn str((LPWSTR)lParam);
                LRESULT lRes = 0;
                SendMessageTimeoutA(hWnd, Msg, wParam, (LPARAM) (LPSTR) str,
                                    SMTO_NORMAL, 3000, (PULONG_PTR)&lRes);

                return lRes;
            }
            goto Ldef;
        }

        // The new unicode comctl32.dll handles these correctly so we don't need to thunk:
        // TTM_DELTOOL, TTM_ADDTOOL, TVM_INSERTITEM, TVM_GETITEM, TCM_INSERTITEM, TCM_SETITEM

        default:
Ldef:
            return SendMessageA(hWnd, Msg, wParam, lParam);
    }
}


LRESULT WINAPI
SendMessageTimeoutAThunk(
        HWND    hWnd,
        UINT    uMsg,
        WPARAM  wParam,
        LPARAM  lParam,
        UINT    fuFlags,
        UINT    uTimeout,
        PULONG_PTR lpdwResult)
{
    switch (uMsg)
    {
    case WM_SETTINGCHANGE:
        {
            if (lParam)
            {
                CStrIn str((LPWSTR)lParam);
                return SendMessageTimeoutA(hWnd, uMsg, wParam, (LPARAM)(LPSTR)str,
                                    fuFlags, uTimeout, lpdwResult);
            }
        }
        break;
    }
    return SendMessageTimeoutA(hWnd, uMsg, wParam, lParam, fuFlags, uTimeout, lpdwResult);
}


LRESULT FORWARD_API WINAPI
SendMessageTimeoutWrapW(
        HWND    hWnd,
        UINT    Msg,
        WPARAM  wParam,
        LPARAM  lParam,
        UINT    fuFlags,
        UINT    uTimeout,
        PULONG_PTR lpdwResult)
{
    VALIDATE_PROTOTYPE(SendMessage);

    // perf: we should do _asm here (see CallWindowProcWrapW), but
    // to do that we need to 'outline' the below switch (o.w. we
    // can't be 'naked').  that in turn slows down the non-NT case...

#ifndef UNIX
    THUNK_AW(SendMessageTimeout, (hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult));
#else
    return SendMessageTimeoutW(hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult);
#endif
}

LRESULT FORWARD_API WINAPI
SendMessageWrapW(
        HWND    hWnd,
        UINT    Msg,
        WPARAM  wParam,
        LPARAM  lParam)
{
    VALIDATE_PROTOTYPE(SendMessage);

    // perf: we should do _asm here (see CallWindowProcWrapW), but
    // to do that we need to 'outline' the below switch (o.w. we
    // can't be 'naked').  that in turn slows down the non-NT case...

#ifndef UNIX
    // n.b. THUNK not FORWARD
    THUNK_AW(SendMessage, (hWnd, Msg, wParam, lParam));
#else
    return SendMessageW(hWnd, Msg, wParam, lParam);
#endif
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI
SetCurrentDirectoryWrapW(LPCWSTR lpszCurDir)
{
    VALIDATE_PROTOTYPE(SetCurrentDirectory);

    if (g_bRunningOnNT)
    {
        return SetCurrentDirectoryW(lpszCurDir);
    }

    CStrIn  str(lpszCurDir);

    return SetCurrentDirectoryA(str);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL WINAPI
SetDlgItemTextWrapW(HWND hDlg, int nIDDlgItem, LPCWSTR lpString)
{
    VALIDATE_PROTOTYPE(SetDlgItemText);

    HWND hWnd = GetDlgItem(hDlg, nIDDlgItem);

    if (hWnd)
        return SetWindowTextWrapW(hWnd, lpString);
    else
        return FALSE;
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL WINAPI
SetPropWrapW(
    HWND    hWnd,
    LPCWSTR lpString,
    HANDLE  hData)
{
    VALIDATE_PROTOTYPE(SetProp);

    if (g_bRunningOnNT)
    {
        return SetPropW(hWnd, lpString, hData);
    }

    CStrIn  str(lpString);

    return SetPropA(hWnd, str, hData);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

LONG FORWARD_API WINAPI
SetWindowLongWrapW(HWND hWnd, int nIndex, LONG dwNewLong)
{
    VALIDATE_PROTOTYPE(SetWindowLong);

    FORWARD_AW(SetWindowLong, (hWnd, nIndex, dwNewLong));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

HHOOK
FORWARD_API WINAPI
SetWindowsHookExWrapW(
    int idHook,
    HOOKPROC lpfn,
    HINSTANCE hmod,
    DWORD dwThreadId)
{
    VALIDATE_PROTOTYPE(SetWindowsHookEx);

    FORWARD_AW(SetWindowsHookEx, (idHook, lpfn, hmod, dwThreadId));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL WINAPI
SetWindowTextWrapW(HWND hWnd, LPCWSTR lpString)
{
    VALIDATE_PROTOTYPE(SetWindowText);

    if (MLIsEnabled(hWnd))
        return MLSetControlTextI(hWnd, lpString);

    if (g_bRunningOnNT)
    {
        return SetWindowTextW(hWnd, lpString);
    }

    CStrIn  str(lpString);

    return SetWindowTextA(hWnd, str);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL WINAPI
SystemParametersInfoWrapW(
        UINT    uiAction,
        UINT    uiParam,
        PVOID   pvParam,
        UINT    fWinIni)
{
    VALIDATE_PROTOTYPE(SystemParametersInfo);

    if (g_bRunningOnNT)
    {
        return SystemParametersInfoW(
                        uiAction,
                        uiParam,
                        pvParam,
                        fWinIni);
    }

    BOOL        ret;
    char        ach[LF_FACESIZE];

    if (uiAction == SPI_SETDESKWALLPAPER)
    {
        CStrIn str((LPCWSTR) pvParam);

        ret = SystemParametersInfoA(
                        uiAction,
                        uiParam,
                        str,
                        fWinIni);
    }
    else
        ret = SystemParametersInfoA(
                        uiAction,
                        uiParam,
                        pvParam,
                        fWinIni);

    if ((uiAction == SPI_GETICONTITLELOGFONT) && ret)
    {
        strcpy(ach, ((LPLOGFONTA)pvParam)->lfFaceName);
        SHAnsiToUnicode(ach, ((LPLOGFONTW)pvParam)->lfFaceName, ARRAYSIZE(((LPLOGFONTW)pvParam)->lfFaceName));
    }

    return ret;
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

int FORWARD_API WINAPI
TranslateAcceleratorWrapW(HWND hWnd, HACCEL hAccTable, LPMSG lpMsg)
{
    VALIDATE_PROTOTYPE(TranslateAccelerator);

    FORWARD_AW(TranslateAccelerator, (hWnd, hAccTable, lpMsg));
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL WINAPI
UnregisterClassWrapW(LPCWSTR lpClassName, HINSTANCE hInstance)
{
    VALIDATE_PROTOTYPE(UnregisterClass);

    if (g_bRunningOnNT)
    {
        return UnregisterClassW(lpClassName, hInstance);
    }

    CStrIn  str(lpClassName);

    return UnregisterClassA(str, hInstance);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

SHORT
WINAPI
VkKeyScanWrapW(WCHAR ch)
{
    VALIDATE_PROTOTYPE(VkKeyScan);

    if (g_bRunningOnNT)
    {
        return VkKeyScanW(ch);
    }

    CStrIn str(&ch, 1);

    return VkKeyScanA(*(char *)str);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

BOOL WINAPI
WinHelpWrapW(HWND hwnd, LPCWSTR szFile, UINT uCmd, ULONG_PTR dwData)
{
    VALIDATE_PROTOTYPE(WinHelp);
  
    if (g_bRunningOnNT)
    {
        return WinHelpW(hwnd, szFile, uCmd, dwData);
    }

    CStrIn  str(szFile);

    return WinHelpA(hwnd, str, uCmd, dwData);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

//+---------------------------------------------------------------------------
//
//  Function:   wvsprintfW
//
//  Synopsis:   Nightmare string function
//
//  Arguments:  [pwszOut]    --
//              [pwszFormat] --
//              [...]        --
//
//  Returns:
//
//  History:    1-06-94   ErikGav   Created
//
//  Notes:      If you're reading this, you're probably having a problem with
//              this function.  Make sure that your "%s" in the format string
//              says "%ws" if you are passing wide strings.
//
//              %s on NT means "wide string"
//              %s on Chicago means "ANSI string"
//
//  NOTE:       This function should not be used.  Use Format instead.
//
//----------------------------------------------------------------------------

int WINAPI
wvsprintfWrapW(LPWSTR pwszOut, LPCWSTR pwszFormat, va_list arglist)
{
    VALIDATE_PROTOTYPE(wvsprintf);

    if (g_bRunningOnNT)
    {
        return wvsprintfW(pwszOut, pwszFormat, arglist);
    }

    // Old code created a 1K ansi output buffer for wvsprintfA and then
    // thunked the result.  If we're going to do that, might as
    // well create a 1K unicode output buffer and call our wvnsprintfW
    // implementation directly.  Two benefits: 1) no buffer overrun
    // and 2) native W implementation so we can handle unicode strings!
    WCHAR wszOut[1024];

    wvnsprintfW(wszOut, ARRAYSIZE(wszOut), pwszFormat, arglist);

    // Roll our own "ConvertExcludingNul" since this is W->W
    int ret = SHUnicodeToUnicode(wszOut, pwszOut, ARRAYSIZE(wszOut));
    if (ret > 0)
    {
        ret -= 1;
    }
    return ret;
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_MPR_WRAPPER

//+---------------------------------------------------------------------------
//
//  Function:   WNetRestoreConnectionWrapW
//
//----------------------------------------------------------------------------

DWORD WINAPI WNetRestoreConnectionWrapW(IN HWND hwndParent, IN LPCWSTR pwzDevice)
{
    if (g_bRunningOnNT)
    {
        return WNetRestoreConnectionW(hwndParent, pwzDevice);
    }

    CStrIn  strIn(pwzDevice);
    return WNetRestoreConnectionA(hwndParent, strIn);
}

#endif // NEED_MPR_WRAPPER

#ifdef NEED_MPR_WRAPPER

//+---------------------------------------------------------------------------
//
//  Function:   WNetGetLastErrorWrapW
//
//
//----------------------------------------------------------------------------

DWORD WINAPI WNetGetLastErrorWrapW(OUT LPDWORD pdwError, OUT LPWSTR pwzErrorBuf, IN DWORD cchErrorBufSize, OUT LPWSTR pwzNameBuf, IN DWORD cchNameBufSize)
{
    VALIDATE_PROTOTYPE(WNetGetLastError);

    if (g_bRunningOnNT)
    {
        return WNetGetLastErrorW(pdwError, pwzErrorBuf, cchErrorBufSize, pwzNameBuf, cchNameBufSize);
    }

    // Consider: Out-string bufsize too large or small?
    CStrOut strErrorOut(pwzErrorBuf, cchErrorBufSize);
    CStrOut strNameOut(pwzNameBuf, cchNameBufSize);

    DWORD dwResult = WNetGetLastErrorA(pdwError, strErrorOut, strErrorOut.BufSize(), strNameOut, strNameOut.BufSize());

    strErrorOut.ConvertExcludingNul();
    strNameOut.ConvertExcludingNul();
    return dwResult;
}

#endif // NEED_MPR_WRAPPER

#ifdef NEED_USER32_WRAPPER

int WINAPI DrawTextExWrapW(
    HDC hdc,    // handle of device context
    LPWSTR pwzText, // address of string to draw
    int cchText,    // length of string to draw
    LPRECT lprc,    // address of rectangle coordinates
    UINT dwDTFormat,    // formatting options
    LPDRAWTEXTPARAMS lpDTParams // address of structure for more options
   )
{
    VALIDATE_PROTOTYPE(DrawTextEx);
    if (_MayNeedFontLinking(pwzText, cchText))
    {
        return DrawTextExFLW(hdc, pwzText, cchText, lprc, dwDTFormat, lpDTParams);
    }
    else if (g_bRunningOnNT)
    {
        return DrawTextExW(hdc, pwzText, cchText, lprc, dwDTFormat, lpDTParams);
    }
    CStrIn strText(pwzText, cchText);
    return DrawTextExA(hdc, strText, strText.strlen(), lprc, dwDTFormat, lpDTParams);
}

#endif // NEED_USER32_WRAPPER

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

void SetThunkMenuItemInfoWToA(LPCMENUITEMINFOW pmiiW, LPMENUITEMINFOA pmiiA, LPSTR pszBuffer, DWORD cchSize)
{
    *pmiiA = *(LPMENUITEMINFOA) pmiiW;

    // MFT_STRING is Zero. So MFT_STRING & anything evaluates to False.
    // so instead you have to check for the absence of non-string items
    if ((pmiiW->dwTypeData) && (MFT_NONSTRING & pmiiW->fType) == 0)
    {
        pmiiA->dwTypeData = pszBuffer;
        pmiiA->cch = cchSize;
        SHUnicodeToAnsi(pmiiW->dwTypeData, pmiiA->dwTypeData, cchSize);
    }
}

void GetThunkMenuItemInfoWToA(LPCMENUITEMINFOW pmiiW, LPMENUITEMINFOA pmiiA, LPSTR pszBuffer, DWORD cchSize)
{
    *pmiiA = *(LPMENUITEMINFOA) pmiiW;

    if ((pmiiW->dwTypeData) && (MFT_STRING & pmiiW->fType))
    {
        pszBuffer[0] = 0;
        pmiiA->dwTypeData = pszBuffer;
        pmiiA->cch = cchSize;
    }
}

void GetThunkMenuItemInfoAToW(LPCMENUITEMINFOA pmiiA, LPMENUITEMINFOW pmiiW)
{
    LPWSTR pwzText = pmiiW->dwTypeData;
    UINT cch = pmiiW->cch;

    *pmiiW = *(LPMENUITEMINFOW) pmiiA;
    pmiiW->dwTypeData = pwzText;
    pmiiW->cch = cch;

    if ((pmiiA->cch > 0) && (pmiiA->dwTypeData) && (pwzText) && !((MFT_SEPARATOR | MFT_BITMAP) & pmiiA->fType))
        SHAnsiToUnicode(pmiiA->dwTypeData, pmiiW->dwTypeData, pmiiW->cch);
}

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

BOOL WINAPI GetMenuItemInfoWrapW(
    HMENU  hMenu,
    UINT  uItem,
    BOOL  fByPosition,
    LPMENUITEMINFOW  pmiiW)
{
    BOOL fResult;
    VALIDATE_PROTOTYPE(GetMenuItemInfo);
    ASSERT(pmiiW->cbSize == MENUITEMINFOSIZE_WIN95); // Ensure Win95 compatibility

    static BOOL bOwnerDraw = NeedMenuOwnerDraw();
    BOOL fItemData;
    DWORD_PTR dwItemData;
    LPWSTR pwz;
    UINT cch;

    if (bOwnerDraw)
    {
        if (MIIM_DATA & pmiiW->fMask)
            fItemData = TRUE;
        else
        {
            fItemData = FALSE;
            pmiiW->fMask |= MIIM_DATA;
        }
        pwz = pmiiW->dwTypeData;        // save original buffer pointer
        cch = pmiiW->cch;               // save original buffer size
        dwItemData = pmiiW->dwItemData; // save original dwItemData size
    }

#ifndef UNIX
    // Widechar API's are messed up in MAINWIN. For now assume not ruuning on
    // NT for this.
    if (g_bRunningOnNT)
        fResult = GetMenuItemInfoW(hMenu, uItem, fByPosition, pmiiW);
    else
#endif
    {
        if (pmiiW->fMask & MIIM_TYPE)
        {
            MENUITEMINFOA miiA = *(LPMENUITEMINFOA)pmiiW;
            LPSTR pszText = NULL;

            if (pmiiW->cch > 0)
                pszText = new char[pmiiW->cch * 2];  // for DBCS, we multifly by 2

            miiA.dwTypeData = pszText;
            miiA.cch = (pszText)? pmiiW->cch * 2: 0; // set correct buffer size
            fResult = GetMenuItemInfoA(hMenu, uItem, fByPosition, &miiA);
            GetThunkMenuItemInfoAToW(&miiA, pmiiW);

            if (pszText)
                delete pszText;
        }
        else
            fResult = GetMenuItemInfoA(hMenu, uItem, fByPosition, (LPMENUITEMINFOA) pmiiW); // It doesn't contain a string so W and A are the same.
    }

    if (bOwnerDraw)
    {
        if ((MIIM_TYPE & pmiiW->fMask) && (MFT_OWNERDRAW & pmiiW->fType))
        {
            LPPUIMENUITEM lpItem = (LPPUIMENUITEM)pmiiW->dwItemData;

            if (!IsBadReadPtr(lpItem, sizeof(lpItem->dwSig)) && PUI_OWNERDRAW_SIG == lpItem->dwSig && !(MFT_OWNERDRAW & lpItem->fType))
            {
                if ((cch > 0) && pwz && !((MFT_SEPARATOR | MFT_BITMAP) & pmiiW->fType))
                {
                    StrCpyNW(pwz, lpItem->lpwz, cch);
                    pmiiW->dwTypeData = pwz;
                    pmiiW->cch = lpItem->cch;
                    pmiiW->fType &= ~MFT_OWNERDRAW;
                    if (fItemData)
                        pmiiW->dwItemData = lpItem->dwItemData;
                    else
                        pmiiW->dwItemData = dwItemData;
                }
            }
        }
    }

    return fResult;
}

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

BOOL WINAPI InsertMenuItemWrapW(
    HMENU hMenu,
    UINT uItem,
    BOOL fByPosition,
    LPCMENUITEMINFOW pmiiW)
{
    VALIDATE_PROTOTYPE(InsertMenuItem);
    ASSERT(pmiiW->cbSize == MENUITEMINFOSIZE_WIN95); // Ensure Win95 compatibility

    BOOL fResult;

    static BOOL bOwnerDraw = NeedMenuOwnerDraw();
    MENUITEMINFOW miiW;

    if (bOwnerDraw)
    {
        MungeMenuItem(hMenu, pmiiW, &miiW);
        pmiiW = &miiW;
    }

    if (g_bRunningOnNT)
        return InsertMenuItemW(hMenu, uItem, fByPosition, pmiiW);

    MENUITEMINFOA miiA;
    CHAR szText[INTERNET_MAX_URL_LENGTH];

    SetThunkMenuItemInfoWToA(pmiiW, &miiA, szText, ARRAYSIZE(szText));
    fResult = InsertMenuItemA(hMenu, uItem, fByPosition, &miiA);

    return fResult;
}

// This is required for ML, so do not put inside #ifdef NEED_USER32_WRAPPER

BOOL WINAPI
SetMenuItemInfoWrapW(
    HMENU hMenu,
    UINT uItem,
    BOOL fByPosition,
    LPCMENUITEMINFOW lpmiiW)
{
    VALIDATE_PROTOTYPE(SetMenuItemInfo);
    ASSERT(lpmiiW->cbSize == MENUITEMINFOSIZE_WIN95); // Ensure Win95 compatibility

    static BOOL bOwnerDraw = NeedMenuOwnerDraw();
    MENUITEMINFOW miiW;

    if (bOwnerDraw)
    {
        if ( (MIIM_TYPE & lpmiiW->fMask) &&
             0 == (lpmiiW->fType & (MFT_BITMAP | MFT_SEPARATOR)))
        {
            DeleteOwnerDrawMenuItem(hMenu, uItem, fByPosition);
            MungeMenuItem(hMenu, lpmiiW, &miiW);
            lpmiiW = &miiW;
        }
    }

    if (g_bRunningOnNT)
    {
        return SetMenuItemInfoW( hMenu, uItem, fByPosition, lpmiiW);
    }

    BOOL fRet;

    ASSERT( sizeof(MENUITEMINFOW) == sizeof(MENUITEMINFOA) &&
            FIELD_OFFSET(MENUITEMINFOW, dwTypeData) ==
            FIELD_OFFSET(MENUITEMINFOA, dwTypeData) );

    if ( (MIIM_TYPE & lpmiiW->fMask) &&
         0 == (lpmiiW->fType & (MFT_BITMAP | MFT_SEPARATOR)))
    {
        MENUITEMINFOA miiA;
        // the cch is ignored on SetMenuItemInfo
        CStrIn str((LPWSTR)lpmiiW->dwTypeData, -1);

        memcpy( &miiA, lpmiiW, sizeof(MENUITEMINFOA) );
        miiA.dwTypeData = (LPSTR)str;
        miiA.cch = str.strlen();

        fRet = SetMenuItemInfoA( hMenu, uItem, fByPosition, &miiA );
    }
    else
    {
        fRet = SetMenuItemInfoA( hMenu, uItem, fByPosition,
                                 (LPCMENUITEMINFOA)lpmiiW );
    }

    return fRet;
}

#ifdef NEED_GDI32_WRAPPER

HFONT WINAPI
CreateFontWrapW(int  nHeight,   // logical height of font
                int  nWidth,    // logical average character width
                int  nEscapement,   // angle of escapement
                int  nOrientation,  // base-line orientation angle
                int  fnWeight,  // font weight
                DWORD  fdwItalic,   // italic attribute flag
                DWORD  fdwUnderline,    // underline attribute flag
                DWORD  fdwStrikeOut,    // strikeout attribute flag
                DWORD  fdwCharSet,  // character set identifier
                DWORD  fdwOutputPrecision,  // output precision
                DWORD  fdwClipPrecision,    // clipping precision
                DWORD  fdwQuality,  // output quality
                DWORD  fdwPitchAndFamily,   // pitch and family
                LPCWSTR  pwzFace)   // address of typeface name string )
{
    VALIDATE_PROTOTYPE(CreateFont);

    if (g_bRunningOnNT)
    {
        return CreateFontW(nHeight, nWidth, nEscapement, nOrientation, fnWeight, fdwItalic,
                        fdwUnderline, fdwStrikeOut, fdwCharSet, fdwOutputPrecision,
                        fdwClipPrecision, fdwQuality, fdwPitchAndFamily, pwzFace);
    }

    CStrIn  str(pwzFace);
    return CreateFontA(nHeight, nWidth, nEscapement, nOrientation, fnWeight, fdwItalic,
                        fdwUnderline, fdwStrikeOut, fdwCharSet, fdwOutputPrecision,
                        fdwClipPrecision, fdwQuality, fdwPitchAndFamily, str);
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

HANDLE WINAPI CreateMutexWrapW(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCWSTR pwzName)
{
    VALIDATE_PROTOTYPE(CreateMutex);

    if (g_bRunningOnNT)
        return CreateMutexW(lpMutexAttributes, bInitialOwner, pwzName);

    CStrIn strText(pwzName);
    return CreateMutexA(lpMutexAttributes, bInitialOwner, strText);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

HDC WINAPI CreateMetaFileWrapW(LPCWSTR pwzFile)
{
    VALIDATE_PROTOTYPE(CreateMetaFile);

    if (g_bRunningOnNT)
        return CreateMetaFileW(pwzFile);

    CStrIn strText(pwzFile);
    return CreateMetaFileA(strText);
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

// ExpandEnvironmentStrings returns the size of the needed string
DWORD WINAPI ExpandEnvironmentStringsWrapW(LPCWSTR pwzSrc, LPWSTR pwzDst, DWORD cchSize)
{
    DWORD dwRet;

    if (pwzDst)
    {
        VALIDATE_OUTBUF(pwzDst, cchSize);
    }

    if (g_bRunningOnNT)
    {
        dwRet = ExpandEnvironmentStringsW(pwzSrc, pwzDst, cchSize);
    }
    else
    {
        CStrIn strTextIn(pwzSrc);
        CStrOut strTextOut(pwzDst, cchSize);
        DWORD dwResult = ExpandEnvironmentStringsA(strTextIn, strTextOut, strTextOut.BufSize());
        DWORD dwResultUnicode = strTextOut.ConvertIncludingNul();

        // NT4 returned the number of bytes in the UNICODE string, not the number
        // of bytes in the ANSI string.  NT5 returns the number of bytes in the UNICODE
        // string divided by 2, so it has even less bearing on the ansi string.  Hopefully
        // the Win9x implementations did the right thing...
        //
        if (dwResult <= cchSize)
        {
            ASSERT(dwResultUnicode <= dwResult);
            dwRet = dwResultUnicode; // we fit in the out buffer, give accurate count
        }
        else
        {
            dwRet = dwResult; // buffer was too small, let caller know (NOTE: this may be bigger than needed)
        }
    }

    return dwRet;
}

#endif // NEED_KERNEL32_WRAPPER

// SHExpandEnvironmentStrings
//
// In all cases, this returns a valid output buffer.  The buffer may
// be empty, or it may be truncated, but you can always use the string.
//
// RETURN VALUE:
//   0  implies failure, either a truncated expansion or no expansion whatsoever
//   >0 implies complete expansion, value is count of characters written (excluding NULL)
//
DWORD WINAPI SHExpandEnvironmentStringsForUserW(HANDLE hToken, LPCWSTR pwzSrc, LPWSTR pwzDst, DWORD cchSize)
{
    DWORD   dwRet;

    // 99/05/28 vtan: Handle specified users here. It's a Windows NT
    // thing only. Check for both conditions then load the function
    // dynamically out of userenv.dll. If the function cannot be
    // located or returns a problem default to the current user as
    // NULL hToken.

    if (g_bRunningOnNT5OrHigher && (hToken != NULL))
    {
        VALIDATE_OUTBUF(pwzDst, cchSize);
        if (NT5_ExpandEnvironmentStringsForUserW(hToken, pwzSrc, pwzDst, cchSize) != FALSE)
        {

            // userenv!ExpandEnvironmentStringsForUser returns
            // a BOOL result. Convert this to a DWORD result
            // that matches what kernel32!ExpandEnvironmentStrings
            // returns.

            dwRet = lstrlenW(pwzDst) + sizeof('\0');
        }
        else
        {
            dwRet = 0;
        }
    }
    else
    {
        dwRet = ExpandEnvironmentStringsWrapW(pwzSrc, pwzDst, cchSize);
    }

    // The implementations of this function don't seem to gurantee gurantee certain
    // things about the output buffer in failure conditions that callers rely on.
    // So clean things up here.
    //
    // And I found code occasionally that handled semi-failure (success w/ dwRet>cchSize)
    // that assumed the string wasn't properly NULL terminated in this case.  Fix that here
    // in the wrapper so our callers don't have to wig-out about errors.
    //
    // NOTE: we map all failures to 0 too.
    //
    if (dwRet > cchSize)
    {
        // Buffer too small, some code assumed there was still a string there and
        // tried to NULL terminate, do it for them.
        SHTruncateString(pwzDst, cchSize);
        dwRet = 0;
    }
    else if (dwRet == 0)
    {
        // Failure, assume no expansions...
        StrCpyNW(pwzDst, pwzSrc, cchSize);
    }

    return dwRet;
}

DWORD WINAPI SHExpandEnvironmentStringsW(LPCWSTR pwzSrc, LPWSTR pwzDst, DWORD cchSize)
{
    return(SHExpandEnvironmentStringsForUserW(NULL, pwzSrc, pwzDst, cchSize));
}

DWORD WINAPI SHExpandEnvironmentStringsForUserA(HANDLE hToken, LPCSTR pszSrc, LPSTR pszDst, DWORD cchSize)
{
    DWORD dwRet;

    // 99/05/28 vtan: The ANSI version of SHExpandEnvironmentStringsForUser
    // exists in case somebody calls the ANSI implementation in WindowsNT.
    // This is meaningless on Win9x. This just thunks parameters and invokes
    // the Unicode implementation. If a problem occurs when thunking just
    // use the current user.

    if (g_bRunningOnNT5OrHigher && (hToken != NULL))
    {
        DWORD       dwResultAnsi;
        CStrInW     strInW(pszSrc);
        CStrOutW    strOutW(pszDst, cchSize);

        dwRet = SHExpandEnvironmentStringsForUserW(hToken, strInW, strOutW, strOutW.BufSize());
        dwResultAnsi = strOutW.ConvertIncludingNul();
        if (dwResultAnsi <= cchSize)
            dwRet = dwResultAnsi;
    }
    else
    {
        // NT4 returns the number of bytes in the UNICODE string.
        //
        // NT5 (as of June 99) returns the number of bytes in the UNICODE string / 2, so it
        // really has no accurate bearing on the number of ansi characters.  Plus we're not
        // guranteed buffer truncation in failure case.
        //
        // Not much we can do about it here, eh?  Hopefully our callers ignore the return result...
        //
        dwRet = ExpandEnvironmentStringsA(pszSrc, pszDst, cchSize);
    }

    // The implementations of this function don't seem to gurantee gurantee certain
    // things about the output buffer in failure conditions that callers rely on.
    // So clean things up here.
    //
    // And I found code occasionally that handled semi-failure (success w/ dwRet>cchSize)
    // that assumed the string wasn't properly NULL terminated in this case.  Fix that here
    // in the wrapper so our callers don't have to wig-out about errors.
    //
    // NOTE: we map all failures to 0 too.
    //
    if (dwRet > cchSize)
    {
        // Buffer too small, make sure it's NULL terminated.
        SHTruncateString(pszDst, cchSize);
        dwRet = 0;
    }
    else if (dwRet == 0)
    {
        // Failure, assume no expansions...
        StrCpyNA(pszDst, pszSrc, cchSize);
    }

    return dwRet;
}

DWORD WINAPI SHExpandEnvironmentStringsA(LPCSTR pszSrc, LPSTR pszDst, DWORD cchSize)
{
    return(SHExpandEnvironmentStringsForUserA(NULL, pszSrc, pszDst, cchSize));
}

#ifdef NEED_KERNEL32_WRAPPER

HANDLE WINAPI CreateSemaphoreWrapW(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, LPCWSTR pwzName)
{
    VALIDATE_PROTOTYPE(CreateSemaphore);
    if (g_bRunningOnNT)
        return CreateSemaphoreW(lpSemaphoreAttributes, lInitialCount, lMaximumCount, pwzName);

    CStrIn strText(pwzName);
    return CreateSemaphoreA(lpSemaphoreAttributes, lInitialCount, lMaximumCount, strText);
}

#endif // NEED_KERNEL32_WRAPPER

// FEATURE: Todo - GetStartupInfoWrapW

#ifdef NEED_KERNEL32_WRAPPER

#define ISGOOD 0
#define ISBAD 1

BOOL WINAPI IsBadStringPtrWrapW(LPCWSTR pwzString, UINT_PTR ucchMax)
{
    VALIDATE_PROTOTYPE(IsBadStringPtr);
    if (g_bRunningOnNT)
        return IsBadStringPtrW(pwzString, ucchMax);

    if (!ucchMax)
        return ISGOOD;

    if (!pwzString)
        return ISBAD;

    LPCWSTR pwzStartAddress = pwzString;
    // ucchMax maybe -1 but that's ok because the loop down below will just
    // look for the terminator.
    LPCWSTR pwzEndAddress = &pwzStartAddress[ucchMax - 1];
    TCHAR chTest;

    _try
    {
        chTest = *(volatile WCHAR *)pwzStartAddress;
        while (chTest && (pwzStartAddress != pwzEndAddress))
        {
            pwzStartAddress++;
            chTest = *(volatile WCHAR *)pwzStartAddress;
        }
    }
    _except (EXCEPTION_EXECUTE_HANDLER)
    {
        return ISBAD;
    }
    __endexcept

    return ISGOOD;
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

HINSTANCE WINAPI LoadLibraryWrapW(LPCWSTR pwzLibFileName)
{
    VALIDATE_PROTOTYPE(LoadLibrary);

    if (g_bRunningOnNT)
        return LoadLibraryW(pwzLibFileName);

    CStrIn  strFileName(pwzLibFileName);
    return LoadLibraryA(strFileName);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

int WINAPI GetTimeFormatWrapW(LCID Locale, DWORD dwFlags, CONST SYSTEMTIME * lpTime, LPCWSTR pwzFormat, LPWSTR pwzTimeStr, int cchTime)
{
    VALIDATE_PROTOTYPE(GetTimeFormat);
    if (g_bRunningOnNT)
        return GetTimeFormatW(Locale, dwFlags, lpTime, pwzFormat, pwzTimeStr, cchTime);

    CStrIn strTextIn(pwzFormat);

    // Cross codepage text will be trashed if it is rendered in ANSI, 
    // Use system codepage for conversion to make sure we don't trash native ANSI text.
    CStrOut strTextOut(pwzTimeStr, cchTime);
    int nResult = GetTimeFormatA(Locale, dwFlags, lpTime, strTextIn, strTextOut, strTextOut.BufSize());
    if (cchTime != 0)
        nResult = strTextOut.ConvertIncludingNul();

    return nResult;
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

int WINAPI GetDateFormatWrapW(LCID Locale, DWORD dwFlags, CONST SYSTEMTIME * lpDate, LPCWSTR pwzFormat, LPWSTR pwzDateStr, int cchDate)
{
    VALIDATE_PROTOTYPE(GetDateFormat);
    if (g_bRunningOnNT)
        return GetDateFormatW(Locale, dwFlags, lpDate, pwzFormat, pwzDateStr, cchDate);

    CStrIn strTextIn(pwzFormat);
    CStrOut strTextOut(pwzDateStr, cchDate);
    int nResult = GetDateFormatA(Locale, dwFlags, lpDate, strTextIn, strTextOut, strTextOut.BufSize());
    if (cchDate != 0)
        nResult = strTextOut.ConvertIncludingNul();

    return nResult;
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI WritePrivateProfileStringWrapW(LPCWSTR pwzAppName, LPCWSTR pwzKeyName, LPCWSTR pwzString, LPCWSTR pwzFileName)
{
    VALIDATE_PROTOTYPE(WritePrivateProfileString);
    if (g_bRunningOnNT)
        return WritePrivateProfileStringW(pwzAppName, pwzKeyName, pwzString, pwzFileName);

    CStrIn strTextAppName(pwzAppName);
    CStrIn strTextKeyName(pwzKeyName);
    CStrIn strTextString(pwzString);
    CPPFIn strTextFileName(pwzFileName); // PrivateProfile filename needs special class

    return WritePrivateProfileStringA(strTextAppName, strTextKeyName, strTextString, strTextFileName);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

DWORD WINAPI GetPrivateProfileStringWrapW(LPCWSTR pwzAppName, LPCWSTR pwzKeyName, LPCWSTR pwzDefault, LPWSTR pwzReturnedString, DWORD cchSize, LPCWSTR pwzFileName)
{
    VALIDATE_PROTOTYPE(GetPrivateProfileString);
    if (g_bRunningOnNT)
        return GetPrivateProfileStringW(pwzAppName, pwzKeyName, pwzDefault, pwzReturnedString, cchSize, pwzFileName);

    CStrIn strTextAppName(pwzAppName);
    CStrIn strTextKeyName(pwzKeyName);
    CStrIn strTextDefault(pwzDefault);
    CPPFIn strTextFileName(pwzFileName); // PrivateProfile filename needs special class

    CStrOut strTextOut(pwzReturnedString, cchSize);
    DWORD dwResult = GetPrivateProfileStringA(strTextAppName, strTextKeyName, strTextDefault, strTextOut, cchSize, strTextFileName);
    strTextOut.ConvertIncludingNul();

    return dwResult;
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_SHELL32_WRAPPER

STDAPI_(DWORD_PTR) SHGetFileInfoWrapW(LPCWSTR pwzPath, DWORD dwFileAttributes, SHFILEINFOW FAR  *psfi, UINT cbFileInfo, UINT uFlags)
{
    if (g_bRunningOnNT)
    {
        return SHGetFileInfoW(pwzPath, dwFileAttributes, psfi, cbFileInfo, uFlags);
    }

    SHFILEINFOA shFileInfo;
    DWORD_PTR dwResult;

    shFileInfo.szDisplayName[0] = 0;        // Terminate so we can always thunk afterward.
    shFileInfo.szTypeName[0] = 0;           // Terminate so we can always thunk afterward.

    // Do we need to thunk the Path?
    if (SHGFI_PIDL & uFlags)
    {
        // No, because it's really a pidl pointer.
        dwResult = SHGetFileInfoA((LPCSTR)pwzPath, dwFileAttributes, &shFileInfo, sizeof(shFileInfo), uFlags);
    }
    else
    {
        // Yes
        CStrIn strPath(pwzPath);
        dwResult = SHGetFileInfoA(strPath, dwFileAttributes, &shFileInfo, sizeof(shFileInfo), uFlags);
    }

    psfi->hIcon = shFileInfo.hIcon;
    psfi->iIcon = shFileInfo.iIcon;
    psfi->dwAttributes = shFileInfo.dwAttributes;
    SHAnsiToUnicode(shFileInfo.szDisplayName, psfi->szDisplayName, ARRAYSIZE(shFileInfo.szDisplayName));
    SHAnsiToUnicode(shFileInfo.szTypeName, psfi->szTypeName, ARRAYSIZE(shFileInfo.szTypeName));

    return dwResult;
}

#endif // NEED_SHELL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

STDAPI_(ATOM) RegisterClassExWrapW(CONST WNDCLASSEXW FAR * pwcx)
{
    VALIDATE_PROTOTYPE(RegisterClassEx);
    if (g_bRunningOnNT)
        return RegisterClassExW(pwcx);

    CStrIn strMenuName(pwcx->lpszMenuName);
    CStrIn strClassName(pwcx->lpszClassName);
    WNDCLASSEXA wcx = *(CONST WNDCLASSEXA FAR *) pwcx;
    wcx.cbSize = sizeof(wcx);
    wcx.lpszMenuName = strMenuName;
    wcx.lpszClassName = strClassName;

    return RegisterClassExA(&wcx);
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

STDAPI_(BOOL) GetClassInfoExWrapW(HINSTANCE hinst, LPCWSTR pwzClass, LPWNDCLASSEXW lpwcx)
{
    VALIDATE_PROTOTYPE(GetClassInfoEx);
    if (g_bRunningOnNT)
        return GetClassInfoExW(hinst, pwzClass, lpwcx);

    BOOL fResult;
    CStrIn strClassName(pwzClass);
    WNDCLASSEXA wcx;
    wcx.cbSize = sizeof(wcx);

    fResult = GetClassInfoExA(hinst, strClassName, &wcx);
    *(WNDCLASSEXA FAR *) lpwcx = wcx;
    lpwcx->lpszMenuName = NULL;        // GetClassInfoExA makes this point off to private data that they own.
    lpwcx->lpszClassName = pwzClass;

    return fResult;
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

//+---------------------------------------------------------------------------
//      StartDoc
//----------------------------------------------------------------------------

int
StartDocWrapW( HDC hDC, const DOCINFO * lpdi )
{
    VALIDATE_PROTOTYPE(StartDoc);

    if (g_bRunningOnNT)
    {
        return StartDocW( hDC, lpdi );
    }

    CStrIn  strDocName( lpdi->lpszDocName );
    CStrIn  strOutput( lpdi->lpszOutput );
    CStrIn  strDatatype( lpdi->lpszDatatype );
    DOCINFOA dia;

    dia.cbSize = sizeof(DOCINFO);
    dia.lpszDocName = strDocName;
    dia.lpszOutput = strOutput;
    dia.lpszDatatype = strDatatype;
    dia.fwType = lpdi->fwType;

    return StartDocA( hDC, &dia );
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_GDI32_WRAPPER

STDAPI_(HCOLORSPACE)
CreateColorSpaceWrapW(LOGCOLORSPACEW * lpLogColorSpaceW)
{
    VALIDATE_PROTOTYPE(CreateColorSpace);

    if (g_bRunningOnNT)
    {
        return CreateColorSpaceW(lpLogColorSpaceW);
    }

    HCOLORSPACE     hColorSpace;
    LOGCOLORSPACEA  logColorSpaceA;

    memcpy(&logColorSpaceA, lpLogColorSpaceW, 
           FIELD_OFFSET(LOGCOLORSPACEA, lcsFilename));

    SHUnicodeToAnsi(lpLogColorSpaceW->lcsFilename, 
                    logColorSpaceA.lcsFilename, 
                    ARRAYSIZE(logColorSpaceA.lcsFilename));

    hColorSpace = CreateColorSpaceA(&logColorSpaceA);

    return hColorSpace;
}

#endif // NEED_GDI32_WRAPPER

#ifdef NEED_SHELL32_WRAPPER

STDAPI_(UINT) DragQueryFileWrapW(HDROP hDrop, UINT iFile, LPWSTR lpszFile, UINT cch)
{
    VALIDATE_OUTBUF(lpszFile, cch);

    //
    //  We are lazy and do not support lpszFile == NULL to query the length
    //  of an individual string.
    //
    ASSERT(iFile == 0xFFFFFFFF || lpszFile);

    if (g_bRunningOnNT)
    {
        return DragQueryFileW(hDrop, iFile, lpszFile, cch);
    }

    //
    //  If iFile is 0xFFFFFFFF, then lpszFile and cch are ignored.
    //
    if (iFile == 0xFFFFFFFF)
    {
        return DragQueryFileA(hDrop, iFile, NULL, 0);
    }

    CStrOut str(lpszFile, cch);

    DragQueryFileA(hDrop, iFile, str, str.BufSize());
    return str.ConvertExcludingNul();
}

#endif // NEED_SHELL32_WRAPPER

#ifdef NEED_VERSION_WRAPPER

//
//  the version APIs are not conducive to using
//  wrap versions of the APIs, but we are going to
//  do something reasonable....
//
#define VERSIONINFO_BUFF   (MAX_PATH * SIZEOF(WCHAR))

STDAPI_(DWORD)
GetFileVersionInfoSizeWrapW(LPWSTR pwzFilename,  LPDWORD lpdwHandle)
{
    if (g_bRunningOnNT)
    {
        return GetFileVersionInfoSizeW(pwzFilename, lpdwHandle);
    }
    else
    {
        char szFilename[MAX_PATH];
        DWORD dwRet;

        ASSERT(pwzFilename);
        SHUnicodeToAnsi(pwzFilename, szFilename, ARRAYSIZE(szFilename));
        dwRet = GetFileVersionInfoSizeA(szFilename, lpdwHandle);
        if (dwRet > 0)
        {
            // Add a scratch buffer to front for converting to UNICODE
            dwRet += VERSIONINFO_BUFF;
        }
        return dwRet;
    }
}

#endif // NEED_VERSION_WRAPPER

#ifdef NEED_VERSION_WRAPPER

STDAPI_(BOOL)
GetFileVersionInfoWrapW(LPWSTR pwzFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
{
    if (g_bRunningOnNT)
    {
        return GetFileVersionInfoW(pwzFilename, dwHandle, dwLen, lpData);
    }
    else
    {
        char szFilename[MAX_PATH];
        BYTE* pb;

        if (dwLen <= VERSIONINFO_BUFF)
        {
            return FALSE;
        }

        ASSERT(pwzFilename);
        SHUnicodeToAnsi(pwzFilename, szFilename, ARRAYSIZE(szFilename));
        //Skip over our scratch buffer at the beginning
        pb = (BYTE*)lpData + VERSIONINFO_BUFF;

        return GetFileVersionInfoA(szFilename, dwHandle, dwLen - VERSIONINFO_BUFF, (void*)pb);
    }
}

#endif // NEED_VERSION_WRAPPER

#ifdef NEED_VERSION_WRAPPER

STDAPI_(BOOL)
VerQueryValueWrapW(const LPVOID pBlock, LPWSTR pwzSubBlock, LPVOID *ppBuffer, PUINT puLen)
{
    if (g_bRunningOnNT)
    {
        return VerQueryValueW(pBlock, pwzSubBlock, ppBuffer, puLen);
    }
    else
    {
        const WCHAR pwzStringFileInfo[] = L"\\StringFileInfo";

        //
        // WARNING: This function wipes out any string previously returned
        // for this pBlock because a common buffer at the beginning of the
        // block is used for ansi/unicode translation!
        //
        char szSubBlock[MAX_PATH];
        BOOL fRet;
        BYTE* pb;

        ASSERT(pwzSubBlock);
        SHUnicodeToAnsi(pwzSubBlock, szSubBlock, ARRAYSIZE(szSubBlock));

        // The first chunk is our scratch buffer for converting to UNICODE
        pb = (BYTE*)pBlock + VERSIONINFO_BUFF;
        fRet = VerQueryValueA((void*)pb, szSubBlock, ppBuffer, puLen);

        // Convert to unicode if ansi string returned
        if (fRet && StrCmpNIW(pwzSubBlock, pwzStringFileInfo, ARRAYSIZE(pwzStringFileInfo) - 1) == 0)
        {
            // Convert returned string to UNICODE.  We use the scratch buffer
            // at the beginning of pBlock
            LPWSTR pwzBuff = (LPWSTR)pBlock;
            if (*puLen == 0)
            {
                pwzBuff[0] = L'\0';
            }
            else
            {
                SHAnsiToUnicode((LPCSTR)*ppBuffer, pwzBuff, VERSIONINFO_BUFF/sizeof(WCHAR));
            }
            *ppBuffer = pwzBuff;
        }
        return fRet;
    }
}

#endif // NEED_VERSION_WRAPPER


#ifdef NEED_SHELL32_WRAPPER

HRESULT WINAPI SHDefExtractIconWrapW(LPCWSTR pszFile, int nIconIndex,
                                     UINT uFlags, HICON *phiconLarge,
                                     HICON *phiconSmall, UINT nIconSize)
{
    HRESULT hr;

    if (UseUnicodeShell32())
    {
        hr = SHDefExtractIconW(pszFile, nIconIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
    }
    else
    {
        CStrIn striFile(pszFile);

        hr = SHDefExtractIconA(striFile, nIconIndex, uFlags, phiconLarge, phiconSmall, nIconSize);
    }

    return hr;
}

#endif // NEED_SHELL32_WRAPPER


BOOL WINAPI SHGetNewLinkInfoWrapW(LPCWSTR pszpdlLinkTo, LPCWSTR pszDir, LPWSTR pszName, BOOL *pfMustCopy, UINT uFlags)
{
    BOOL fRet;

    if (g_bRunningOnNT5OrHigher)
    {
        fRet =  SHGetNewLinkInfoW(pszpdlLinkTo, pszDir, pszName, pfMustCopy, uFlags);
    }
    else
    {
        CStrIn  striDir(pszDir);
        CStrOut stroName(pszName, MAX_PATH);

        if (SHGNLI_PIDL & uFlags)
        {
            fRet = SHGetNewLinkInfoA((LPCSTR)pszpdlLinkTo, striDir, stroName, pfMustCopy, uFlags);
        }
        else
        {
            CStrIn striLinkTo(pszpdlLinkTo);

            fRet = SHGetNewLinkInfoA(striLinkTo, striDir, stroName, pfMustCopy, uFlags);
        }

        if (fRet)
        {
            stroName.ConvertIncludingNul();
        }
    }
    
    return fRet;
}


#ifdef NEED_ADVAPI32_WRAPPER

LONG WINAPI RegEnumValueWrapW(HKEY hkey, DWORD dwIndex, LPWSTR lpValueName,
                              LPDWORD lpcbValueName, LPDWORD lpReserved,
                              LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData)
{
    VALIDATE_PROTOTYPE(RegEnumValue);

    LONG lRet;

    if (UseUnicodeShell32())
    {
        lRet = RegEnumValueW(hkey, dwIndex, lpValueName, lpcbValueName,
                             lpReserved, lpType, lpData, lpcbData);
    }
    else
    {
        CStrOut stroValueName(lpValueName, *lpcbValueName);
        DWORD   dwTypeTemp;

        if (lpData)
        {
            ASSERT(lpcbData);

            CStrOut stroData((LPWSTR)lpData, (*lpcbData) / sizeof(WCHAR));

            lRet = RegEnumValueA(hkey, dwIndex, stroValueName, lpcbValueName,
                                 lpReserved, &dwTypeTemp,
                                 (LPBYTE)(LPSTR)stroData, lpcbData);

            if (ERROR_SUCCESS == lRet && REG_SZ == dwTypeTemp)
            {
                *lpcbData = sizeof(WCHAR) * stroData.ConvertIncludingNul();
            }
        }
        else
        {
            lRet = RegEnumValueA(hkey, dwIndex, stroValueName, lpcbValueName,
                                 lpReserved, &dwTypeTemp, lpData, lpcbData);
        }

        if (ERROR_SUCCESS == lRet)
            *lpcbValueName = stroValueName.ConvertExcludingNul();

        if (lpType)
            *lpType = dwTypeTemp;
    }

    return lRet;
}

#endif // NEED_ADVAPI32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI WritePrivateProfileStructWrapW(LPCWSTR lpszSection, LPCWSTR lpszKey,
                                           LPVOID lpStruct, UINT uSizeStruct,
                                           LPCWSTR szFile)
{
    VALIDATE_PROTOTYPE(WritePrivateProfileStruct);

    BOOL fRet;

    if (UseUnicodeShell32())
    {
        fRet = WritePrivateProfileStructW(lpszSection, lpszKey, lpStruct,
                                          uSizeStruct, szFile);
    }
    else
    {
        CStrIn striSection(lpszSection);
        CStrIn striKey(lpszKey);
        CPPFIn striFile(szFile); // PrivateProfile filename needs special class

        fRet = WritePrivateProfileStructA(striSection, striKey, lpStruct,
                                          uSizeStruct, striFile);
    }

    return fRet;
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI GetPrivateProfileStructWrapW(LPCWSTR lpszSection, LPCWSTR lpszKey,
                                         LPVOID lpStruct, UINT uSizeStruct,
                                         LPCWSTR szFile)
{
    VALIDATE_PROTOTYPE(GetPrivateProfileStruct);

    BOOL fRet;

    if (UseUnicodeShell32())
    {
        fRet = GetPrivateProfileStructW(lpszSection, lpszKey, lpStruct,
                                        uSizeStruct, szFile);
    }
    else
    {
        CStrIn striSection(lpszSection);
        CStrIn striKey(lpszKey);
        CPPFIn striFile(szFile); // PrivateProfile filename needs special class

        fRet = GetPrivateProfileStructA(striSection, striKey, lpStruct,
                                        uSizeStruct, striFile);
    }

    return fRet;
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

BOOL WINAPI CreateProcessWrapW(LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
                               LPSECURITY_ATTRIBUTES lpProcessAttributes,
                               LPSECURITY_ATTRIBUTES lpThreadAttributes,
                               BOOL bInheritHandles,
                               DWORD dwCreationFlags,
                               LPVOID lpEnvironment,
                               LPCWSTR lpCurrentDirectory,
                               LPSTARTUPINFOW lpStartupInfo,
                               LPPROCESS_INFORMATION lpProcessInformation)
{
    BOOL fRet;
    VALIDATE_PROTOTYPE(CreateProcess);

    if (UseUnicodeShell32())
    {
        fRet = CreateProcessW(lpApplicationName, lpCommandLine,
                              lpProcessAttributes, lpThreadAttributes,
                              bInheritHandles, dwCreationFlags, lpEnvironment,
                              lpCurrentDirectory, lpStartupInfo,
                              lpProcessInformation);
    }
    else
    {
        CStrIn striApplicationName(lpApplicationName);
        CStrIn striCommandLine(lpCommandLine);
        CStrIn striCurrentDirectory(lpCurrentDirectory);

        if (NULL == lpStartupInfo)
        {
            fRet = CreateProcessA(striApplicationName, striCommandLine,
                                  lpProcessAttributes, lpThreadAttributes,
                                  bInheritHandles, dwCreationFlags,
                                  lpEnvironment, striCurrentDirectory,
                                  NULL, lpProcessInformation);
        }
        else
        {
            STARTUPINFOA si = *(STARTUPINFOA*)lpStartupInfo;

            CStrIn striReserved(lpStartupInfo->lpReserved);
            CStrIn striDesktop(lpStartupInfo->lpDesktop);
            CStrIn striTitle(lpStartupInfo->lpTitle);

            si.lpReserved = striReserved;
            si.lpDesktop  = striDesktop;
            si.lpTitle   = striTitle;

            fRet = CreateProcessA(striApplicationName, striCommandLine,
                                  lpProcessAttributes, lpThreadAttributes,
                                  bInheritHandles, dwCreationFlags,
                                  lpEnvironment, striCurrentDirectory,
                                  &si, lpProcessInformation);
        }

    }

    return fRet;
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_SHELL32_WRAPPER

HICON WINAPI ExtractIconWrapW(HINSTANCE hInst, LPCWSTR lpszExeFileName, UINT nIconIndex)
{
    HICON hicon;

    if (UseUnicodeShell32())
    {
        hicon = ExtractIconW(hInst, lpszExeFileName, nIconIndex);
    }
    else
    {
        CStrIn striExeFileName(lpszExeFileName);

        hicon = ExtractIconA(hInst, striExeFileName, nIconIndex);
    }

    return hicon;
}

#endif // NEED_SHELL32_WRAPPER

#ifdef NEED_USER32_WRAPPER

UINT WINAPI DdeInitializeWrapW(LPDWORD pidInst, PFNCALLBACK pfnCallback,
                               DWORD afCmd, DWORD ulRes)
{
    UINT uRet;

    if (UseUnicodeShell32())
    {
        uRet = DdeInitializeW(pidInst, pfnCallback, afCmd, ulRes);
    }
    else
    {
        //
        // This assumes the callback function will used the wrapped dde
        // string functions (DdeCreateStringHandle and DdeQueryString)
        // to access strings.
        //

        uRet = DdeInitializeA(pidInst, pfnCallback, afCmd, ulRes);
    }

    return uRet;
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

HSZ WINAPI DdeCreateStringHandleWrapW(DWORD idInst, LPCWSTR psz, int iCodePage)
{
    HSZ hszRet;

    if (UseUnicodeShell32())
    {
        hszRet = DdeCreateStringHandleW(idInst, psz, iCodePage);
    }
    else
    {
        CStrIn stripsz(psz);

        hszRet = DdeCreateStringHandleA(idInst, stripsz, CP_WINANSI);
    }

    return hszRet;
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_USER32_WRAPPER

DWORD WINAPI DdeQueryStringWrapW(DWORD idInst, HSZ hsz, LPWSTR psz,
                                 DWORD cchMax, int iCodePage)
{
    DWORD dwRet;

    if (UseUnicodeShell32())
    {
        dwRet = DdeQueryStringW(idInst, hsz, psz, cchMax, iCodePage);
    }
    else
    {
        CStrOut stropsz(psz, cchMax);

        dwRet = DdeQueryStringA(idInst, hsz, stropsz, stropsz.BufSize(),
                                CP_WINANSI);

        if (dwRet && psz)
            dwRet = stropsz.ConvertExcludingNul();        
    }

    return dwRet;
}

#endif // NEED_USER32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

ATOM WINAPI
GlobalAddAtomWrapW(LPCWSTR lpString)
{
    VALIDATE_PROTOTYPE(GlobalAddAtom);

    if (g_bRunningOnNT)
    {
        return GlobalAddAtomW(lpString);
    }

    CStrIn  str(lpString);

    return GlobalAddAtomA(str);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_KERNEL32_WRAPPER

ATOM WINAPI
GlobalFindAtomWrapW(LPCWSTR lpString)
{
    VALIDATE_PROTOTYPE(GlobalFindAtom);

    if (g_bRunningOnNT)
    {
        return GlobalFindAtomW(lpString);
    }

    CStrIn  str(lpString);

    return GlobalFindAtomA(str);
}

#endif // NEED_KERNEL32_WRAPPER

#ifdef NEED_COMDLG32_WRAPPER

BOOL WINAPI GetSaveFileNameWrapW(LPOPENFILENAMEW lpofn)
{
    BOOL fRet;

    if (UseUnicodeShell32())
    {
        fRet = GetSaveFileNameW(lpofn);
    }
    else
    {
        ASSERT(lpofn);
        ASSERT(sizeof(OPENFILENAMEA) == sizeof(OPENFILENAMEW));

        OPENFILENAMEA ofnA = *(LPOPENFILENAMEA)lpofn;

        // In parameters
        CStrInMulti strimFilter(lpofn->lpstrFilter);
        CStrIn      striInitialDir(lpofn->lpstrInitialDir);
        CStrIn      striTitle(lpofn->lpstrTitle);
        CStrIn      striDefExt(lpofn->lpstrDefExt);
        CStrIn      striTemplateName(lpofn->lpTemplateName);

        ASSERT(NULL == lpofn->lpstrCustomFilter); // add support if you need it.

        // Out parameters
        CStrOut     stroFile(lpofn->lpstrFile, lpofn->nMaxFile);
        CStrOut     stroFileTitle(lpofn->lpstrFileTitle, lpofn->nMaxFileTitle);

        //In Out parameters
        SHUnicodeToAnsi(lpofn->lpstrFile, stroFile, stroFile.BufSize());

        // Set up the parameters
        ofnA.lpstrFilter        = strimFilter;
        ofnA.lpstrInitialDir    = striInitialDir;
        ofnA.lpstrTitle         = striTitle;
        ofnA.lpstrDefExt        = striDefExt;
        ofnA.lpTemplateName     = striTemplateName;
        ofnA.lpstrFile          = stroFile;
        ofnA.lpstrFileTitle     = stroFileTitle;

        fRet = GetSaveFileNameA(&ofnA);

        if (fRet)
        {
            // Copy the out parameters
            lpofn->nFilterIndex = ofnA.nFilterIndex;
            lpofn->Flags        = ofnA.Flags;

            // Get the offset to the filename
            stroFile.ConvertIncludingNul();
            LPWSTR psz = PathFindFileNameW(lpofn->lpstrFile);

            if (psz)
            {
                lpofn->nFileOffset = (int) (psz-lpofn->lpstrFile);

                // Get the offset of the extension
                psz = PathFindExtensionW(psz);

                lpofn->nFileExtension = psz ? (int)(psz-lpofn->lpstrFile) : 0; 
            }
            else
            {
                lpofn->nFileOffset    = 0;
                lpofn->nFileExtension = 0;
            }

        }
    }

    return fRet;
}

#endif // NEED_COMDLG32_WRAPPER

#ifdef NEED_COMDLG32_WRAPPER

BOOL WINAPI GetOpenFileNameWrapW(LPOPENFILENAMEW lpofn)
{
    BOOL fRet;

    if (UseUnicodeShell32())
    {
        fRet = GetOpenFileNameW(lpofn);
    }
    else
    {
        ASSERT(lpofn);
        ASSERT(sizeof(OPENFILENAMEA) == sizeof(OPENFILENAMEW));

        OPENFILENAMEA ofnA = *(LPOPENFILENAMEA)lpofn;

        // In parameters
        CStrInMulti strimFilter(lpofn->lpstrFilter);
        CStrIn      striInitialDir(lpofn->lpstrInitialDir);
        CStrIn      striTitle(lpofn->lpstrTitle);
        CStrIn      striDefExt(lpofn->lpstrDefExt);
        CStrIn      striTemplateName(lpofn->lpTemplateName);

        ASSERT(NULL == lpofn->lpstrCustomFilter); // add support if you need it.

        // Out parameters
        CStrOut     stroFile(lpofn->lpstrFile, lpofn->nMaxFile);
        CStrOut     stroFileTitle(lpofn->lpstrFileTitle, lpofn->nMaxFileTitle);

        //In Out parameters
        SHUnicodeToAnsi(lpofn->lpstrFile, stroFile, stroFile.BufSize());

        // Set up the parameters
        ofnA.lpstrFilter        = strimFilter;
        ofnA.lpstrInitialDir    = striInitialDir;
        ofnA.lpstrTitle         = striTitle;
        ofnA.lpstrDefExt        = striDefExt;
        ofnA.lpTemplateName     = striTemplateName;
        ofnA.lpstrFile          = stroFile;
        ofnA.lpstrFileTitle     = stroFileTitle;

        fRet = GetOpenFileNameA(&ofnA);

        if (fRet)
        {
            // Copy the out parameters
            lpofn->nFilterIndex = ofnA.nFilterIndex;
            lpofn->Flags        = ofnA.Flags;

            // Get the offset to the filename
            stroFile.ConvertIncludingNul();
            LPWSTR psz = PathFindFileNameW(lpofn->lpstrFile);

            if (psz)
            {
                lpofn->nFileOffset = (int) (psz-lpofn->lpstrFile);

                // Get the offset of the extension
                psz = PathFindExtensionW(psz);

                lpofn->nFileExtension = psz ? (int)(psz-lpofn->lpstrFile) : 0; 
            }
            else
            {
                lpofn->nFileOffset    = 0;
                lpofn->nFileExtension = 0;
            }

        }
    }

    return fRet;
}

#endif // NEED_COMDLG32_WRAPPER

#ifdef NEED_SHELL32_WRAPPER

#define SHCNF_HAS_WSTR_PARAMS(f)   ((f & SHCNF_TYPE) == SHCNF_PATHW     ||    \
                                    (f & SHCNF_TYPE) == SHCNF_PRINTERW  ||    \
                                    (f & SHCNF_TYPE) == SHCNF_PRINTJOBW    )

void SHChangeNotifyWrap(LONG wEventId, UINT uFlags, LPCVOID dwItem1,
                        LPCVOID dwItem2)
{
    if (UseUnicodeShell32() || !SHCNF_HAS_WSTR_PARAMS(uFlags))
    {
        SHChangeNotify(wEventId, uFlags, dwItem1, dwItem2);
    }
    else
    {
        CStrIn striItem1((LPWSTR)dwItem1);
        CStrIn striItem2((LPWSTR)dwItem2);
        
        if ((uFlags & SHCNF_TYPE) == SHCNF_PATHW)
        {
            uFlags = (uFlags & ~SHCNF_TYPE) | SHCNF_PATHA;
        }
        else if ((uFlags & SHCNF_TYPE) == SHCNF_PRINTERW)
        {
            uFlags = (uFlags & ~SHCNF_TYPE) | SHCNF_PRINTERA;
        }
        else
        {
            uFlags = (uFlags & ~SHCNF_TYPE) | SHCNF_PRINTJOBA;
        }

        SHChangeNotify(wEventId, uFlags, (void*)(LPSTR)striItem1, (void*)(LPSTR)striItem2);
    }

    return;
}

LWSTDAPI_(void) SHFlushSFCacheWrap(void)
{
    // if shell32 is not in process, then there's nothing to flush
    HMODULE hShell32 = GetModuleHandleWrap(TEXT("SHELL32"));
    if (hShell32) 
    {
        // NOTE: GetProcAddress always takes ANSI strings!
        DLLGETVERSIONPROC pfnGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hShell32, "DllGetVersion");

        if (pfnGetVersion)
        {
            DLLVERSIONINFO dllinfo;

            dllinfo.cbSize = sizeof(DLLVERSIONINFO);
            if (pfnGetVersion(&dllinfo) == NOERROR)
            {
                // first, we need a version of shell32 that supports the function
                if (dllinfo.dwMajorVersion >= 4)
                {
                    if (dllinfo.dwMajorVersion == 4)
                    {
                        // to work around the "missing critical section" bug in v4 shell32.dll,
                        // we simply bail the call.
                        return;
                    }

                    SHFlushSFCache();
                }
            }
        }
    }
}

#endif // NEED_SHELL32_WRAPPER

#ifdef NEED_COMDLG32_WRAPPER

//+---------------------------------------------------------------------------
// PrintDlgWrap, PageSetupDlgWrap - wrappers
// DevNamesAFromDevNamesW, DevNamesWFromDevNamesA - helper functions
//
//        Copied from mshtml\src\core\wrappers\unicwrap.cpp with some
//        cosmetic changes (peterlee)
//        
//+---------------------------------------------------------------------------

HGLOBAL
DevNamesAFromDevNamesW( HGLOBAL hdnw )
{
    HGLOBAL         hdna = NULL;

    if (hdnw)
    {
        LPDEVNAMES lpdnw = (LPDEVNAMES) GlobalLock( hdnw );
        if (lpdnw)
        {
            CStrIn      strDriver( (LPCWSTR) lpdnw + lpdnw->wDriverOffset );
            CStrIn      strDevice( (LPCWSTR) lpdnw + lpdnw->wDeviceOffset );
            CStrIn      strOutput( (LPCWSTR) lpdnw + lpdnw->wOutputOffset );
            int         cchDriver = strDriver.strlen() + 1;
            int         cchDevice = strDevice.strlen() + 1;
            int         cchOutput = strOutput.strlen() + 1;

            hdna = GlobalAlloc( GHND, sizeof(DEVNAMES) +
                                cchDriver + cchDevice + cchOutput );
            if (hdna)
            {
                LPDEVNAMES lpdna = (LPDEVNAMES) GlobalLock( hdna );
                if (!lpdna)
                {
                    GlobalFree( hdna );
                    hdna = NULL;
                }
                else
                {
                    lpdna->wDriverOffset = sizeof(DEVNAMES);
                    lpdna->wDeviceOffset = lpdna->wDriverOffset + cchDriver;
                    lpdna->wOutputOffset = lpdna->wDeviceOffset + cchDevice;
                    lpdna->wDefault = lpdnw->wDefault;

                    lstrcpyA( (LPSTR) lpdna + lpdna->wDriverOffset, strDriver );
                    lstrcpyA( (LPSTR) lpdna + lpdna->wDeviceOffset, strDevice );
                    lstrcpyA( (LPSTR) lpdna + lpdna->wOutputOffset, strOutput );

                    GlobalUnlock( hdna );
                }
            }

            GlobalUnlock( hdnw );
            GlobalFree( hdnw );
        }
    }

   return hdna;
}

HGLOBAL
DevNamesWFromDevNamesA( HGLOBAL hdna )
{
    HGLOBAL         hdnw = NULL;

    if (hdna)
    {
        LPDEVNAMES lpdna = (LPDEVNAMES) GlobalLock( hdna );
        if (lpdna)
        {
            LPCSTR      lpszDriver = (LPCSTR) lpdna + lpdna->wDriverOffset;
            LPCSTR      lpszDevice = (LPCSTR) lpdna + lpdna->wDeviceOffset;
            LPCSTR      lpszOutput = (LPCSTR) lpdna + lpdna->wOutputOffset;
            int         cchDriver = lstrlenA( lpszDriver ) + 1;
            int         cchDevice = lstrlenA( lpszDevice ) + 1;
            int         cchOutput = lstrlenA( lpszOutput ) + 1;

            // assume the wide charcount won't exceed the multibyte charcount

            hdnw = GlobalAlloc( GHND, sizeof(DEVNAMES) +
                                sizeof(WCHAR) * (cchDriver + cchDevice + cchOutput)  );
            if (hdnw)
            {
                LPDEVNAMES lpdnw = (LPDEVNAMES) GlobalLock( hdnw );
                if (!lpdnw)
                {
                    GlobalFree( hdnw );
                    hdnw = NULL;
                }
                else
                {
                    lpdnw->wDriverOffset = sizeof(DEVNAMES) / sizeof(WCHAR);
                    lpdnw->wDeviceOffset = lpdnw->wDriverOffset + cchDriver;
                    lpdnw->wOutputOffset = lpdnw->wDeviceOffset + cchDevice;
                    lpdnw->wDefault = lpdna->wDefault;

                    SHAnsiToUnicode( (LPSTR) lpszDriver, (LPWSTR) lpdnw + lpdnw->wDriverOffset,
                                     cchDriver );
                    SHAnsiToUnicode( lpszDevice, (LPWSTR) lpdnw + lpdnw->wDeviceOffset,
                                     cchDevice);
                    SHAnsiToUnicode( lpszOutput, (LPWSTR) lpdnw + lpdnw->wOutputOffset,
                                     cchOutput);

                    GlobalUnlock( hdnw );
                }
            }

            GlobalUnlock( hdna );
            GlobalFree( hdna );
        }
    }

   return hdnw;
}

#ifdef UNIX
HGLOBAL
DevModeAFromDevModeW( HGLOBAL hdmw )
{
    HGLOBAL         hdma = NULL;

    if (hdmw)
    {
        LPDEVMODEW lpdmw = (LPDEVMODEW)GlobalLock( hdmw );
    if (lpdmw)
    {
        hdma = GlobalAlloc( GHND, sizeof(DEVMODEA) );
        if (hdma)
        {
            LPDEVMODEA lpdma = (LPDEVMODEA) GlobalLock( hdma );
        if (lpdma)
        {
            CStrIn          strDeviceName( lpdmw->dmDeviceName );
            CStrIn          strFormName( lpdmw->dmFormName );

            // assume memory layout is identical

            memcpy( lpdma->dmDeviceName, strDeviceName, CCHDEVICENAME );

            memcpy( &lpdma->dmSpecVersion,
                &lpdmw->dmSpecVersion,
                offsetof(DEVMODEW, dmFormName) -
                offsetof(DEVMODEW, dmSpecVersion) );

            memcpy( lpdma->dmFormName, strFormName, CCHFORMNAME );

            memcpy( &lpdma->dmLogPixels,
                &lpdmw->dmLogPixels,
                sizeof(DEVMODEW) -
                offsetof(DEVMODEW, dmLogPixels) );

            GlobalUnlock( hdma );
        }
        else
        {
            GlobalFree( hdma );
            hdma = NULL;
        }
        }

        GlobalUnlock( hdmw );
        GlobalFree( hdmw );
    }
    }

    return hdma;
}

//--------------------------------------------------------------
//      DEVMODEW from DEVMODEA
//--------------------------------------------------------------
HGLOBAL
DevModeWFromDevModeA( HGLOBAL hdma )
{
    HGLOBAL         hdmw = NULL;

    if (hdma)
    {
        LPDEVMODEA lpdma = (LPDEVMODEA)GlobalLock( hdma );
    if (lpdma)
    {
        hdmw = GlobalAlloc( GHND, sizeof(DEVMODEW) );
        if (hdmw)
        {
            LPDEVMODEW lpdmw = (LPDEVMODEW) GlobalLock( hdmw );
        if (lpdmw)
        {
            CStrOut     strDeviceName( lpdmw->dmDeviceName, CCHDEVICENAME );
            CStrOut     strFormName( lpdmw->dmFormName, CCHFORMNAME );

            // assume memory layout is identical

            lstrcpyA( strDeviceName, (LPCSTR)lpdma->dmDeviceName );
            strDeviceName.ConvertIncludingNul();

            memcpy( &lpdmw->dmSpecVersion,
                &lpdma->dmSpecVersion,
                offsetof(DEVMODEA, dmFormName) -
                offsetof(DEVMODEA, dmSpecVersion) );

            lstrcpyA( strFormName, (LPCSTR)lpdmw->dmFormName );
            strFormName.ConvertIncludingNul();

            memcpy( &lpdmw->dmLogPixels,
                &lpdma->dmLogPixels,
                sizeof(DEVMODEA) -
                offsetof(DEVMODEA, dmLogPixels) );

            GlobalUnlock( hdmw );
        }
        else
        {
            GlobalFree( hdmw );
            hdmw = NULL;
        }
        }

        GlobalUnlock( hdma );
        GlobalFree( hdma );
    }
    }

    return hdmw;
}
#endif //UNIX
#endif // NEED_COMDLG32_WRAPPER

#ifdef NEED_COMDLG32_WRAPPER

//--------------------------------------------------------------
//      PrintDlgW wrapper
//--------------------------------------------------------------

BOOL WINAPI
PrintDlgWrapW(LPPRINTDLGW lppd)
{
    BOOL        fRet;
    
    if (UseUnicodeShell32())
    {
         fRet = PrintDlgW(lppd);
    }
    else
    {    
        PRINTDLGA   pda;
        LPCWSTR     lpPrintTemplateName = lppd->lpPrintTemplateName;
        LPCWSTR     lpSetupTemplateName = lppd->lpSetupTemplateName;
        CStrIn      strPrintTemplateName( lpPrintTemplateName );
        CStrIn      strSetupTemplateName( lpSetupTemplateName );

        ASSERT( sizeof(pda) == sizeof( *lppd ));

        memcpy( &pda, lppd, sizeof(pda) );

        // IMPORTANT: We are not converting the DEVMODE structure back and forth
        // from ASCII to Unicode on Win95 anymore because we are not touching the
        // two strings or any other member.  Converting the DEVMODE structure can
        // be tricky because of potential and common discrepancies between the
        // value of the dmSize member and sizeof(DEVMODE).  (25155)
    
        // So instead of: pda.hDevMode = DevModeAFromDevModeW( lppd->hDevMode );
        // we just forward the DEVMODE handle:
        pda.hDevMode = lppd->hDevMode;
        pda.hDevNames = DevNamesAFromDevNamesW( lppd->hDevNames );
        pda.lpPrintTemplateName = strPrintTemplateName;
        pda.lpSetupTemplateName = strSetupTemplateName;
    
        fRet = PrintDlgA( &pda );
    
        // copy back wholesale, then restore strings.
    
        memcpy( lppd, &pda, sizeof(pda) );
    
        lppd->lpSetupTemplateName = lpSetupTemplateName;
        lppd->lpPrintTemplateName = lpPrintTemplateName;
        lppd->hDevNames = DevNamesWFromDevNamesA( pda.hDevNames );
    
        // And instead of: lppd->hDevMode = DevModeWFromDevModeA( pda.hDevMode );
        // we just forward the DEVMODE handle:
        lppd->hDevMode = pda.hDevMode;
    }

    return fRet;
}

#endif // NEED_COMDLG32_WRAPPER

#ifdef NEED_COMDLG32_WRAPPER

//--------------------------------------------------------------
//      PageSetupDlgW wrapper
//--------------------------------------------------------------

BOOL WINAPI
PageSetupDlgWrapW(LPPAGESETUPDLGW lppsd)
{
    BOOL fRet;

    if (UseUnicodeShell32())
    {
        fRet = PageSetupDlgW(lppsd);
    }
    else
    {   
        PAGESETUPDLGA   psda;
        LPCWSTR         lpPageSetupTemplateName = lppsd->lpPageSetupTemplateName;
        CStrIn          strPageSetupTemplateName( lpPageSetupTemplateName );
    
        ASSERT( sizeof(psda) == sizeof( *lppsd ) );
    
        memcpy( &psda, lppsd, sizeof(psda));
    
        // IMPORTANT: We are not converting the DEVMODE structure back and forth
        // from ASCII to Unicode on Win95 anymore because we are not touching the
        // two strings or any other member.  Converting the DEVMODE structure can
        // be tricky because of potential and common discrepancies between the
        // value of the dmSize member and sizeof(DEVMODE).  (25155)
    
        // So instead of: psda.hDevMode = DevModeAFromDevModeW( lppsd->hDevMode );
        // we just forward the DEVMODE handle:
        psda.hDevMode = lppsd->hDevMode;
        psda.hDevNames = DevNamesAFromDevNamesW( lppsd->hDevNames );
        psda.lpPageSetupTemplateName = strPageSetupTemplateName;
    
        fRet = PageSetupDlgA((LPPAGESETUPDLGA)&psda);
    
        // copy back wholesale, then restore string.
    
        memcpy( lppsd, &psda, sizeof(psda) );
    
        lppsd->lpPageSetupTemplateName = lpPageSetupTemplateName;
        lppsd->hDevNames = DevNamesWFromDevNamesA( psda.hDevNames );
    
        // And instead of: lppsd->hDevMode = DevModeWFromDevModeA( psda.hDevMode );
        // we just forward the DEVMODE handle:
        lppsd->hDevMode = psda.hDevMode;            
    }

    return fRet;
}

#endif // NEED_COMDLG32_WRAPPER

#ifdef NEED_OLE32_WRAPPER

//  CLSIDFromXXX puke when input is >= 248
#define SAFE_OLE_BUF_LEN    247

HRESULT WINAPI CLSIDFromStringWrap(LPOLESTR lpsz, LPCLSID pclsid)
{
    return GUIDFromStringW(lpsz, pclsid) ? S_OK : E_INVALIDARG;
}

HRESULT WINAPI CLSIDFromProgIDWrap(LPCOLESTR lpszProgID, LPCLSID lpclsid)
{
    HRESULT hr;
    
    if (lstrlenW(lpszProgID) < SAFE_OLE_BUF_LEN)
    {
        hr = CLSIDFromProgID(lpszProgID, lpclsid);
    }
    else
    {
        hr = E_INVALIDARG;
    }

    return hr;
}

#endif // NEED_OLE32_WRAPPER

//************************************************************************
// function GETLONGPATHNAME :
//
// These wrappers are needed on all platforms (x86 and Alpha) because they
// implement the functionality for the Kernel32 function GetLongPathName that 
// exists only on Win2K (Unicode/Ansi) and Win98 (Ansi only). Hence these
// wrappers provide this functionality for lower level platforms (NT4 & Win95)
//

#define achGETLONGPATHNAMEA     "GetLongPathNameA"
typedef DWORD (*PROC_GETLONGPATHNAMEA) (LPCSTR, LPSTR, DWORD);

LWSTDAPI_(DWORD)
GetLongPathNameWrapA(
        LPCSTR lpszShortPath,
        LPSTR lpszLongPath, 
        DWORD cchBuffer)
{
    VALIDATE_PROTOTYPE(GetLongPathName);

    // If NT5 or Win98, use the system API for ANSI
    if (g_bRunningOnNT5OrHigher || g_bRunningOnMemphis)
    {
        static PROC_GETLONGPATHNAMEA s_fpGLPNA = NULL;

        if (!s_fpGLPNA)
        {
            // This codepath is used for Memphis also, hence need to use the
            // wrapper for GetModuleHandle.
            s_fpGLPNA = (PROC_GETLONGPATHNAMEA)GetProcAddress(GetModuleHandleWrap(L"kernel32"),achGETLONGPATHNAMEA);
        }

        ASSERT(s_fpGLPNA);
        
        if (s_fpGLPNA)
        {
            return s_fpGLPNA(lpszShortPath, lpszLongPath, cchBuffer);
        }
    }

    // Otherwise use our own logic to do the conversion....
    BOOL    bCountMode = FALSE;
    CHAR    cTmp;
    HANDLE  hFind;
    LPSTR   pTmp = NULL;
    DWORD   dwReturnLen = 0;    
    WIN32_FIND_DATAA Find_Data;  
    UINT    PrevErrorMode;

    // Since we are going to be touchin the media, turn off file error
    // pop-ups. Remember the current mode and set it back in the end.
    PrevErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);

    if (lpszLongPath == NULL)
    {    // No OUT buffer provided ==> counting mode.
        bCountMode = TRUE;
    }
    else
    {    // Initialize OUT buffer.
        *lpszLongPath = '\0';
    }

    // Validate the input parameters...
    if ((lpszShortPath == NULL) 
        || (0xFFFFFFFF == GetFileAttributesA(lpszShortPath)) )
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        dwReturnLen = 0;
        goto Done;
    }         
                                                                    
    if (PathIsUNCA(lpszShortPath))
    {
        // This is a UNC Path, don't know how to handle
        SetLastError(ERROR_INVALID_PARAMETER);
        dwReturnLen = 0;
        goto Done;
    }

    // Input must be a full path.
    // Since Drive letter cannot be multi-byte, just check 2nd and 3rd chars.
    if ( lpszShortPath[1] != ':' || 
         lpszShortPath[2] != '\\')
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        dwReturnLen = 0;
        goto Done;
    }

    // Create the drive root for the LFN.
    if ( !bCountMode && cchBuffer >= 3)
    {   // Copy the drive letter.
        StrCpyNA(lpszLongPath, lpszShortPath, 3);
    }
    else
    {   // Supplied buffer is so small, can't even copy the drive letter!!
        bCountMode = TRUE;
    }
    dwReturnLen += 2;


    // Create a local copy of the input Short name to party on.
    // Also, as per the documentation, OUT buffer can be the same as the IN.
    // Hence need to save the IN buffer before we start filling the OUT.
    CHAR lpLocalCopy[MAX_PATH];
    StrCpyNA(lpLocalCopy, lpszShortPath, MAX_PATH);

    // Now starts the main processing....
    // Skip past the root backslash in lpszShortPath.
    pTmp = lpLocalCopy+3;
 
    while (*pTmp)
    {
        // Get the next Backslash
        pTmp = StrChrA(pTmp, L'\\');
        if ( pTmp == NULL)
        {
            // Fell off the end of the str. So point pTmp to the terminating \0
            pTmp = lpLocalCopy + lstrlenA(lpLocalCopy);
        }

        cTmp = *pTmp;
        *pTmp = '\0';

        hFind = FindFirstFileA(lpLocalCopy, &Find_Data);
        if (hFind != INVALID_HANDLE_VALUE)
        {
            FindClose(hFind);

            dwReturnLen += lstrlenA(Find_Data.cFileName);
            dwReturnLen += 1;  // Plus for the '\' introduced by PathCombine.
            if (!bCountMode && dwReturnLen < cchBuffer)
            {   // Add the LFN to the path
                PathCombineA(lpszLongPath, lpszLongPath, Find_Data.cFileName);
            }
            else
            {   // We are out of buffer space. Continue loop to find space reqd.
                bCountMode = TRUE;
                *lpszLongPath = '\0';
            }
        }
        else
        {
            //Error: Input path does not exist. The earlier check should catch
            //this
            SetLastError(ERROR_INVALID_PARAMETER);
            if (!bCountMode)
                *lpszLongPath = '\0';
            *pTmp = cTmp;
            dwReturnLen = 0;
            goto Done;
        }

        *pTmp = cTmp;
      
        if (*pTmp)
            pTmp = CharNextA(pTmp);
    }

Done:
    // restore error mode.
    SetErrorMode(PrevErrorMode);

    if ( dwReturnLen && bCountMode)
    {   // If everything OK (dwReturnLen!=0) and Counting Mode, add one.
        return (dwReturnLen+1);  
    }
    else
    {   // Return error (dwReturnLen==0) Or 
        // No. of chars (dwReturnLen != 0 && bCountMode == FALSE)
        return (dwReturnLen);   
    }
}

#define achGETLONGPATHNAMEW     "GetLongPathNameW"
typedef DWORD (*PROC_GETLONGPATHNAMEW) (LPCWSTR, LPWSTR, DWORD);

LWSTDAPI_(DWORD)
GetLongPathNameWrapW(
        LPCWSTR lpszShortPath,
        LPWSTR lpszLongPath,
        DWORD cchBuffer)
{
    VALIDATE_PROTOTYPE(GetLongPathName);

    // If we are running on NT5, use the system API for Unicode
    if (g_bRunningOnNT5OrHigher)
    {
        static PROC_GETLONGPATHNAMEW s_fpGLPNW = NULL;

        if (!s_fpGLPNW)
        {
            s_fpGLPNW = (PROC_GETLONGPATHNAMEW)GetProcAddress(GetModuleHandle(TEXT("kernel32")),achGETLONGPATHNAMEW);
        }

        ASSERT(s_fpGLPNW);
        
        if (s_fpGLPNW)
        {
            return s_fpGLPNW(lpszShortPath, lpszLongPath, cchBuffer);
        }
    }

    // All other platforms, convert UNICODE inputs to ANSI and use the
    // ANSI wrapper.
    CStrIn  strShortPath(lpszShortPath);
    CStrOut strLongPath(lpszLongPath, cchBuffer);

    // DWORD dwRet = GetLongPathNameWrapA(strShortPath, strLongPath, strLongPath.BufSize());
    DWORD dwRet = GetLongPathNameWrapA(strShortPath, strLongPath, cchBuffer);
    if (dwRet != 0)
    {   // If the call succeeded, thunk back the size.
        if (dwRet < (DWORD)cchBuffer)
        {   // Succeeded in getting LFN value in the OUT buffer. Thunk it back
            // to Unicode.
            dwRet = strLongPath.ConvertIncludingNul() - 1;
        }
        // else ==> buffer small, need dwRet character space.
    }

    return dwRet;
}

// End of GETLONGPATHNAME wrapper implementation.
// NOTE: This wrapper is to be built on all platforms.
//************************************************************************
