/*++

Copyright (c) 2000-2001,  Microsoft Corporation  All rights reserved.

Module Name:

    usermsgs.c

    APIs found in this file:
        GodotDoCallback
        GodotTransmitMessage
        GodotReceiveMessage
        GodotDispatchMessage

    Helper functions
        MapCatureMessage
        TransmitHelper

        This function does not currently handle ANSI caller to a UNICODE window.
        All other calls are handled properly.


        CONSIDER: To fully implement the capture window stuff, we would
                  need to support these callback functions:

            capErrorCallback
            capStatusCallback

Revision History:

    6 Feb 2001    v-michka    Created.

--*/

#include "precomp.h"

// Internal MFC messages
#define WM_SETMESSAGESTRING 0x0362  // wParam = nIDS (or 0),
                                    // lParam = lpszOther (or NULL)

// Must dynamically link to "BroadcastSystemMessage" because it 
// does not exist as "BroadcastSystemMessageA" on all platforms. 
typedef BOOL (__stdcall *PFNbsma) (DWORD, LPDWORD, UINT, WPARAM, LPARAM);
static PFNbsma s_pfnBSMA;

// forward declares
LRESULT TransmitHelper(MESSAGETYPES mt, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, 
                       WNDPROC lpPrevWndFunc, SENDASYNCPROC lpCallBack, ULONG_PTR dwData, 
                       UINT fuFlags, UINT uTimeout, PDWORD_PTR lpdwResult);

/*-------------------------------
    MapCaptureMessage

    Simple function that maps one message type to 
    another (A->W and W->A) for video captures
-------------------------------*/
UINT MapCaptureMessage(UINT uMsg)
{
    if(uMsg >= WM_CAP_UNICODE_START)
        return(uMsg - WM_CAP_UNICODE_START);
    else
        return(uMsg + WM_CAP_UNICODE_START);
}

/*-------------------------------
    GodotDoCallback

    Our global wrapper around callback functions; all callbacks that need random
    conversions done go through this proc. Note that all callers will be Unicode
    windows so we do not have to check for this here.
-------------------------------*/
LRESULT GodotDoCallback(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, WNDPROC lpfn, BOOL fUniSrc, FAUXPROCTYPE fpt)
{
    LRESULT RetVal = 0;
    BOOL fUniDst;

    fUniDst = (! DoesProcExpectAnsi(hWnd, lpfn, fpt));

    if(!fUniDst && !fUniSrc)
    {
        // Call through via CallWindowProcA with no conversion

        // Note that this is the only place in this entire function where we use
        // CallWindowProc, since either the user or the system is expecting ANSI.
        // Note that on Win9x, the base wndproc can be a thunked function sitting
        // in USER.EXE; only CallWindowProcA can manage that sort of detail.
        return(CallWindowProcA(lpfn, hWnd, uMsg, wParam, lParam));
    }
    else if(fUniDst && fUniSrc)
    {
        // Pure Unicode: Call directly, no conversion needed
        // Note that we do not currently use this!!!
        return((* lpfn)(hWnd, uMsg, wParam, lParam));
    }
    else if(!fUniDst && fUniSrc)
    {
        // We need to convert from Unicode to ANSI, so use
        // our own GodotTransmitMessage to do the work.
        return(GodotTransmitMessage(mtCallWindowProc, hWnd, uMsg, wParam, lParam, lpfn, 0, 0, 0, 0, 0));
    }
    else // (fUniDst && !fUniSrc)
    {
        switch(uMsg)
        {

            case WM_CHAR:
            case EM_SETPASSWORDCHAR:
            case WM_DEADCHAR:
            case WM_SYSCHAR:
            case WM_SYSDEADCHAR:
            case WM_MENUCHAR:
                if(FDBCS_CPG(g_acp))
                {
                    WPARAM wParamW = 0;
                    static CHAR s_ch[3] = "\0";
                    // We have to go through all this nonsense because DBCS characters 
                    // arrive one byte at a time. Most of this code is never used because 
                    // DBCS chars OUGHT to be handled by WM_IME_CHAR below. 
                    if(!s_ch[0])
                    {
                        // No lead byte already waiting for trail byte
                        s_ch[0] = *(char *)wParam;
                        if(IsDBCSLeadByteEx(g_acp, *(char *)wParam)) 
                        {
                            // This is a lead byte. Save it and wait for trail byte
                            RetVal = FALSE;
                        }
                        // Not a DBCS character. Convert to Unicode.
                        MultiByteToWideChar(g_acp, 0, s_ch, 1, (WCHAR *)&wParamW, 1);

                        // Reset to indicate no Lead byte waiting
                        s_ch[0] = 0 ;
                        RetVal = TRUE;
                    }
                    else 
                    {
                        // Have lead byte, wParam should contain the trail byte
                        s_ch[1] = *(char *)wParam;
                        // Convert both bytes into one Unicode character
                        MultiByteToWideChar(g_acp, 0, s_ch, 2, (WCHAR *)&wParamW, 1);

                        // Reset to non-waiting state
                        s_ch[0] = 0;
                        RetVal = TRUE;
                    }
                    break;
                }

                // Not a DBCS system, so fall through here 

            case WM_IME_CHAR:
            case WM_IME_COMPOSITION:
            {
                WPARAM wParamW = 0;
                MultiByteToWideChar(g_acp, 0, (CHAR *)&wParam, g_mcs, (WCHAR *)&wParamW, 1);
                RetVal = (* lpfn)(hWnd, uMsg, wParamW, lParam);
                WideCharToMultiByte(g_acp, 0, (WCHAR *)&wParamW, 1, (CHAR *)&wParam, g_mcs, NULL, NULL);
                break;
            }

            case WM_CHARTOITEM:
            {
                // Mask off the hiword bits, convert, then stick the hiword bits back on.
                WPARAM wParamW = 0;
                WPARAM wpT = wParam & 0xFFFF;
                MultiByteToWideChar(g_acp, 0, (CHAR *)&wpT, g_mcs, (WCHAR *)&wParamW, 1);
                RetVal = (* lpfn)(hWnd, uMsg, wParamW, lParam);
                WideCharToMultiByte(g_acp, 0, (WCHAR *)&wParamW, 1, (CHAR *)&wpT, g_mcs, NULL, NULL);
                wParam = MAKELONG(LOWORD(wpT),HIWORD(wParam));
                break;
            }

            case (WM_USER + 25):    // might be WM_CAP_FILE_SAVEDIBA
            case (WM_USER + 23):    // might be WM_CAP_FILE_SAVEASA
            case (WM_USER + 66):    // might be WM_CAP_SET_MCI_DEVICEA
            case (WM_USER + 80):    // might be WM_CAP_PAL_OPENA
            case (WM_USER + 81):    // might be WM_CAP_PAL_SAVEA
            case (WM_USER + 20):    // might be WM_CAP_FILE_SET_CAPTURE_FILEA
                if(!IsCaptureWindow(hWnd))
                {
                    // The numbers are right, but its not a capture window, so
                    // do not convert. Instead, just pass as is.
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                }
                else
                {
                    // No memory? If the alloc fails, we eat the results.
                    LPARAM lParamW;
                    ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW);
                    RetVal = (* lpfn)(hWnd, MapCaptureMessage(uMsg), wParam, lParamW);
                    if(ar == arAlloc)
                        GodotHeapFree((LPWSTR)lParamW);
                }
                break;

            case CB_ADDSTRING:
            case CB_DIR:
            case CB_FINDSTRING:
            case CB_FINDSTRINGEXACT:
            case CB_INSERTSTRING:
            case CB_SELECTSTRING:
            {
                LONG styl = GetWindowLongA(hWnd, GWL_STYLE);
                if(((styl & CBS_OWNERDRAWFIXED) || 
                   (styl & CBS_OWNERDRAWVARIABLE)) &&
                   (!(styl & CBS_HASSTRINGS)))
                {
                    // Owner draw combo box which does not have strings stored
                    // (See Windows Bugs # 356304 for details here)
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                }
                else
                {
                    // No memory? If the alloc fails, we eat the results.
                    LPARAM lParamW;
                    ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW);
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW);
                    if(ar == arAlloc)
                        GodotHeapFree((LPWSTR)lParamW);
                }
            }
                break;

            case LB_ADDSTRING:
            case LB_ADDFILE:
            case LB_DIR:
            case LB_FINDSTRING:
            case LB_FINDSTRINGEXACT:
            case LB_INSERTSTRING:
            case LB_SELECTSTRING:
            {
                LONG styl = GetWindowLongA(hWnd, GWL_STYLE);
                if(((styl & LBS_OWNERDRAWFIXED) || 
                   (styl & LBS_OWNERDRAWVARIABLE)) &&
                   (!(styl & LBS_HASSTRINGS)))
                {
                    // Owner draw listbox which does not have strings stored
                    // (See Windows Bugs # 356304 for details here)
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                }
                else
                {
                    // No memory? If the alloc fails, we eat the results.
                    LPARAM lParamW;
                    ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW);
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW);
                    if(ar == arAlloc)
                        GodotHeapFree((LPWSTR)lParamW);
                }
                break;
            }
            case EM_REPLACESEL:
            case WM_SETTEXT:
            case WM_DEVMODECHANGE:
            case WM_SETTINGCHANGE:
            case WM_SETMESSAGESTRING: // MFC internal msg
            {
                // No memory? If the alloc fails, we eat the results.
                LPARAM lParamW;
                ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW);
                RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW);
                if(ar == arAlloc)
                    GodotHeapFree((LPWSTR)lParamW);
                break;
            }

            case WM_DDE_EXECUTE:
                // wParam is the client window hWnd, lParam is the command LPTSTR.
                // Only convert lParam if both client and server windows are Unicode
                if(GetUnicodeWindowProp((HWND)hWnd) && GetUnicodeWindowProp((HWND)wParam))
                {
                    // No memory? If the alloc fails, we eat the results.
                    LPARAM lParamW;
                    ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW);
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW);
                    if(ar == arAlloc)
                        GodotHeapFree((LPWSTR)lParamW);
                }
                else
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                break;
            
            case EM_GETLINE:
            {
                // lParam is a pointer to the buffer that receives a copy of the line. Before 
                // sending the message, set the first word of this buffer to the size, in TCHARs, 
                // of the buffer. For ANSI text, this is the number of bytes; for Unicode text, 
                // this is the numer of characters. The size in the first word is overwritten by 
                // the copied line. 
                size_t cchlParam = (WORD)lParam + 1;
                LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc(cchlParam * sizeof(WCHAR));

                if(RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW))
                {
                    RetVal = WideCharToMultiByte(g_acp, 
                                                 0, 
                                                 (LPWSTR)lParamW, 
                                                 RetVal + 1, 
                                                 (LPSTR)lParam, 
                                                 cchlParam, 
                                                 NULL, 
                                                 NULL);
                    if(RetVal)
                        RetVal--;
                }
                else
                {
                    if((LPSTR)lParam)
                        *((LPSTR)lParam) = '\0';
                }

                if(lParamW)
                    GodotHeapFree((LPWSTR)lParamW);

            }
            case LB_GETTEXT:
            {
                // lParam is a pointer to the buffer that will receive the string; it is type 
                // LPTSTR which is subsequently cast to an LPARAM. The buffer must have sufficient 
                // space for the string and a terminating null character. An LB_GETTEXTLEN message 
                // can be sent before the LB_GETTEXT message to retrieve the length, in TCHARs, of 
                // the string. 
                size_t cchlParam = SendMessageA(hWnd, LB_GETTEXTLEN, wParam, 0) + 1;
                LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc(cchlParam * sizeof(WCHAR));

                if(RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW))
                {
                    RetVal = WideCharToMultiByte(g_acp, 
                                                 0, 
                                                 (LPWSTR)lParamW, 
                                                 RetVal + 1, 
                                                 (LPSTR)lParam, 
                                                 cchlParam, 
                                                 NULL, 
                                                 NULL);
                    if(RetVal)
                        RetVal--;
                }
                else
                {
                    if((LPSTR)lParam)
                        *((LPSTR)lParam) = '\0';
                }

                if(lParamW)
                    GodotHeapFree((LPWSTR)lParamW);

                break;
            }

            case CB_GETLBTEXT:
            {
                // lParam is a pointer to the buffer that will receive the string; it is type 
                // LPTSTR which is subsequently cast to an LPARAM. The buffer must have sufficient 
                // space for the string and a terminating null character. An CB_GETLBTEXTLEN message 
                // can be sent before the CB_GETLBTEXT message to retrieve the length, in TCHARs, of 
                // the string. 
                size_t cchlParam = SendMessageA(hWnd, CB_GETLBTEXTLEN, wParam, 0) + 1;
                LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc(cchlParam * sizeof(WCHAR));

                if((RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW)) && lParamW)
                {
                    RetVal = WideCharToMultiByte(g_acp, 
                                                 0, 
                                                 (LPWSTR)lParamW, 
                                                 RetVal + 1, 
                                                 (LPSTR)lParam, 
                                                 cchlParam, 
                                                 NULL, 
                                                 NULL);
                    if(RetVal)
                        RetVal--;
                }
                else
                {
                    if((LPSTR)lParam)
                        *((LPSTR)lParam) = '\0';
                }

                if(lParamW)
                    GodotHeapFree((LPWSTR)lParamW);

                break;
            }

            case (WM_USER + 67):    // might be WM_CAP_GET_MCI_DEVICEA
            case (WM_USER + 12):    // might be WM_CAP_DRIVER_GET_NAMEA
            case (WM_USER + 13):    // might be WM_CAP_DRIVER_GET_VERSIONA
            case (WM_USER + 21):    // might be WM_CAP_FILE_GET_CAPTURE_FILEA
                if(!IsCaptureWindow(hWnd))
                {
                    // The numbers are right, but its not a capture window, so
                    // do not convert. Instead, just pass as is.
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                    break;
                }

                // If we are still here, then it is a capture message. 
                // So lets map it and fall through.
                uMsg = MapCaptureMessage(uMsg);

            case WM_GETTEXT:
            case WM_ASKCBFORMATNAME:
            {
                // wParam specifies the buffer size of the string lParam (includes the terminating null).
                LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc((UINT)wParam * sizeof(WCHAR));

                if(RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW))
                {
                    RetVal = WideCharToMultiByte(g_acp, 
                                                 0, 
                                                 (LPWSTR)lParamW, 
                                                 RetVal + 1, 
                                                 (LPSTR)lParam, 
                                                 (UINT)wParam, 
                                                 NULL, 
                                                 NULL);
                    if(RetVal)
                        RetVal--;
                }
                else
                {
                    if((LPSTR)lParam)
                        *((LPSTR)lParam) = '\0';
                }

                if(lParamW)
                    GodotHeapFree((LPWSTR)lParamW);

                break;
            }

            case (WM_USER + 1):
                if(IsFontDialog(hWnd))
                {
                    // This is a WM_CHOOSEFONT_GETLOGFONT msg
                    LPARAM lParamW = (LPARAM)(LPLOGFONTW)GodotHeapAlloc(sizeof(LOGFONTW));
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW);
                    LogFontAfromW((LPLOGFONTA)lParam, (LPLOGFONTW)lParamW);
                    if(lParamW)
                        GodotHeapFree((LPWSTR)lParamW);
                }
                else
                {
                    // This would be one of the common control msgs we do not handle
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                    break;
                }
                break;

            case (WM_USER + 100):
                if(IsNewFileOpenDialog(hWnd))
                {
                    // This is a CDM_GETSPEC msg
                    LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc(wParam * sizeof(WCHAR));
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW);
                    WideCharToMultiByte(g_acp, 0, 
                                        (LPWSTR)lParamW, wParam, 
                                        (LPSTR)lParam, wParam, 
                                        NULL, NULL);
                    RetVal = lstrlenA( (LPSTR)lParam);
                    if(lParamW)
                        GodotHeapFree((LPWSTR)lParamW);
                }
                else
                {
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                }
                break;

            case (WM_USER + 101):
                if(IsFontDialog(hWnd))
                {
                    // This is a WM_CHOOSEFONT_SETLOGFONT msg
                    LPARAM lParamW = (LPARAM)(LPLOGFONTW)GodotHeapAlloc(sizeof(LOGFONTW));
                    LogFontWfromA((LPLOGFONTW)lParamW, (LPLOGFONTA)lParam);
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW);
                    if(lParamW)
                        GodotHeapFree((LPLOGFONTW)lParamW);
                }
                else if(IsNewFileOpenDialog(hWnd))
                {
                    // This is a CDM_GETFILEPATH msg
                    LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc(wParam * sizeof(WCHAR));
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW);
                    WideCharToMultiByte(g_acp, 0, 
                                        (LPWSTR)lParamW, wParam, 
                                        (LPSTR)lParam, wParam, 
                                        NULL, NULL);
                    RetVal = lstrlenA( (LPSTR)lParam);
                    if(lParamW)
                        GodotHeapFree((LPWSTR)lParamW);
                }
                else
                {
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                }
                break;

            case (WM_USER + 102):
                if(IsFontDialog(hWnd))
                {
                    // This is a WM_CHOOSEFONT_SETFLAGS msg
                    // The docs claim that lParam has a CHOOSEFONT struct but the code shows
                    // that it only has the Flags in it, so pass it as is
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                }
                else if(IsNewFileOpenDialog(hWnd))
                {
                    // This is a CDM_GETFOLDERPATH
                    // lParam is a buffer for the path of the open folder
                    LPARAM lParamW = (LPARAM)(LPWSTR)GodotHeapAlloc(wParam * sizeof(WCHAR));
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW);
                    WideCharToMultiByte(g_acp, 0, 
                                        (LPWSTR)lParamW, wParam, 
                                        (LPSTR)lParam, wParam, 
                                        NULL, NULL);
                    RetVal = lstrlenA( (LPSTR)lParam);
                    if(lParamW)
                        GodotHeapFree((LPWSTR)lParamW);
                }
                else
                {
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                }
                break;

            case (WM_USER + 104):
                if(IsNewFileOpenDialog(hWnd))
                {
                    // This is a CDM_SETCONTROLTEXT message
                    // lParam is the control text (wParam is the control ID)

                    // No memory? If the alloc fails, we eat the results.
                    LPARAM lParamW;
                    WPARAM wParamW;
                    ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW);
                    wParamW = gwcslen((LPWSTR)lParamW);
                    RetVal = (* lpfn)(hWnd, uMsg, wParamW, lParamW);
                    if(ar == arAlloc)
                        GodotHeapFree((LPWSTR)lParamW);
                }
                else
                {
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                }
                break;

            case (WM_USER + 106):
                if(IsNewFileOpenDialog(hWnd))
                {
                    // This is a CDM_SETDEFEXT message
                    // lParam is the extension

                    // No memory? If the alloc fails, we eat the results.
                    LPARAM lParamW;
                    WPARAM wParamW;
                    ALLOCRETURN ar = GodotToUnicodeOnHeap((LPSTR)lParam, &(LPWSTR)lParamW);
                    wParamW = gwcslen((LPWSTR)lParamW);
                    RetVal = (* lpfn)(hWnd, uMsg, wParamW, lParamW);
                    if(ar == arAlloc)
                        GodotHeapFree((LPWSTR)lParamW);
                }
                else
                {
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                }
                break;


            case WM_CREATE:
            case WM_NCCREATE:
            {
                LPCREATESTRUCTA lpcsA = (LPCREATESTRUCTA)lParam;
                CREATESTRUCTW cs;
                ALLOCRETURN arName = arNoAlloc;
                ALLOCRETURN arClass = arNoAlloc;

                ZeroMemory(&cs, sizeof(CREATESTRUCTW));
                cs.lpCreateParams   = lpcsA->lpCreateParams;
                cs.hInstance        = lpcsA->hInstance;
                cs.hMenu            = lpcsA->hMenu;
                cs.hwndParent       = lpcsA->hwndParent;
                cs.cy               = lpcsA->cy;
                cs.cx               = lpcsA->cx;
                cs.y                = lpcsA->y;
                cs.x                = lpcsA->x;
                cs.style            = lpcsA->style;
                cs.dwExStyle        = lpcsA->dwExStyle;
                arName = GodotToUnicodeOnHeap(lpcsA->lpszName, &(LPWSTR)(cs.lpszName));
                arClass = GodotToUnicodeOnHeap(lpcsA->lpszClass, &(LPWSTR)(cs.lpszClass));
            
                RetVal = (* lpfn)(hWnd, uMsg, wParam, (LPARAM)&cs);

                // Free up strings if we allocated any
                if(arName==arAlloc)
                    GodotHeapFree((LPWSTR)(cs.lpszName));
                if(arClass==arAlloc)
                    GodotHeapFree((LPWSTR)(cs.lpszClass));
                break;
            }

            case WM_MDICREATE:
            {
                // wParam is not used, lParam is a pointer to an MDICREATESTRUCT structure containing 
                // information that the system uses to create the MDI child window. 
                LPMDICREATESTRUCTA lpmcsiA = (LPMDICREATESTRUCTA)lParam;
                MDICREATESTRUCTW mcsi;
                ALLOCRETURN arTitle = arNoAlloc;
                ALLOCRETURN arClass = arNoAlloc;

                ZeroMemory(&mcsi, sizeof(MDICREATESTRUCTW));
                mcsi.hOwner = lpmcsiA->hOwner;
                mcsi.x      = lpmcsiA->x;
                mcsi.y      = lpmcsiA->y;
                mcsi.cx     = lpmcsiA->cx;
                mcsi.cy     = lpmcsiA->cy;
                mcsi.style  = lpmcsiA->style;
                mcsi.lParam = lpmcsiA->lParam;
                arTitle = GodotToUnicodeOnHeap(lpmcsiA->szTitle, &(LPWSTR)(mcsi.szTitle));
                arClass = GodotToUnicodeOnHeap(lpmcsiA->szClass, &(LPWSTR)(mcsi.szClass));

                RetVal = (* lpfn)(hWnd, uMsg, wParam, (LPARAM)&mcsi);

                // Free up strings if we allocated any
                if(arTitle==arAlloc)
                    GodotHeapFree((LPWSTR)(mcsi.szTitle));
                if(arClass==arAlloc)
                    GodotHeapFree((LPWSTR)(mcsi.szClass));

                break;
            }

            case WM_DEVICECHANGE:
            {
                switch(wParam)
                {
                    case DBT_CUSTOMEVENT:
                    case DBT_DEVICEARRIVAL:
                    case DBT_DEVICEQUERYREMOVE:
                    case DBT_DEVICEQUERYREMOVEFAILED:
                    case DBT_DEVICEREMOVECOMPLETE:
                    case DBT_DEVICEREMOVEPENDING:
                    case DBT_DEVICETYPESPECIFIC:
                    {
                        // lParam contains info about the device. We interrogate it as if it were
                        // a PDEV_BROADCAST_HDR in order to find out what it really is, then convert
                        // as needed
                        if (((PDEV_BROADCAST_HDR)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
                        {
                            PDEV_BROADCAST_DEVICEINTERFACE_A pdbdia = (PDEV_BROADCAST_DEVICEINTERFACE_A)lParam;
                            DEV_BROADCAST_DEVICEINTERFACE_W dbdi;
                            ALLOCRETURN arName = arNoAlloc;

                            ZeroMemory(&dbdi, sizeof(DEV_BROADCAST_DEVICEINTERFACE_W));
                            dbdi.dbcc_size          = sizeof(DEV_BROADCAST_DEVICEINTERFACE_W);
                            dbdi.dbcc_devicetype    = pdbdia->dbcc_devicetype;
                            dbdi.dbcc_reserved      = pdbdia->dbcc_reserved;
                            dbdi.dbcc_classguid     = pdbdia->dbcc_classguid;
                            arName = GodotToUnicodeOnHeap(pdbdia->dbcc_name, *(LPWSTR**)(dbdi.dbcc_name));

                            RetVal = (* lpfn)(hWnd, uMsg, wParam, (LPARAM)&dbdi);
                            if(arName==arAlloc)
                                GodotHeapFree(dbdi.dbcc_name);
                        }
                        else if(((PDEV_BROADCAST_HDR)lParam)->dbch_devicetype == DBT_DEVTYP_PORT)
                        {
                            PDEV_BROADCAST_PORT_A pdbpa = (PDEV_BROADCAST_PORT_A)lParam;
                            DEV_BROADCAST_PORT_W dbp;
                            ALLOCRETURN arName = arNoAlloc;

                            ZeroMemory(&dbp, sizeof(DEV_BROADCAST_PORT_W));
                            dbp.dbcp_size       = sizeof(DEV_BROADCAST_PORT_W);
                            dbp.dbcp_devicetype = pdbpa->dbcp_devicetype;
                            dbp.dbcp_reserved   = pdbpa->dbcp_reserved;
                            arName = GodotToUnicodeOnHeap(pdbpa->dbcp_name, *(LPWSTR**)(dbp.dbcp_name));

                            RetVal = (* lpfn)(hWnd, uMsg, wParam, (LPARAM)&dbp);
                            if(arName==arAlloc)
                                GodotHeapFree(dbp.dbcp_name);
                        }
                        else
                        {
                            // No changes needed! There are no strings in the other structures.
                            RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                        }
                        break;
                    }
                    case DBT_USERDEFINED: 
                        // No UNICODE string in this one, so fall through
                    
                    default:
                        RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                        break;
                }
                break;
            }

            default:
            {
                // Lets get our registered messages, if we haven't yet.
                if(!msgHELPMSGSTRING)
                    msgHELPMSGSTRING = RegisterWindowMessage(HELPMSGSTRINGA);
                if(!msgFINDMSGSTRING)
                    msgFINDMSGSTRING = RegisterWindowMessage(FINDMSGSTRINGA);

                if((uMsg == msgHELPMSGSTRING) && 
                    (((LPOPENFILENAMEA)lParam)->lStructSize == OPENFILENAME_SIZE_VERSION_400A))
                {
                    WCHAR drive[_MAX_DRIVE];
                    WCHAR dir[_MAX_DIR];
                    WCHAR file[_MAX_FNAME];
                    LPOPENFILENAMEA lpofnA = (LPOPENFILENAMEA)lParam;
                    OPENFILENAMEW ofn;
                    ALLOCRETURN arCustomFilter = arNoAlloc;
                    ALLOCRETURN arFile = arNoAlloc;
                    ALLOCRETURN arFileTitle = arNoAlloc;

                    // lParam is an LPOPENFILENAMEA to be converted. Copy all the
                    // members that the user might expect.
                    ZeroMemory(&ofn, OPENFILENAME_SIZE_VERSION_400W);
                    ofn.lStructSize         = OPENFILENAME_SIZE_VERSION_400W;
                    arCustomFilter          = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrCustomFilter,
                                                                         lpofnA->nMaxCustFilter,
                                                                         &ofn.lpstrCustomFilter,
                                                                         g_acp);
                    ofn.nMaxCustFilter      = gwcslen(ofn.lpstrCustomFilter);
                    ofn.nFilterIndex        = lpofnA->nFilterIndex;
                    ofn.nMaxFile            = lpofnA->nMaxFile * sizeof(WCHAR);
                    arFile                  = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFile,
                                                                         lpofnA->nMaxFile,
                                                                         &ofn.lpstrFile,
                                                                         g_acp);
                    ofn.nMaxFile            = gwcslen(ofn.lpstrFile);
                    arFileTitle             = GodotToUnicodeCpgCchOnHeap(lpofnA->lpstrFileTitle,
                                                                         lpofnA->nMaxFileTitle,
                                                                         &ofn.lpstrFileTitle,
                                                                         g_acp);
                    ofn.nMaxFileTitle       = gwcslen(ofn.lpstrFileTitle);
                    ofn.Flags = lpofnA->Flags;

                    // nFileOffset and nFileExtension are to provide info about the
                    // file name and extension location in lpstrFile, but there is 
                    // no reasonable way to get it from the return so we just recalc
                    gwsplitpath(ofn.lpstrFile, drive, dir, file, NULL);
                    ofn.nFileOffset         = (gwcslen(drive) + gwcslen(dir));
                    ofn.nFileExtension      = ofn.nFileOffset + gwcslen(file);

                    RetVal = (*lpfn)(hWnd, uMsg, wParam, (LPARAM)&ofn);

                    // Free up some memory if we allocated any
                    if(arCustomFilter==arAlloc)
                        GodotHeapFree(ofn.lpstrCustomFilter);
                    if(arFile==arAlloc)
                        GodotHeapFree(ofn.lpstrFile);
                    if(arFileTitle==arAlloc)
                        GodotHeapFree(ofn.lpstrFileTitle);

                }
                else if(((uMsg == msgFINDMSGSTRING) || (uMsg == msgHELPMSGSTRING)) && 
                        ((((LPFINDREPLACEW)lParam)->lStructSize) == sizeof(FINDREPLACEA)))
                {
                    LPFINDREPLACEW lpfr = (LPFINDREPLACEW)lParam;

                    // lParam is an LPFINDREPLACEW that we passed on through. 
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);

                    if((lpfr->Flags & FR_DIALOGTERM) &&
                       ((lpfr->lpfnHook == &FRHookProcFind) || (lpfr->lpfnHook == &FRHookProcReplace)))
                    {
                        // Now handle our cleanup. I do not think this should
                        // be needed, but it can't hurt to do it just in case.
                        LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);

                        // They are destroying the dialog, so unhook ourselves
                        // and clean up the dialog (if we have not done it yet). 
                        if(lpfr->lpfnHook == &FRHookProcFind) 
                        {
                            // Find dialog, not yet cleaned up
                            lpfr->lpfnHook = lpgti->pfnFindText;
                            if(lpfr->lpfnHook == NULL)
                                lpfr->Flags &= ~FR_ENABLEHOOK;
                        }
                        else if(lpfr->lpfnHook == &FRHookProcReplace) 
                        {
                            // Replace dialog, not yet cleaned up
                            lpfr->lpfnHook = lpgti->pfnReplaceText;
                            if(lpfr->lpfnHook == NULL)
                                lpfr->Flags &= ~FR_ENABLEHOOK;
                        }
                    }
                }
                else if((uMsg == msgHELPMSGSTRING) && 
                        ((LPCHOOSEFONTA)lParam)->lStructSize == sizeof(CHOOSEFONTA))
                {
                    LPCHOOSEFONTA lpcfA = (LPCHOOSEFONTA)lParam;
                    CHOOSEFONTW cf;
                    LPARAM lParamW;
                    ALLOCRETURN ar = arNoAlloc;

                    // lParam is an LPCHOOSEFONTA to be converted. Copy all the
                    // members that the user might expect.
                    ZeroMemory(&cf, sizeof(CHOOSEFONTW));
                    cf.lStructSize  = sizeof(CHOOSEFONTW);
                    cf.hDC          = lpcfA->hDC;
                    LogFontWfromA(cf.lpLogFont, lpcfA->lpLogFont);
                    cf.iPointSize    = lpcfA->iPointSize;
                    cf.Flags        = lpcfA->Flags;
                    cf.rgbColors    = lpcfA->rgbColors;
                    cf.lCustData    = lpcfA->lCustData;
                    cf.nFontType    = lpcfA->nFontType;
                    cf.nSizeMin     = lpcfA->nSizeMin;
                    cf.nSizeMax     = lpcfA->nSizeMax;
                    ar = GodotToUnicodeOnHeap(lpcfA->lpszStyle, &(cf.lpszStyle));

                    lParamW = (LPARAM)&cf;

                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParamW);
                    if(ar==arAlloc)
                        GodotHeapFree((LPWSTR)cf.lpszStyle);
                }
                else
                {
                    // No translation needed, as far as we know.
                    RetVal = (* lpfn)(hWnd, uMsg, wParam, lParam);
                }
                break;
            }
        }
    return(RetVal);
    }
}

/*-------------------------------
    GodotTransmitMessage

    Our global wrapper for sending out messages (SendMessage, et. al.). 

    Its fundamental purpose: 

    1) Convert back to Ansi, as expected
    2) Call the function as specified by mt
    3) Convert to Unicode, as expected
-------------------------------*/
LRESULT GodotTransmitMessage(
    MESSAGETYPES mt,            // Type of message function
    HWND hWnd,                  // handle to window - overloaded for thread id in PostThreadMessage
    UINT Msg,                   // message
    WPARAM wParamW,             // first message parameter
    LPARAM lParamW,             // second message parameter
    WNDPROC lpPrevWndFunc,      // pointer to previous procedure - overloaded for multiple hook procs  
    SENDASYNCPROC lpCallBack,   // callback function
    ULONG_PTR dwData,           // application-defined value - overloaded for DefFrameProc as second hWnd
    UINT fuFlags,               // send options -- overloaded for BroadcastSystemMessages as dwFlags
    UINT uTimeout,              // time-out duration
    PDWORD_PTR lpdwResult       // retval for synch. -- overloaded BroadcastSystemMessages lpdwRecipients
)
{
    LRESULT retval = 0;
    WPARAM wParam = 0;
    LPARAM lParam = 0;
    size_t cchlParam;

    // Some flags we will need for our message handling
    BOOL fUnicodeProc = (! DoesProcExpectAnsi(hWnd, lpPrevWndFunc, fptUnknown));
    
    /*
        fUnicodeProc == Does the wndproc being called expect Unicode messages?
    */

    if((!fUnicodeProc) && (mt==mtCallWindowProcA) ||
        (fUnicodeProc) && ((mt==mtCallWindowProc)))
    {
        // The wndproc either expects ANSI and the caller has used one of the ANSI 
        // functions or it expects Unicode and they have used CallWindowProcW. In 
        // these cases, we do no conversions here
        retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, 
                                lpPrevWndFunc, lpCallBack, dwData, 
                                fuFlags, uTimeout, lpdwResult);
    }
    else
    {
        switch(Msg)
        {
            case WM_CHAR:
            case EM_SETPASSWORDCHAR:
            case WM_DEADCHAR:
            case WM_SYSCHAR:
            case WM_SYSDEADCHAR:
            case WM_MENUCHAR:
                // All these messages require the wParamW to be converted from Unicode
                wParam = 0;
                WideCharToMultiByte(g_acp, 0, (WCHAR *)&wParamW, 1, (char *)&wParam, g_mcs, NULL, NULL);

                if(FDBCS_CPG(g_acp))
                {
                    if(!wParam)
                    {
                        retval = TransmitHelper(mt, hWnd, Msg, wParam, lParamW, 
                                                lpPrevWndFunc, lpCallBack, dwData, 
                                                fuFlags, uTimeout, lpdwResult);
                        break;
                    }
                    else if(IsDBCSLeadByte(*(char *)(LOBYTE(wParam))))
                    {
                        // Ok, its a DBCS code page and wParam contains a DBCS character.
                        // we must send two WM_CHAR messages, one with each byte in it
                        char sz[2];

                        sz[0] = *(char *)LOBYTE(wParam);
                        sz[1] = *(char *)HIBYTE(wParam);
                        retval = TransmitHelper(mt, hWnd, Msg, (WPARAM)&sz[0], lParamW, 
                                                lpPrevWndFunc, lpCallBack, dwData, 
                                                fuFlags, uTimeout, lpdwResult);
                        if(retval==0)
                        {
                            // The first byte was handled, so send the second byte
                            retval = TransmitHelper(mt, hWnd, Msg, (WPARAM)&sz[1], lParamW, 
                                                    lpPrevWndFunc, lpCallBack, dwData, 
                                                    fuFlags, uTimeout, lpdwResult);
                        }
                        MultiByteToWideChar(g_acp, 0, (char *)&sz[0], g_mcs, (WCHAR *)&wParamW, 1);
                        break;
                    }
                }
/*
                if(FDBCS_CPG(g_acp) && (IsDBCSLeadByte((LOBYTE(wParam)))))
                {
                    // Ok, its a DBCS code page and wParam contains a DBCS character.
                    // we must send two WM_CHAR messages, one with each byte in it
                    retval = TransmitHelper(mt, hWnd, Msg, (WPARAM)LOBYTE(wParam), lParamW, 
                                            lpPrevWndFunc, lpCallBack, dwData, 
                                            fuFlags, uTimeout, lpdwResult);
                    if(retval==0)
                    {
                        // The first byte was handled, so send the second byte
                        retval = TransmitHelper(mt, hWnd, Msg, (WPARAM)HIBYTE(wParam), lParamW, 
                                                lpPrevWndFunc, lpCallBack, dwData, 
                                                fuFlags, uTimeout, lpdwResult);
                    }
                    break;
                }
*/
                // Not a DBCS code page, or at least not a DBCS character, so we just fall through now.
                
            case WM_IME_CHAR:
            case WM_IME_COMPOSITION:
                retval = TransmitHelper(mt, hWnd, Msg, wParam, lParamW, 
                                        lpPrevWndFunc, lpCallBack, dwData, 
                                        fuFlags, uTimeout, lpdwResult);
                MultiByteToWideChar(g_acp, 0, (char *)&wParam, g_mcs, (WCHAR *)&wParamW, 1);
                break;

            case WM_CHARTOITEM:
            {
                // Mask off the hiword bits, convert, then stick the hiword bits back on.
                WPARAM wpT = wParamW & 0xFFFF;
                WideCharToMultiByte(g_acp, 0, (WCHAR *)&wpT, 1, (char *)&wParam, g_mcs, NULL, NULL);
                retval = TransmitHelper(mt, hWnd, Msg, wParam, lParamW, 
                                        lpPrevWndFunc, lpCallBack, dwData, 
                                        fuFlags, uTimeout, lpdwResult);
                MultiByteToWideChar(g_acp, 0, (char *)&wParam, g_mcs, (WCHAR *)&wpT, 1);
                wParamW = MAKELONG(LOWORD(wpT),HIWORD(wParamW));
                break;
            }

            case (WM_USER + 125):   // might be WM_CAP_FILE_SAVEDIBW:
            case (WM_USER + 123):   // might be WM_CAP_FILE_SAVEASW:
            case (WM_USER + 166):   // might be WM_CAP_SET_MCI_DEVICEW:
            case (WM_USER + 180):   // might be WM_CAP_PAL_OPENW:
            case (WM_USER + 181):   // might be WM_CAP_PAL_SAVEW:
            case (WM_USER + 120):   // might be WM_CAP_FILE_SET_CAPTURE_FILEW:
                if(!IsCaptureWindow(hWnd))
                {
                    // The numbers are right, but its not a capture window, so
                    // do not convert. Instead, just pass as is.
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, 
                                            lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                }
                else
                {
                    // No memory? If the alloc fails, we eat the results.
                    ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam);
                    if(ar != arFailed)
                    {
                        Msg = MapCaptureMessage(Msg);
                        retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, 
                                                lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                    }
                    if(ar == arAlloc)
                        GodotHeapFree((LPSTR)lParam);
                }
                break;

            case CB_ADDSTRING:
            case CB_DIR:
            case CB_FINDSTRING:
            case CB_FINDSTRINGEXACT:
            case CB_INSERTSTRING:
            case CB_SELECTSTRING:
            {
                LONG styl = GetWindowLongA(hWnd, GWL_STYLE);
                if(((styl & CBS_OWNERDRAWFIXED) || 
                   (styl & CBS_OWNERDRAWVARIABLE)) &&
                   (!(styl & CBS_HASSTRINGS)))
                {
                    // Owner draw combo box which does not have strings stored
                    // (See Windows Bugs # 356304 for details here)
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, 
                                            lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                }
                else
                {
                    // No memory? If the alloc fails, we eat the results.
                    ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam);
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, 
                                            lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                    if(ar == arAlloc)
                        GodotHeapFree((LPSTR)lParam);
                }
                break;
            }

            case LB_ADDFILE:
            case LB_ADDSTRING:
            case LB_DIR:
            case LB_FINDSTRING:
            case LB_FINDSTRINGEXACT:
            case LB_INSERTSTRING:
            case LB_SELECTSTRING:
            {
                LONG styl = GetWindowLongA(hWnd, GWL_STYLE);
                if(((styl & LBS_OWNERDRAWFIXED) || 
                   (styl & LBS_OWNERDRAWVARIABLE)) &&
                   (!(styl & LBS_HASSTRINGS)))
                {
                    // Owner draw listbox which does not have strings stored
                    // (See Windows Bugs # 356304 for details here)
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, 
                                            lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                }
                else
                {
                    // No memory? If the alloc fails, we eat the results.
                    ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam);
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, 
                                            lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                    if(ar == arAlloc)
                        GodotHeapFree((LPSTR)lParam);
                }
                break;
            }

            case EM_REPLACESEL:
            case WM_SETTEXT:
            case WM_DEVMODECHANGE:
            case WM_SETTINGCHANGE:
            case WM_SETMESSAGESTRING: // MFC internal msg
            {
                // All these messages require a string in lParam to be converted from Unicode

                // No memory? If the alloc fails, we eat the results.
                ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam);
                retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, 
                                        lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                if(ar == arAlloc)
                    GodotHeapFree((LPSTR)lParam);
                break;
            }

            case WM_DDE_EXECUTE:
                // wParam is the client window hWnd, lParam is the command LPTSTR.
                // Only convert lParam if both client and server windows are Unicode
                if(GetUnicodeWindowProp((HWND)wParamW))
                {
                    // No memory? If the alloc fails, we eat the results.
                    ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam);
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, 
                                            lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                    if(ar == arAlloc)
                        GodotHeapFree((LPSTR)lParam);
                }
                else
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, 
                                            lpPrevWndFunc, lpCallBack, dwData, 
                                            fuFlags, uTimeout, lpdwResult);
                break;
            
            case EM_GETLINE:
                // lParam is a pointer to the buffer that receives a copy of the line. Before 
                // sending the message, set the first word of this buffer to the size, in TCHARs, 
                // of the buffer. For ANSI text, this is the number of bytes; for Unicode text, 
                // this is the numer of characters. The size in the first word is overwritten by 
                // the copied line. 
                cchlParam = (WORD)lParamW;
                lParam = (LPARAM)(LPSTR)GodotHeapAlloc(cchlParam * g_mcs);
                retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, 
                                        lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                if(retval)
                {
                    retval = MultiByteToWideChar(g_acp, 
                                                 0, 
                                                 (LPSTR)lParam, 
                                                 retval + 1, 
                                                 (LPWSTR)lParamW, 
                                                 cchlParam);
                    if(retval)
                        retval--;
                }
                else
                {
                    if((LPWSTR)lParamW)
                        *((LPWSTR)lParamW) = L'\0';
                }

                if(lParam)
                    GodotHeapFree((LPSTR)lParam);
                break;
                
            case LB_GETTEXT:
                // lParam is a pointer to the buffer that will receive the string; it is type 
                // LPTSTR which is subsequently cast to an LPARAM. The buffer must have sufficient 
                // space for the string and a terminating null character. An LB_GETTEXTLEN message 
                // can be sent before the LB_GETTEXT message to retrieve the length, in TCHARs, of 
                // the string. 
                cchlParam = SendMessageA(hWnd, LB_GETTEXTLEN, wParamW, 0) + 1;
                lParam = (LPARAM)(LPSTR)GodotHeapAlloc(cchlParam * g_mcs);
                retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, 
                                        lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                if(retval)
                {
                    retval = MultiByteToWideChar(g_acp, 
                                                 0, 
                                                 (LPSTR)lParam, 
                                                 retval + 1, 
                                                 (LPWSTR)lParamW, 
                                                 cchlParam);
                    if(retval)
                        retval--;
                }
                else
                {
                    if((LPWSTR)lParamW)
                        *((LPWSTR)lParamW) = L'\0';
                }

                if(lParam)
                    GodotHeapFree((LPSTR)lParam);
                break;

            case LB_GETTEXTLEN:
                retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, 
                                        lpPrevWndFunc, lpCallBack, dwData, 
                                        fuFlags, uTimeout, lpdwResult);
                if(FDBCS_CPG(g_acp))
                {
                    // In the DBCS case, LB_GETTEXTLEN returns number of bytes, but
                    // we need to get the number of characters for the Unicode case.
                    LPSTR lpsz = GodotHeapAlloc(retval + 1);
                    if(lpsz)
                    {
                        cchlParam = TransmitHelper(mt, hWnd, LB_GETTEXT, (WPARAM)(retval + 1), (LPARAM)lpsz, 
                                                lpPrevWndFunc, lpCallBack, dwData, 
                                                fuFlags, uTimeout, lpdwResult);
                        if(cchlParam > 0)
                        {
                            LPWSTR lpwz = GodotHeapAlloc((cchlParam + 1) * sizeof(WCHAR));
                            size_t cch = (cchlParam + 1) * sizeof(WCHAR);
                            retval = MultiByteToWideChar(g_acp, 0, lpsz, cchlParam, lpwz, cch);
                            if(lpwz)
                                GodotHeapFree(lpwz);
                        }
                        GodotHeapFree(lpsz);
                    }
                }
                break;

            case CB_GETLBTEXT:
                // lParam is a pointer to the buffer that will receive the string; it is type 
                // LPTSTR which is subsequently cast to an LPARAM. The buffer must have sufficient 
                // space for the string and a terminating null character. An CB_GETLBTEXTLEN message 
                // can be sent before the CB_GETLBTEXT message to retrieve the length, in TCHARs, of 
                // the string. 
                cchlParam = SendMessageA(hWnd, CB_GETLBTEXTLEN, wParamW, 0) + 1;
                lParam = (LPARAM)(LPSTR)GodotHeapAlloc(cchlParam * g_mcs);
                retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, 
                                        lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                if(retval)
                {
                    retval = MultiByteToWideChar(g_acp, 
                                                 0, 
                                                 (LPSTR)lParam, 
                                                 retval + 1, 
                                                 (LPWSTR)lParamW, 
                                                 cchlParam);
                    if(retval)
                        retval--;
                }
                else
                {
                    if((LPWSTR)lParamW)
                        *((LPWSTR)lParamW) = L'\0';
                }
                if(lParam)
                    GodotHeapFree((LPSTR)lParam);
                break;

            case CB_GETLBTEXTLEN:
                retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, 
                                        lpPrevWndFunc, lpCallBack, dwData, 
                                        fuFlags, uTimeout, lpdwResult);
                if(FDBCS_CPG(g_acp))
                {
                    // In the DBCS case, CB_GETLBTEXTLEN returns number of bytes, but
                    // we need to get the number of characters for the Unicode case.
                    LPSTR lpsz = GodotHeapAlloc(retval + 1);
                    if(lpsz)
                    {
                        cchlParam = TransmitHelper(mt, hWnd, CB_GETLBTEXT, (WPARAM)(retval + 1), (LPARAM)lpsz, 
                                                lpPrevWndFunc, lpCallBack, dwData, 
                                                fuFlags, uTimeout, lpdwResult);
                        if(cchlParam > 0)
                        {
                            LPWSTR lpwz = GodotHeapAlloc((cchlParam + 1) * sizeof(WCHAR));
                            if(lpwz)
                            {
                                size_t cch = (cchlParam + 1) * sizeof(WCHAR);
                                retval = MultiByteToWideChar(g_acp, 0, lpsz, cchlParam, lpwz, cch);
                                GodotHeapFree(lpwz);
                            }
                        }
                        GodotHeapFree(lpsz);
                    }
                }
                break;

            case (WM_USER + 167):   // might be WM_CAP_GET_MCI_DEVICEW
            case (WM_USER + 112):   // might be WM_CAP_DRIVER_GET_NAMEW:
            case (WM_USER + 113):   // might be WM_CAP_DRIVER_GET_VERSIONW:
            case (WM_USER + 121):   // might be WM_CAP_FILE_GET_CAPTURE_FILEW:
                if(!IsCaptureWindow(hWnd))
                {
                    // The numbers are right, but its not a capture window, so
                    // do not convert. Instead, just pass as is.
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, 
                                            lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                    break;
                }

                // If we are still here, then it is a capture message. So lets map
                // it and faill through.
                Msg = MapCaptureMessage(Msg);

                
            case WM_GETTEXT:
                // wParam specifies the size of the buffer in the string in lParam
                cchlParam = (size_t)wParamW;
                lParam = (LPARAM)(LPSTR)GodotHeapAlloc(cchlParam * g_mcs);
                retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, 
                                        lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                if(retval)
                {
                    retval = MultiByteToWideChar(g_acp, 
                                                 0, 
                                                 (LPSTR)lParam, 
                                                 retval + 1, 
                                                 (LPWSTR)lParamW, 
                                                 cchlParam);
                    if(retval)
                        retval--;
                }
                else
                {
                    if((LPWSTR)lParamW)
                        *((LPWSTR)lParamW) = L'\0';
                }
                if(lParam)
                    GodotHeapFree((LPSTR)lParam);
                break;

            case WM_GETTEXTLENGTH:
                retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, 
                                        lpPrevWndFunc, lpCallBack, dwData, 
                                        fuFlags, uTimeout, lpdwResult);
                if(FDBCS_CPG(g_acp))
                {
                    // In the DBCS case, WM_GETTEXTLENGTH returns number of bytes, but
                    // we need to get the number of characters for the Unicode case.
                    // If any of the allocs fail, we will live with the less than perfect
                    // result we have in hand
                    LPSTR lpsz = GodotHeapAlloc(retval + 1);
                    if(lpsz)
                    {
                        cchlParam = TransmitHelper(mt, hWnd, WM_GETTEXT, (WPARAM)(retval + 1), (LPARAM)lpsz, 
                                                lpPrevWndFunc, lpCallBack, dwData, 
                                                fuFlags, uTimeout, lpdwResult);
                        if(cchlParam > 0)
                        {
                            LPWSTR lpwz = GodotHeapAlloc((cchlParam + 1) * sizeof(WCHAR));
                            if(lpwz)
                            {
                                size_t cch = (cchlParam + 1) * sizeof(WCHAR);
                                retval = MultiByteToWideChar(g_acp, 0, lpsz, cchlParam, lpwz, cch);
                                GodotHeapFree(lpwz);
                            }
                        }
                        GodotHeapFree(lpsz);
                    }
                }
                break;

            case (WM_USER + 1):
                if(IsFontDialog(hWnd))
                {
                    // This is a WM_CHOOSEFONT_GETLOGFONT msg
                    LOGFONTA lfa;
                    lParam = (LPARAM)&lfa;
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, 
                                            lpPrevWndFunc, lpCallBack, dwData, 
                                            fuFlags, uTimeout, lpdwResult);
                    LogFontWfromA((LPLOGFONTW)lParamW, (LPLOGFONTA)lParam);
                }
                else
                {
                    // This would be one of the common control msgs we do not handle
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, 
                                            lpPrevWndFunc, lpCallBack, dwData, 
                                            fuFlags, uTimeout, lpdwResult);
                    break;
                }
                break;

            case (WM_USER + 100):
                if(IsNewFileOpenDialog(hWnd))
                {
                    // This is a CDM_GETSPEC msg
                    wParam = wParamW * g_mcs;
                    (LPSTR)lParam = (LPSTR)GodotHeapAlloc(wParam);
                    retval = TransmitHelper(mt, hWnd, Msg, wParam, lParam, 
                                            lpPrevWndFunc, lpCallBack, dwData, 
                                            fuFlags, uTimeout, lpdwResult);
                    MultiByteToWideChar(g_acp, 0, (LPSTR)lParam, wParam, (LPWSTR)lParamW, wParamW);
                    retval = gwcslen((LPWSTR)lParamW);
                    if(lParam)
                        GodotHeapFree((LPSTR)lParam);
                }
                else
                {
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, 
                                            lpPrevWndFunc, lpCallBack, dwData, 
                                            fuFlags, uTimeout, lpdwResult);
                }
                break;

            case (WM_USER + 101):
                if(IsFontDialog(hWnd))
                {
                    // This is a WM_CHOOSEFONT_SETLOGFONT msg
                    LOGFONTA lfa;

                    lParam = (LPARAM)&lfa;
                    LogFontAfromW((LPLOGFONTA)lParam, (LPLOGFONTW)lParamW);
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, 
                                             lpPrevWndFunc, lpCallBack, dwData, 
                                             fuFlags, uTimeout, lpdwResult);
                }
                else if(IsNewFileOpenDialog(hWnd))
                {
                    // This is a CDM_GETFILEPATH msg
                    wParam = wParamW * g_mcs;
                    (LPSTR)lParam = (LPSTR)GodotHeapAlloc(wParam);
                    retval = TransmitHelper(mt, hWnd, Msg, wParam, lParam, 
                                            lpPrevWndFunc, lpCallBack, dwData, 
                                            fuFlags, uTimeout, lpdwResult);
                    MultiByteToWideChar(g_acp, 0, (LPSTR)lParam, wParam, (LPWSTR)lParamW, wParamW);
                    retval = gwcslen((LPWSTR)lParamW);
                    if(lParam)
                        GodotHeapFree((LPSTR)lParam);
                }
                else
                {
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, 
                                            lpPrevWndFunc, lpCallBack, dwData, 
                                            fuFlags, uTimeout, lpdwResult);
                }
                break;

            case (WM_USER + 102):
                if(IsFontDialog(hWnd))
                {
                    // This is a WM_CHOOSEFONT_SETFLAGS msg
                    // The docs claim that lParam has a CHOOSEFONT struct but the code shows
                    // that it only has the Flags in it
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, 
                                             lpPrevWndFunc, lpCallBack, dwData, 
                                             fuFlags, uTimeout, lpdwResult);
                }
                else if(IsNewFileOpenDialog(hWnd))
                {
                    // This is a CDM_GETFOLDERPATH
                    // lParam is a buffer for the path of the open folder
                    wParam = wParamW * g_mcs;
                    (LPSTR)lParam = (LPSTR)GodotHeapAlloc(wParam);
                    retval = TransmitHelper(mt, hWnd, Msg, wParam, lParam, 
                                            lpPrevWndFunc, lpCallBack, dwData, 
                                            fuFlags, uTimeout, lpdwResult);
                    MultiByteToWideChar(g_acp, 0, (LPSTR)lParam, wParam, (LPWSTR)lParamW, wParamW);
                    retval = gwcslen((LPWSTR)lParamW);
                    if(lParam)
                        GodotHeapFree((LPSTR)lParam);
                }
                else
                {
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, 
                                            lpPrevWndFunc, lpCallBack, dwData, 
                                            fuFlags, uTimeout, lpdwResult);
                }
                break;

            case (WM_USER + 104):
                if(IsNewFileOpenDialog(hWnd))
                {
                    // This is a CDM_SETCONTROLTEXT message
                    // lParam is the control text (wParam is the control ID)

                    // No memory? If the alloc fails, we eat the results.
                    ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam);
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, 
                                            lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                    if(ar == arAlloc)
                        GodotHeapFree((LPSTR)lParam);
                }
                else
                {
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, 
                                            lpPrevWndFunc, lpCallBack, dwData, 
                                            fuFlags, uTimeout, lpdwResult);
                }
                break;

            case (WM_USER + 106):
                if(IsNewFileOpenDialog(hWnd))
                {
                    // This is a CDM_SETDEFEXT message
                    // lParam is the extension

                    // No memory? If the alloc fails, we eat the results.
                    ALLOCRETURN ar = GodotToAcpOnHeap((LPWSTR)lParamW, &(LPSTR)lParam);
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, lpPrevWndFunc, 
                                            lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                    if(ar == arAlloc)
                        GodotHeapFree((LPSTR)lParam);
                }
                else
                {
                    retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, 
                                            lpPrevWndFunc, lpCallBack, dwData, 
                                            fuFlags, uTimeout, lpdwResult);
                }
                break;

            case EM_GETPASSWORDCHAR:
            {
                // All these messages require that the (single char) retval be converted to Unicode
                // CONSIDER: is this always single character?
                LRESULT retvalA = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, 
                                         lpPrevWndFunc, lpCallBack, dwData, 
                                         fuFlags, uTimeout, lpdwResult);
                MultiByteToWideChar(g_acp, 0, 
                                    (char *)&retvalA, 
                                    g_mcs, 
                                    (WCHAR *)&retval, 
                                    sizeof(WCHAR));
                break;
            }

            case WM_CREATE:
            case WM_NCCREATE:
            {
                LPCREATESTRUCTW lpcs = (LPCREATESTRUCTW)lParamW;
                CREATESTRUCTA csA;
                ALLOCRETURN arClass = arNoAlloc;
                ALLOCRETURN arName = arNoAlloc;

                ZeroMemory(&csA, sizeof(CREATESTRUCTA));
                csA.lpCreateParams  = lpcs->lpCreateParams;
                csA.hInstance       = lpcs->hInstance;
                csA.hMenu           = lpcs->hMenu;
                csA.hwndParent      = lpcs->hwndParent;
                csA.cy              = lpcs->cy;
                csA.cx              = lpcs->cx;
                csA.y               = lpcs->y;
                csA.x               = lpcs->x;
                csA.style           = lpcs->style;
                csA.dwExStyle       = lpcs->dwExStyle;
                arClass = GodotToAcpOnHeap(lpcs->lpszClass, &(LPSTR)(csA.lpszClass));
                arName = GodotToAcpOnHeap(lpcs->lpszName, &(LPSTR)(csA.lpszName));
                lParam = (LPARAM)&csA;
            
                retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParam, 
                                        lpPrevWndFunc, lpCallBack, dwData, 
                                        fuFlags, uTimeout, lpdwResult);
                if(arClass==arAlloc)
                    GodotHeapFree((LPSTR)csA.lpszClass);
                if(arName==arAlloc)
                    GodotHeapFree((LPSTR)csA.lpszName);
                break;
            }
            
        case WM_MDICREATE:
            // wParam is not used, lParam is a pointer to an MDICREATESTRUCT structure containing 
            // information that the system uses to create the MDI child window. 
            {
                LPMDICREATESTRUCTW lpmcsi = (LPMDICREATESTRUCTW)lParamW;
                MDICREATESTRUCTA mcsiA;
                ALLOCRETURN arClass = arNoAlloc;
                ALLOCRETURN arTitle = arNoAlloc;
                
                ZeroMemory(&mcsiA, sizeof(MDICREATESTRUCTA));
                mcsiA.hOwner    = lpmcsi->hOwner;
                mcsiA.x         = lpmcsi->x;
                mcsiA.y         = lpmcsi->y;
                mcsiA.cx        = lpmcsi->cx;
                mcsiA.cy        = lpmcsi->cy;
                mcsiA.style     = lpmcsi->style;
                mcsiA.lParam    = lpmcsi->lParam;
                arClass = GodotToAcpOnHeap(lpmcsi->szClass, &(LPSTR)(mcsiA.szClass));
                arTitle = GodotToAcpOnHeap(lpmcsi->szTitle, &(LPSTR)(mcsiA.szTitle));

                retval = TransmitHelper(mt, hWnd, Msg, wParamW, (LPARAM)&mcsiA, 
                                         lpPrevWndFunc, lpCallBack, dwData, 
                                         fuFlags, uTimeout, lpdwResult);
                if(arClass==arAlloc)
                    GodotHeapFree((LPSTR)mcsiA.szClass);
                if(arTitle==arAlloc)
                    GodotHeapFree((LPSTR)mcsiA.szTitle);
            }
            break;

        default:
            // Lets get our registered messages, if we haven't yet.
            if(!msgHELPMSGSTRING)
                msgHELPMSGSTRING = RegisterWindowMessage(HELPMSGSTRINGA);
            if(!msgFINDMSGSTRING)
                msgFINDMSGSTRING = RegisterWindowMessage(FINDMSGSTRINGA);

            if(((Msg == msgFINDMSGSTRING) || (Msg == msgHELPMSGSTRING)) && 
                    ((((LPFINDREPLACEW)lParamW)->lStructSize) == sizeof(FINDREPLACEA)))
            {
                LPFINDREPLACEW lpfr;
                
                // No translation needed
                retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, 
                                        lpPrevWndFunc, lpCallBack, dwData, 
                                        fuFlags, uTimeout, lpdwResult);

                lpfr = (LPFINDREPLACEW)lParamW;
                
                if((lpfr->Flags & FR_DIALOGTERM) &&
                   ((lpfr->lpfnHook == &FRHookProcFind) || (lpfr->lpfnHook == &FRHookProcReplace)))
                {
                    // Now handle our cleanup. I do not think this should
                    // be needed, but it can't hurt to do it just in case.
                    LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);

                    // They are destroying the dialog, so unhook ourselves
                    // and clean up the dialog (if we have not done it yet). 
                    if(lpfr->lpfnHook == &FRHookProcFind) 
                    {
                        // Find dialog, not yet cleaned up
                        lpfr->lpfnHook = lpgti->pfnFindText;
                        if(lpfr->lpfnHook == NULL)
                            lpfr->Flags &= ~FR_ENABLEHOOK;
                    }
                    else if(lpfr->lpfnHook == &FRHookProcReplace) 
                    {
                        // Replace dialog, not yet cleaned up
                        lpfr->lpfnHook = lpgti->pfnReplaceText;
                        if(lpfr->lpfnHook == NULL)
                            lpfr->Flags &= ~FR_ENABLEHOOK;
                    }
                }
            }
            else if((Msg == msgHELPMSGSTRING) && 
                    ((LPCHOOSEFONTA)lParamW)->lStructSize == sizeof(CHOOSEFONTA))
            {
                LPCHOOSEFONTW lpcf = (LPCHOOSEFONTW)lParamW;
                CHOOSEFONTA cfA;
                ALLOCRETURN ar = arNoAlloc;

                // lParam is an LPCHOOSEFONTW to be converted. Copy all the
                // members that the user might expect.
                ZeroMemory(&cfA, sizeof(CHOOSEFONTA));
                cfA.lStructSize     = sizeof(CHOOSEFONTA);
                cfA.hDC             = lpcf->hDC;
                LogFontAfromW(cfA.lpLogFont, lpcf->lpLogFont);
                cfA.iPointSize      = lpcf->iPointSize;
                cfA.Flags           = lpcf->Flags;
                cfA.rgbColors       = lpcf->rgbColors;
                cfA.lCustData       = lpcf->lCustData;
                ar = GodotToAcpOnHeap(lpcf->lpszStyle, &(cfA.lpszStyle));
                cfA.nFontType       = lpcf->nFontType;
                cfA.nSizeMin        = lpcf->nSizeMin;
                cfA.nSizeMax        = lpcf->nSizeMax;

                retval = TransmitHelper(mt, hWnd, Msg, wParamW, (LPARAM)&cfA, lpPrevWndFunc, 
                                        lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                if(ar==arAlloc)
                    GodotHeapFree(cfA.lpszStyle);
                break;
            }
            else
            {
                // No translation needed
                retval = TransmitHelper(mt, hWnd, Msg, wParamW, lParamW, lpPrevWndFunc, 
                                        lpCallBack, dwData, fuFlags, uTimeout, lpdwResult);
                break;
            }
        }
    }

    return (retval);
}

/*-------------------------------
    TransmitHelper

    Our helper function that handles the proper one of 
    the twelve functions that call GodotTransmitMessage
-------------------------------*/
LRESULT TransmitHelper(
    MESSAGETYPES mt,
    HWND hWnd,
    UINT Msg,
    WPARAM wParam,
    LPARAM lParam,
    WNDPROC lpPrevWndFunc,  
    SENDASYNCPROC lpCallBack,
    ULONG_PTR dwData,
    UINT fuFlags,
    UINT uTimeout,
    PDWORD_PTR lpdwResult
)
{
    LRESULT RetVal;

    switch(mt)
    {
    case mtSendMessage:
    {
        // We have to special case the WM_MDICREATE case, because it creates a window and we 
        // thus have to do the hook, etc. Note that currently it is only done in SendMessage, 
        // not in any other call -- this matches the docs, and we cannot risk hitting a 
        // recursive situation here.
        if(Msg==WM_MDICREATE)
        {
            LPGODOTTLSINFO lpgti = GetThreadInfoSafe(TRUE);

            if(lpgti)
                INIT_WINDOW_SNIFF(lpgti->hHook);

            RetVal=SendMessageA(hWnd, Msg, wParam, lParam);

            if(lpgti)
                TERM_WINDOW_SNIFF(lpgti->hHook);
        }
        else
        {
            RetVal=SendMessageA(hWnd, Msg, wParam, lParam);
        }

        break;
    }
    case mtSendMessageCallback:
        RetVal=(LRESULT)SendMessageCallbackA(hWnd, Msg, wParam, lParam, lpCallBack, dwData);
        break;
    case mtSendMessageTimeout:
        RetVal=SendMessageTimeoutA(hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult);
        break;
    case mtSendNotifyMessage:
        RetVal=(LRESULT)SendNotifyMessageA(hWnd, Msg, wParam, lParam);
        break;
    case mtPostMessage:
        RetVal=(LRESULT)PostMessageA(hWnd, Msg, wParam, lParam);
        break;
    case mtPostThreadMessage:
        // hWnd is overloaded in this case to be the thread ID
        RetVal=(LRESULT)PostThreadMessageA((DWORD)hWnd, Msg, wParam, lParam);
        break;
    case mtCallWindowProc:
    case mtCallWindowProcA:
    {
        WNDPROC lpfn = WndprocFromFauxWndproc(hWnd, lpPrevWndFunc, fptUnknown);
        
        // If the wndproc was not a "faux" one or if the wndproc
        // is expecting ANSI (or both!), then use CallWindowProcA,
        // since that is what the wndproc would expect. Otherwise,
        // use Unicode and call the function directly.
        if((lpfn==lpPrevWndFunc) || (DoesProcExpectAnsi(hWnd, lpfn, fptUnknown)))
        {
            if(lpfn)
                RetVal=((CallWindowProcA)(lpfn, hWnd, Msg, wParam, lParam));
        }
        else
        {
            if(lpfn)
                RetVal=((* lpfn)(hWnd, Msg, wParam, lParam));
        }
        break;
    }
    case mtDefWindowProc:
        RetVal=DefWindowProcA(hWnd, Msg, wParam, lParam);
        break;
    case mtDefDlgProc:
        RetVal=DefDlgProcA(hWnd, Msg, wParam, lParam);
        break;
    case mtDefFrameProc:
        // dwData is overload in this case to ve the second hWnd param
        RetVal=DefFrameProcA(hWnd, (HWND)dwData, Msg, wParam, lParam);
        break;
    case mtDefMDIChildProc:
        RetVal=DefMDIChildProcA(hWnd, Msg, wParam, lParam);
        break;
    case mtBroadcastSystemMessage:
        if (s_pfnBSMA == NULL)
        {
            s_pfnBSMA = (PFNbsma)GetProcAddress(GetUserHandle(), "BroadcastSystemMessageA");
            if (s_pfnBSMA == NULL)
                s_pfnBSMA = (PFNbsma)GetProcAddress(GetUserHandle(), "BroadcastSystemMessage");
        }

        if (s_pfnBSMA)
            // fuFlags is overloaded here as the dwFlags broadcast options
            // lpdwResult is overloaded here as the passed in lpdwRecipients
            RetVal=(s_pfnBSMA((DWORD)fuFlags, lpdwResult, Msg, wParam, lParam));
        else
        {
            // Should be impossible!!!
            SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
            RetVal=(-1);
        }
        break;
    default:
        // Should also be impossible!!!
        RetVal=(0);
        break;
    }

    return(RetVal);
}

/*-------------------------------
    GodotReceiveMessage

        Our global wrapper for getting messages (GetMessage, et. al.). Does all precall
        and postcall conversions needed for any string values that are used
-------------------------------*/
BOOL GodotReceiveMessage(MESSAGETYPES mt, LPMSG lpMsg, HWND hWnd, UINT wMin, UINT wMax, UINT wRemoveMsg)
{
    BOOL retval;
    MSG MsgA;

    // call stuff
    switch(mt)
    {
    case mtGetMessage:
        retval = (GetMessageA)(&MsgA, hWnd, wMin, wMax);
        break;
    case mtPeekMessage:
        retval = (PeekMessageA)(&MsgA, hWnd, wMin, wMax, wRemoveMsg);
        break;
    }

    // Copy some defaults (we will override for specific messages, as needed)
    lpMsg->wParam = MsgA.wParam;
    lpMsg->lParam = MsgA.lParam;
    lpMsg->hwnd = MsgA.hwnd;
    lpMsg->message = MsgA.message;
    lpMsg->time = MsgA.time;
    lpMsg->pt.x = MsgA.pt.x;
    lpMsg->pt.y = MsgA.pt.y;

    // The caller is always expecting Unicode here, so we must ALWAYS convert.
    switch(MsgA.message)
    {
        case EM_SETPASSWORDCHAR:
        case WM_CHAR:
        case WM_DEADCHAR:
        case WM_SYSCHAR:
        case WM_SYSDEADCHAR:
        case WM_MENUCHAR:
        case WM_IME_CHAR:
        case WM_IME_COMPOSITION:
            // All these messages require the wParam to be converted to Unicode
            MultiByteToWideChar(g_acp, 0, (char *)&(MsgA.wParam), g_mcs, (WCHAR *)&(lpMsg->wParam), 1);
            break;

        case WM_CHARTOITEM:
        {
            // Mask off the hiword bits, convert, then stick the hiword bits back on.
            WPARAM wpT = MsgA.wParam & 0xFFFF;
            MultiByteToWideChar(g_acp, 0, (char *)&(wpT), g_mcs, (WCHAR *)&(lpMsg->wParam), 1);
            lpMsg->wParam = MAKELONG(LOWORD(wpT),HIWORD(MsgA.wParam));
            break;
        }

        case CB_ADDSTRING:
        case CB_DIR:
        case CB_FINDSTRING:
        case CB_FINDSTRINGEXACT:
        case CB_INSERTSTRING:
        case CB_SELECTSTRING:
        {
            LONG styl = GetWindowLongA(hWnd, GWL_STYLE);
            if(!(((styl & CBS_OWNERDRAWFIXED) || 
               (styl & CBS_OWNERDRAWVARIABLE)) &&
               (!(styl & CBS_HASSTRINGS))))
            {
                // Owner draw combo box which does not have strings stored
                // (See Windows Bugs # 356304 for details here)
                if(FSTRING_VALID((LPSTR)(MsgA.lParam)))
                {
                    MultiByteToWideChar(g_acp, 0, 
                                        (LPSTR)(MsgA.lParam), ((MsgA.wParam) * g_mcs), 
                                        (LPWSTR)(lpMsg->lParam), MsgA.wParam);
                }
            }
        }
            break;

        case LB_ADDSTRING:
        case LB_ADDFILE:
        case LB_DIR:
        case LB_FINDSTRING:
        case LB_FINDSTRINGEXACT:
        case LB_INSERTSTRING:
        case LB_SELECTSTRING:
        {
            LONG styl = GetWindowLongA(hWnd, GWL_STYLE);
            if(!(((styl & LBS_OWNERDRAWFIXED) || 
               (styl & LBS_OWNERDRAWVARIABLE)) &&
               (!(styl & LBS_HASSTRINGS))))
            {
                // Owner draw listbox which does not have strings stored
                // (See Windows Bugs # 356304 for details here)
                if(FSTRING_VALID((LPSTR)(MsgA.lParam)))
                {
                    MultiByteToWideChar(g_acp, 0, 
                                        (LPSTR)(MsgA.lParam), ((MsgA.wParam) * g_mcs), 
                                        (LPWSTR)(lpMsg->lParam), MsgA.wParam);
                }
            }
        }
            break;

            case EM_REPLACESEL:
            case WM_SETTEXT:
            case WM_DEVMODECHANGE:
            case WM_SETTINGCHANGE:
            case WM_SETMESSAGESTRING: // MFC internal msg
                if(FSTRING_VALID((LPSTR)(MsgA.lParam)))
                {
                    // All these messages require the lParam to be converted to Unicode
                    // CONSIDER: Is that string buffer size right?
                    MultiByteToWideChar(g_acp, 0, 
                                        (LPSTR)(MsgA.lParam), ((MsgA.wParam) * g_mcs), 
                                        (LPWSTR)(lpMsg->lParam), MsgA.wParam);
                }


        case WM_DDE_EXECUTE:
            // wParam is the client window hWnd, lParam is the command LPTSTR.
            // Only convert lParam if both client and server windows are Unicode
            if(GetUnicodeWindowProp((HWND)lpMsg->wParam))
            {
                MultiByteToWideChar(g_acp, 0, 
                                    (LPSTR)(MsgA.lParam), ((MsgA.wParam) * g_mcs), 
                                    (LPWSTR)(lpMsg->lParam), MsgA.wParam);
            }
            break;
    }
    
    // Get out of here
    return (retval);

}

/*-------------------------------
    GodotDispatchMessage

    Handles all the message dispatch functions
-------------------------------*/
LRESULT GodotDispatchMessage(MESSAGETYPES mt, HWND hDlg, HACCEL hAccTable, LPMSG lpMsg)
{
    // Begin locals
    LRESULT RetVal;
    ALLOCRETURN ar = arNoAlloc;
    MSG MsgA;

    // We will override some of these params later, as needed
    MsgA.wParam = lpMsg->wParam;
    MsgA.lParam = lpMsg->lParam;
    MsgA.hwnd = lpMsg->hwnd;
    MsgA.message = lpMsg->message;
    MsgA.time = lpMsg->time;
    MsgA.pt = lpMsg->pt;
    
    // The caller is always passing Unicode here, so we must ALWAYS convert.
    switch(lpMsg->message)
    {
        case EM_SETPASSWORDCHAR:
        case WM_CHAR:
        case WM_DEADCHAR:
        case WM_SYSCHAR:
        case WM_SYSDEADCHAR:
        case WM_MENUCHAR:
        case WM_IME_CHAR:
        case WM_IME_COMPOSITION:
            // All these messages require the wParam to be converted from Unicode
            WideCharToMultiByte(g_acp, 0, 
                                (WCHAR *)&(lpMsg->wParam), 1, 
                                (CHAR *)&(MsgA.wParam), g_mcs, NULL, NULL);
            break;

        case WM_CHARTOITEM:
        {
            // Mask off the hiword bits, convert, then stick the hiword bits back on.
            WPARAM wpT = lpMsg->wParam & 0xFFFF;
            WideCharToMultiByte(g_acp, 0, 
                                (WCHAR *)&(wpT), 1, 
                                (CHAR *)&(MsgA.wParam), g_mcs, NULL, NULL);
            MsgA.wParam = MAKELONG(LOWORD(wpT),HIWORD(lpMsg->wParam));
            break;
        }

        case CB_ADDSTRING:
        case LB_ADDFILE:
        case CB_DIR:
        case CB_FINDSTRING:
        case CB_FINDSTRINGEXACT:
        case CB_INSERTSTRING:
        case CB_SELECTSTRING:
        {
            LONG styl = GetWindowLongA(hDlg, GWL_STYLE);
            if(!(((styl & CBS_OWNERDRAWFIXED) || 
               (styl & CBS_OWNERDRAWVARIABLE)) &&
               (!(styl & CBS_HASSTRINGS))))
            {
                // Owner draw combobox which does not have strings stored
                // (See Windows Bugs # 356304 for details here)
                ar = GodotToAcpOnHeap((LPWSTR)(lpMsg->lParam), &(LPSTR)(MsgA.lParam));
            }
            break;
        }
                
        case LB_ADDSTRING:
        case LB_DIR:
        case LB_FINDSTRING:
        case LB_FINDSTRINGEXACT:
        case LB_INSERTSTRING:
        case LB_SELECTSTRING:
        {
            LONG styl = GetWindowLongA(hDlg, GWL_STYLE);
            if(!(((styl & LBS_OWNERDRAWFIXED) || 
               (styl & LBS_OWNERDRAWVARIABLE)) &&
               (!(styl & LBS_HASSTRINGS))))
            {
                // Owner draw listbox which does not have strings stored
                // (See Windows Bugs # 356304 for details here)
                ar = GodotToAcpOnHeap((LPWSTR)(lpMsg->lParam), &(LPSTR)(MsgA.lParam));
            }
            break;
        }

        case EM_REPLACESEL:
        case WM_SETTEXT:
        case WM_DEVMODECHANGE:
        case WM_SETTINGCHANGE:
        case WM_SETMESSAGESTRING: // MFC internal msg
            // All these messages require the lParam to be converted from Unicode
            // It is a full string, so lets treat it as such.
            ar = GodotToAcpOnHeap((LPWSTR)(lpMsg->lParam), &(LPSTR)(MsgA.lParam));
            break;

        case WM_DDE_EXECUTE:
            // wParam is the client window hWnd, lParam is the command LPTSTR.
            // Only convert lParam if both client and server windows are Unicode
            if(GetUnicodeWindowProp((HWND)lpMsg->wParam))
            {
                ar = GodotToAcpOnHeap((LPWSTR)(lpMsg->lParam), &(LPSTR)(MsgA.lParam));
            }
            break;
    }
    
    switch(mt)
    {
        case mtDispatchMessage:
            RetVal=DispatchMessageA(&MsgA);
            break;
        case mtIsDialogMessage:
            RetVal=(LRESULT)IsDialogMessageA(hDlg, &MsgA);
            break;
        case mtTranslateAccelerator:
            RetVal = (LRESULT)TranslateAcceleratorA(hDlg, hAccTable, &MsgA);
            break;
    }

    // If we used some heap memory then free it now
    if(ar == arAlloc)
        GodotHeapFree((LPSTR)(MsgA.lParam));

    return(RetVal);
}