        /*
    ###########################################################################
    # message loops
    #
    # $Id: GUI_MessageLoops.cpp,v 1.11 2005/07/01 23:46:28 robertemay Exp $
    #
    ###########################################################################
        */

#include "GUI.h"
// #pragma optimize( "", off )

    /*
    ###########################################################################
    # (@)INTERNAL:CommonMsgLoop(hwnd, uMsg, wParam, lParam)
    # this is the message loop (WndProc) that process common messages
    # (eventmodel independent)
    */
LRESULT CommonMsgLoop(NOTXSPROC HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, WNDPROC wndprocOriginal)
{
    LPPERLWIN32GUI_USERDATA perlud;
#ifdef PERLWIN32GUI_STRONGDEBUG
    printf("!XS(CommonMsgLoop) got (0x%x, 0x%x, 0x%x, 0x%x)\n", hwnd, uMsg, wParam, lParam);
#endif
    switch(uMsg) {
    case WM_PAINT:
        // Create compatible DC for window.
        // WM_PRINT into new DC with PRF_ERASEBKGND .
        // BitBlt new DC into window DC.

        perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong((HWND) hwnd, GWL_USERDATA);
        if(perlud && (perlud->dwPlStyle & PERLWIN32GUI_FLICKERFREE)) {
            WINDOWINFO pwi;
            RECT cr;
            pwi.cbSize = sizeof(WINDOWINFO);

            GetWindowInfo(hwnd,&pwi);

            LONG width  = pwi.rcClient.right - pwi.rcClient.left;
            LONG height = pwi.rcClient.bottom - pwi.rcClient.top;
            cr.left = 0;
            cr.right = width;
            cr.top = 0;
            cr.bottom = height;

            LONG offsetx = pwi.rcClient.left - pwi.rcWindow.left;
            LONG offsety = pwi.rcClient.top - pwi.rcWindow.top;

            // Get window DC:
            HDC hdc = GetDC(hwnd);
            // Create compatible DC for window:
            HDC hdc2 = CreateCompatibleDC(hdc);

            HBITMAP hbmp = CreateCompatibleBitmap(hdc, width + offsetx, height + offsety);
            HBITMAP holdbmp = (HBITMAP) SelectObject(hdc2, hbmp);

            HBRUSH bgBrush    = (HBRUSH) GetClassLong (hwnd, GCL_HBRBACKGROUND);

            cr.right  += offsetx;
            cr.bottom += offsety;
            FillRect(hdc2, &cr, bgBrush);
            // Sent WM_PRINT message to draw into new DC .
            SendMessage(hwnd, WM_PRINT, (WPARAM) hdc2, PRF_CLIENT | PRF_NONCLIENT | PRF_CHILDREN);
            // BitBlt new DC into window DC.
            BitBlt(hdc, 0, 0, width, height, hdc2, offsetx, offsety, SRCCOPY);

            SelectObject(hdc2, holdbmp);
            ValidateRect(hwnd, NULL);
            DeleteObject(hbmp);
            DeleteDC(hdc2);
            ReleaseDC(hwnd, hdc);
            return (LRESULT) 0;
        }
        break;
    case WM_ERASEBKGND:
        perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong((HWND) hwnd, GWL_USERDATA);
        if(perlud && (perlud->dwPlStyle & PERLWIN32GUI_FLICKERFREE)) {
           return (LRESULT) 1;
        }
        break;
    case WM_CTLCOLOREDIT:
    case WM_CTLCOLORSTATIC:
    case WM_CTLCOLORBTN:
    case WM_CTLCOLORLISTBOX:
        {
            perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong((HWND) lParam, GWL_USERDATA);
            if( ValidUserData(perlud) ) {
                if(uMsg == WM_CTLCOLORSTATIC)
                    SetBkMode((HDC) wParam, TRANSPARENT);
                if(perlud->clrForeground != CLR_INVALID)
                    SetTextColor((HDC) wParam, perlud->clrForeground);
                if(perlud->clrBackground != CLR_INVALID) {
                    SetBkColor((HDC) wParam, perlud->clrBackground);
                    return ((LRESULT) perlud->hBackgroundBrush);
                }
            }

            switch(uMsg) {
            case WM_CTLCOLOREDIT:
            case WM_CTLCOLORLISTBOX:
                return ((LRESULT) GetSysColorBrush(COLOR_WINDOW));
                break;
            default:
                return ((LRESULT) GetSysColorBrush(COLOR_BTNFACE));
                break;
            }
        }
        break;

    case WM_GETMINMAXINFO:
        {
            LPMINMAXINFO minmax;
            LPPERLWIN32GUI_USERDATA perlud;
            perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(hwnd, GWL_USERDATA);
            if( ValidUserData(perlud) && !(perlud->dwPlStyle & PERLWIN32GUI_MDICHILD) ) {
                minmax = (LPMINMAXINFO) lParam;
                if(perlud->iMinWidth  != -1) minmax->ptMinTrackSize.x = (LONG) perlud->iMinWidth;
                if(perlud->iMaxWidth  != -1) minmax->ptMaxTrackSize.x = (LONG) perlud->iMaxWidth;
                if(perlud->iMinHeight != -1) minmax->ptMinTrackSize.y = (LONG) perlud->iMinHeight;
                if(perlud->iMaxHeight != -1) minmax->ptMaxTrackSize.y = (LONG) perlud->iMaxHeight;
                return 0;
            }
        }
        break;

    case WM_SETCURSOR:
        {
            WORD nHitTest = LOWORD( lParam );
            if( nHitTest == HTCLIENT ) {  // only diddle cursor in client areas
                LPPERLWIN32GUI_USERDATA perlud;
                perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong((HWND) wParam, GWL_USERDATA);
                if( ValidUserData(perlud) && perlud->hCursor != NULL ) {
                    SetCursor( perlud->hCursor );
                    return TRUE;
                }
            }
            break;
        }
    }
#ifdef PERLWIN32GUI_STRONGDEBUG
    printf("!XS(CommonMsgLoop) returning DefWindowProc\n");
#endif

    if (wndprocOriginal != NULL) {
        return CallWindowProc((WNDPROC_CAST) wndprocOriginal, hwnd, uMsg, wParam, lParam);
    } else {
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
}

    /*
    ###########################################################################
    # (@)INTERNAL:WindowMsgLoop(hwnd, uMsg, wParam, lParam)
    # message loop for Main Window
    */
LRESULT CALLBACK WindowMsgLoop(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LPPERLWIN32GUI_USERDATA perlud;
    LPPERLWIN32GUI_USERDATA childud;
    int PerlResult = 1;

#ifdef PERLWIN32GUI_STRONGDEBUG
    printf("!XS(WindowMsgLoop) got (0x%x, 0x%x, 0x%x, 0x%x)\n", hwnd, uMsg, wParam, lParam);
#endif

    /*
     * WM_CREATE && WM_NCCREATE
     * If we handle this message we are using a custom control class (See RegisterClassEx).
     */
    if(uMsg == WM_CREATE || uMsg == WM_NCCREATE)
    {
        perlud = (LPPERLWIN32GUI_USERDATA) ((CREATESTRUCT *) lParam)->lpCreateParams;
        if(perlud != NULL) {
            PERLUD_FETCH;
            SetWindowLong(hwnd, GWL_USERDATA, (long) perlud);
            SwitchBit(perlud->dwPlStyle, PERLWIN32GUI_CUSTOMCLASS, 1);  // Set Custom class flag

            // Search for an extend MsgLoop procedure (-extends option in RegisterClassEx)
            perlud->WndProc = (LWNDPROC_CAST) GetDefClassProc (NOTXSCALL ((CREATESTRUCT *) lParam)->lpszClass);
            if (perlud->WndProc) {
                return CallWindowProc((WNDPROC_CAST) perlud->WndProc, hwnd, uMsg, wParam, lParam);
            }
        }

        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(hwnd, GWL_USERDATA);
    if(!ValidUserData(perlud)) {
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    perlud->forceResult = 0;

    PERLUD_FETCH;

    switch(uMsg) {

    case WM_COMMAND:

        /*
         * Menu command
         */
        if(HIWORD(wParam) == 0 && lParam == 0) {
           /*
            * (@)EVENT:Click()
            * Sent when the users choose a menu point.
            * (@)APPLIES_TO:Menu
            */
            PerlResult = DoEvent_Menu(NOTXSCALL hwnd, LOWORD(wParam));
        }
        /*
         * Accelerator command
         */
        else if(HIWORD(wParam) == 1 && lParam == 0) {
            /*
            * (@)EVENT:Click()
            * Sent when the users triggers an Accelerator object.
            * (@)APPLIES_TO:AcceleratorTable
            */
            PerlResult = DoEvent_Accelerator(NOTXSCALL perlud, LOWORD(wParam));
        }
        /*
         * Control command
         */
        else {
            childud = (LPPERLWIN32GUI_USERDATA) GetWindowLong((HWND) lParam, GWL_USERDATA);
            if( ValidUserData(childud) ) {
                childud->forceResult = 0;

                PerlResult = OnEvent[childud->iClass](NOTXSCALL childud, uMsg, wParam, lParam);

                if (childud->avHooks != NULL)
                    DoHook(NOTXSCALL childud, (UINT) HIWORD(wParam), wParam, lParam, &PerlResult, WM_COMMAND);

                if(childud->forceResult != 0) {
                    perlud->forceResult  = childud->forceResult;
                    childud->forceResult = 0;
                }
            }
        }
        break;

     case WM_NOTIFY:

        childud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(((LPNMHDR) lParam)->hwndFrom, GWL_USERDATA);
        if( ValidUserData(childud) ) {
            childud->forceResult = 0;

            /* Standard notification */
            switch(((LPNMHDR) lParam)->code) {
            case NM_CLICK:
                /*
                 * (@)EVENT:Click()
                 * (@)APPLIES_TO:*
                 */
                PerlResult = DoEvent(NOTXSCALL childud, PERLWIN32GUI_NEM_CLICK, "Click", -1 );
                break;
            case NM_RCLICK:
                /*
                 * (@)EVENT:RightClick()
                 * (@)APPLIES_TO:*
                 */
                 PerlResult = DoEvent(NOTXSCALL childud, PERLWIN32GUI_NEM_RIGHTCLICK, "RightClick", -1 );
                break;
            case NM_DBLCLK:
                /*
                 * (@)EVENT:DblClick()
                 * (@)APPLIES_TO:*
                 */
                PerlResult = DoEvent(NOTXSCALL childud, PERLWIN32GUI_NEM_DBLCLICK, "DblClick", -1 );
                break;
            case NM_RDBLCLK:
                /*
                 * (@)EVENT:DblRightClick()
                 * (@)APPLIES_TO:*
                 */
                PerlResult = DoEvent(NOTXSCALL childud, PERLWIN32GUI_NEM_DBLRIGHTCLICK, "DblRightClick", -1 );
                break;
            case NM_SETFOCUS:
                /*
                 * (@)EVENT:GotFocus()
                 * (@)APPLIES_TO:*
                 */
                PerlResult = DoEvent(NOTXSCALL childud, PERLWIN32GUI_NEM_GOTFOCUS, "GotFocus", -1 );
                break;
            case NM_KILLFOCUS:
                /*
                 * (@)EVENT:LostFocus()
                 * (@)APPLIES_TO:*
                 */
                PerlResult = DoEvent(NOTXSCALL childud, PERLWIN32GUI_NEM_LOSTFOCUS, "LostFocus", -1 );
                break;
            default :
                PerlResult = OnEvent[childud->iClass](NOTXSCALL childud, uMsg, wParam, lParam);
                break;
            }

            if (childud->avHooks != NULL)
                DoHook(NOTXSCALL childud, (UINT) (((LPNMHDR) lParam)->code), wParam, lParam, &PerlResult, WM_NOTIFY);

            if(childud->forceResult != 0) {
                perlud->forceResult  = childud->forceResult;
                childud->forceResult = 0;
            }
        }
        break;

    case WM_DESTROY:
        if (perlud->WndProc)
            PerlResult = CallWindowProc((WNDPROC_CAST) perlud->WndProc, hwnd, uMsg, wParam, lParam);
        else
            PerlResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
        PERLUD_FREE;
        return PerlResult;

    case WM_MOUSEMOVE:
        /*
         * (@)EVENT:MouseMouve()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_MOUSEMOVE, "MouseMove",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_LBUTTONDOWN:
        /*
         * (@)EVENT:MouseDown()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_LMOUSEDOWN, "MouseDown",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_LBUTTONUP:
        /*
         * (@)EVENT:MouseUp()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_LMOUSEUP, "MouseUp",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_LBUTTONDBLCLK:
        /*
         * (@)EVENT:MouseDblClick()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_LMOUSEDBLCLK, "MouseDblClick",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_RBUTTONDOWN:
        /*
         * (@)EVENT:MouseRightDown()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_RMOUSEDOWN, "MouseRightDown",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_RBUTTONUP:
        /*
         * (@)EVENT:MouseRightUp()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_RMOUSEUP, "MouseRightUp",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_RBUTTONDBLCLK:
        /*
         * (@)EVENT:MouseRightDblClick()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_RMOUSEDBLCLK, "MouseRightDblClick",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_MBUTTONDOWN:
        /*
         * (@)EVENT:MouseMiddleDown()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_MMOUSEDOWN, "MouseMiddleDown",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_MBUTTONUP:
        /*
         * (@)EVENT:MouseMiddleUp()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_MMOUSEUP, "MouseMiddleUp",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_MBUTTONDBLCLK:
        /*
         * (@)EVENT:MouseMiddleDblClick()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_MMOUSEDBLCLK, "MouseMiddleDblClick",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_KEYDOWN:
        /*
         * (@)EVENT:KeyDown()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_KEYDOWN, "KeyDown",
            PERLWIN32GUI_ARGTYPE_LONG, lParam,
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
        /*
         * (@)EVENT:KeyUp()
         * (@)APPLIES_TO:*
         */
    case WM_KEYUP:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_KEYUP, "KeyUp",
            PERLWIN32GUI_ARGTYPE_LONG, lParam,
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
        /*
         * (@)EVENT:Char()
         * (@)APPLIES_TO:*
         */
    case WM_CHAR:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_CHAR, "Char",
            PERLWIN32GUI_ARGTYPE_LONG, lParam,
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_MOUSEHOVER :
        /*
         * (@)EVENT:MouseOver()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_MOUSEOVER, "MouseOver", -1);
        if(PerlResult) {
            TRACKMOUSEEVENT tme;
            tme.cbSize = sizeof(TRACKMOUSEEVENT);
            tme.hwndTrack = hwnd;
            tme.dwFlags = TME_QUERY;
            if(_TrackMouseEvent( &tme )) {
                _TrackMouseEvent( &tme );
            }
        }
        break;
    case WM_MOUSELEAVE :
        /*
         * (@)EVENT:MouseOut()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_MOUSEOUT, "MouseOut", -1);
        if(PerlResult) {
            TRACKMOUSEEVENT tme;
            tme.cbSize = sizeof(TRACKMOUSEEVENT);
            tme.hwndTrack = hwnd;
            tme.dwFlags = TME_QUERY;
            if(_TrackMouseEvent( &tme )) {
                _TrackMouseEvent( &tme );
            }
        }
        break;

    case WM_PAINT :
    case WM_ACTIVATE :
    case WM_SYSCOMMAND:
    case WM_SIZE:
    case WM_MDIACTIVATE:
    case WM_SETFOCUS:
    case WM_INITMENU:
        PerlResult = OnEvent[perlud->iClass](NOTXSCALL perlud, uMsg, wParam, lParam);
        break;

    case WM_HSCROLL:
    case WM_VSCROLL:
        childud = (LPPERLWIN32GUI_USERDATA) GetWindowLong((HWND) lParam, GWL_USERDATA);
        if (ValidUserData(childud))
            PerlResult = OnEvent[childud->iClass](NOTXSCALL childud, uMsg, wParam, lParam);
        else
            PerlResult = OnEvent[perlud->iClass](NOTXSCALL perlud, uMsg, wParam, lParam);
        break;

    case WM_TIMER:
        /*
         * (@)EVENT:Timer()
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent_Timer (NOTXSCALL perlud, (int) wParam, PERLWIN32GUI_NEM_TIMER, "Timer", -1);
        break;

    case WM_NOTIFYICON:

        switch(lParam) {
        case WM_LBUTTONDOWN:
            /*
             * (@)EVENT:Click()
             * Sent when the user clicks the left mouse button on
             * a NotifyIcon.
             * (@)APPLIES_TO:NotifyIcon
             */
            PerlResult = DoEvent_NotifyIcon (NOTXSCALL perlud, (int) wParam, "Click", -1);
            break;
        case WM_LBUTTONDBLCLK:
            /*
             * (@)EVENT:DblClick()
             * Sent when the user double clicks the left mouse button on
             * a NotifyIcon.
             * (@)APPLIES_TO:NotifyIcon
             */
            PerlResult = DoEvent_NotifyIcon (NOTXSCALL perlud, (int) wParam, "DblClick", -1);
            break;
        case WM_RBUTTONDOWN:
            /*
             * (@)EVENT:RightClick()
             * Sent when the user clicks the right mouse button on
             * a NotifyIcon.
             * (@)APPLIES_TO:NotifyIcon
             */
            PerlResult = DoEvent_NotifyIcon (NOTXSCALL perlud, (int) wParam, "RightClick", -1);
            break;
        case WM_RBUTTONDBLCLK:
            /*
             * (@)EVENT:RightDblClick()
             * Sent when the user double clicks the right mouse button on
             * a NotifyIcon.
             * (@)APPLIES_TO:NotifyIcon
             */
            PerlResult = DoEvent_NotifyIcon (NOTXSCALL perlud, (int) wParam, "RightDblClick", -1);
            break;
        case WM_MBUTTONDOWN:
            /*
             * (@)EVENT:MiddleClick()
             * Sent when the user clicks the middle mouse button on
             * a NotifyIcon.
             * (@)APPLIES_TO:NotifyIcon
             */
            PerlResult = DoEvent_NotifyIcon (NOTXSCALL perlud, (int) wParam, "MiddleClick", -1);
            break;
        case WM_MBUTTONDBLCLK:
            /*
             * (@)EVENT:MiddleDblClick()
             * Sent when the user double clicks the middle mouse button on
             * a NotifyIcon.
             * (@)APPLIES_TO:NotifyIcon
             */
            PerlResult = DoEvent_NotifyIcon (NOTXSCALL perlud, (int) wParam, "MiddleDblClick", -1);
            break;
        default:
            /*
             * (@)EVENT:MouseEvent(MSG)
             * Sent when the user performs a mouse event on
             * a NotifyIcon; MSG is the message code.
             * (@)APPLIES_TO:NotifyIcon
             */
            PerlResult = DoEvent_NotifyIcon (NOTXSCALL perlud, (int) wParam, "MouseEvent",
                PERLWIN32GUI_ARGTYPE_LONG, lParam,
                -1);
            break;
        }
        break;

    case WM_DROPFILES:
        /*
         * (@)EVENT:DropFiles(DROP_HANDLE)
         * Sent when the window receives dropped files.
         * (@)APPLIES_TO:*
         */
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_DROPFILE, "DropFiles",
                        PERLWIN32GUI_ARGTYPE_LONG, (LONG) wParam,
                        -1);
        break;
    }

    // Hook processing
    if(perlud->avHooks != NULL) {
        DoHook(NOTXSCALL perlud, uMsg, wParam, lParam, &PerlResult);
    }

    // Default processing
    if(PerlResult == -1) {
        PostMessage(hwnd, WM_EXITLOOP, (WPARAM) -1, 0);
        PerlResult = 0;
    } else if (PerlResult != 0) {
        PerlResult = CommonMsgLoop(NOTXSCALL hwnd, uMsg, wParam, lParam, perlud->WndProc);
    }
    else if (perlud->forceResult != 0) {
        return perlud->forceResult;
    }

    return PerlResult;
}

    /*
    ###########################################################################
    # (@)INTERNAL:DefMDIFrameLoop(hwnd, uMsg, wParam, lParam)
    # Default message loop for Frame MDI window
    */
LRESULT CALLBACK DefMDIFrameLoop(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    LPPERLWIN32GUI_USERDATA perlud;

#ifdef PERLWIN32GUI_STRONGDEBUG
    printf("!XS(DefMDIFrameLoop) got (0x%x, 0x%x, 0x%x, 0x%x)\n", hwnd, uMsg, wParam, lParam);
#endif

    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(hwnd, GWL_USERDATA);
    if( !ValidUserData(perlud)) {
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    return DefFrameProc(hwnd, (HWND) perlud->dwData, uMsg, wParam, lParam);
}

    /*
    ###########################################################################
    # (@)INTERNAL:MDIFrame(hwnd, uMsg, wParam, lParam)
    # message loop for container control class
    */
LRESULT CALLBACK MDIFrameMsgLoop(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

#ifdef PERLWIN32GUI_STRONGDEBUG
    printf("!XS(MDIFrameMsgLoop) got (0x%x, 0x%x, 0x%x, 0x%x)\n", hwnd, uMsg, wParam, lParam);
#endif

    /*
     * WM_CREATE && WM_NCCREATE
     * If we handle this message we are using a custom control class (See RegisterClassEx).
     */
    if (uMsg == WM_CREATE || uMsg == WM_NCCREATE)
    {
        LPPERLWIN32GUI_USERDATA perlud = (LPPERLWIN32GUI_USERDATA) ((CREATESTRUCT *) lParam)->lpCreateParams;
        if(perlud != NULL) {
            PERLUD_FETCH;
            SetWindowLong(hwnd, GWL_USERDATA, (long) perlud);
            SwitchBit(perlud->dwPlStyle, PERLWIN32GUI_CUSTOMCLASS, 1);  // Set Custom class flag
            SwitchBit(perlud->dwPlStyle, PERLWIN32GUI_MDIFRAME   , 1);  // Set MDI Frame flag
            perlud->WndProc = (LWNDPROC_CAST) DefMDIFrameLoop;          // Set DefFrameProc

            if (perlud->WndProc) {
                return CallWindowProc((WNDPROC_CAST) perlud->WndProc, hwnd, uMsg, wParam, lParam);
            }
        }

        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    // Call WindowMsgLoop.
    return CallWindowProc((WNDPROC_CAST) WindowMsgLoop, hwnd, uMsg, wParam, lParam);
}

    /*
    ###########################################################################
    # (@)INTERNAL:MDIClientMsgLoop(hwnd, uMsg, wParam, lParam)
    # message loop for MDI Client class
    */

LRESULT CALLBACK MDIClientMsgLoop(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LPPERLWIN32GUI_USERDATA perlud;
    int PerlResult = 1;

#ifdef PERLWIN32GUI_STRONGDEBUG
    printf("!XS(MDIClientMsgLoop) got (0x%x, 0x%x, 0x%x, 0x%x)\n", hwnd, uMsg, wParam, lParam);
#endif

    /* Fetch perlud */
    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(hwnd, GWL_USERDATA);
    if( !ValidUserData(perlud)) {
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    if (perlud->WndProc)
        PerlResult = CallWindowProc((WNDPROC_CAST) perlud->WndProc, hwnd, uMsg, wParam, lParam);
    else
        PerlResult = DefWindowProc(hwnd, uMsg, wParam, lParam);

    if (uMsg == WM_DESTROY) {
        PERLUD_FETCH;
        PERLUD_FREE;
    }

    return PerlResult;
}

    /*
    ###########################################################################
    # (@)INTERNAL:DefMDIChildLoop(hwnd, uMsg, wParam, lParam)
    # Default message loop for Child MDI window
    */
LRESULT CALLBACK DefMDIChildLoop (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {

#ifdef PERLWIN32GUI_STRONGDEBUG
    printf("!XS(DefMDIChildLoop) got (0x%x, 0x%x, 0x%x, 0x%x)\n", hwnd, uMsg, wParam, lParam);
#endif
    return DefMDIChildProc(hwnd, uMsg, wParam, lParam);
}

    /*
    ###########################################################################
    # (@)INTERNAL:MDIChildMsgLoop(hwnd, uMsg, wParam, lParam)
    # message loop for MDI Child window class
    */
LRESULT CALLBACK MDIChildMsgLoop(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    /*
     * WM_CREATE && WM_NCCREATE
     * If we handle this message we are using a custom control class (See RegisterClassEx).
     */
    if (uMsg == WM_CREATE || uMsg == WM_NCCREATE)
    {
        LPMDICREATESTRUCT lpMdiCreate = (LPMDICREATESTRUCT) ((CREATESTRUCT *) lParam)->lpCreateParams;
        LPPERLWIN32GUI_USERDATA perlud = (LPPERLWIN32GUI_USERDATA) lpMdiCreate->lParam;
        if(perlud != NULL) {
            PERLUD_FETCH;
            SetWindowLong(hwnd, GWL_USERDATA, (long) perlud);
            SwitchBit(perlud->dwPlStyle, PERLWIN32GUI_CUSTOMCLASS, 1);  // Set Custom class flag
            SwitchBit(perlud->dwPlStyle, PERLWIN32GUI_MDICHILD   , 1);  // Set MDI Frame flag
            perlud->WndProc = (LWNDPROC_CAST) DefMDIChildLoop;          // Set DefMDIChildProc
            perlud->dwData = (DWORD) hwnd;                              // For fast hwnd acces (Activate/Deactivate)
            if (perlud->WndProc) {
                return CallWindowProc((WNDPROC_CAST) perlud->WndProc, hwnd, uMsg, wParam, lParam);
            }
        }

        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    // Call WindowMsgLoop.
    return CallWindowProc((WNDPROC_CAST) WindowMsgLoop, hwnd, uMsg, wParam, lParam);
}

    /*
    ###########################################################################
    # (@)INTERNAL:ControlMsgLoop(hwnd, uMsg, wParam, lParam)
    # ControlMsgLoop for subclassing base control and default WNDPROC control class.
    */
LRESULT CALLBACK ControlMsgLoop(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LPPERLWIN32GUI_USERDATA perlud;
    int PerlResult = 1;

    /*
     * WM_CREATE && WM_NCCREATE
     * If we handle this message we are using a custom control class (See RegisterClassEx).
     */
    if(uMsg == WM_CREATE || uMsg == WM_NCCREATE)
    {
        perlud = (LPPERLWIN32GUI_USERDATA) ((CREATESTRUCT *) lParam)->lpCreateParams;
        if(perlud != NULL) {
            PERLUD_FETCH;
            SetWindowLong(hwnd, GWL_USERDATA, (long) perlud);
            SwitchBit(perlud->dwPlStyle, PERLWIN32GUI_CUSTOMCLASS, 1);  // Set Custom class flag

            // Search for an extend MsgLoop procedure (-extends option in RegisterClassEx)
            perlud->WndProc = (LWNDPROC_CAST) GetDefClassProc (NOTXSCALL ((CREATESTRUCT *) lParam)->lpszClass);
            if (perlud->WndProc) {
                return CallWindowProc((WNDPROC_CAST) perlud->WndProc, hwnd, uMsg, wParam, lParam);
            }
        }

        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(hwnd, GWL_USERDATA);
    if( !ValidUserData(perlud)) {
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    perlud->forceResult = 0;

    PERLUD_FETCH;

    switch(uMsg) {
    case WM_DESTROY :
        if (perlud->WndProc)
            PerlResult = CallWindowProc((WNDPROC_CAST) perlud->WndProc, hwnd, uMsg, wParam, lParam);
        else
            PerlResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
         PERLUD_FREE;
         return PerlResult;
    case WM_TIMER:
        PerlResult = DoEvent_Timer (NOTXSCALL perlud, wParam, PERLWIN32GUI_NEM_TIMER, "Timer", -1);
        break;
    case WM_MOUSEMOVE:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_MOUSEMOVE, "MouseMove",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_LBUTTONDOWN:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_LMOUSEDOWN, "MouseDown",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_LBUTTONUP:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_LMOUSEUP, "MouseUp",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_LBUTTONDBLCLK:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_LMOUSEDBLCLK, "MouseDblClick",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_RBUTTONDOWN:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_RMOUSEDOWN, "MouseRightDown",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_RBUTTONUP:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_RMOUSEUP, "MouseRightUp",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_RBUTTONDBLCLK:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_RMOUSEDBLCLK, "MouseRightDblClick",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_MBUTTONDOWN:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_MMOUSEDOWN, "MouseMiddleDown",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_MBUTTONUP:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_MMOUSEUP, "MouseMiddleUp",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_MBUTTONDBLCLK:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_MMOUSEDBLCLK, "MouseMiddleDblClick",
            PERLWIN32GUI_ARGTYPE_LONG, LOWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, HIWORD(lParam),
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;

    case WM_KEYDOWN:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_KEYDOWN, "KeyDown",
            PERLWIN32GUI_ARGTYPE_LONG, lParam,
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_KEYUP:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_KEYUP, "KeyUp",
            PERLWIN32GUI_ARGTYPE_LONG, lParam,
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_CHAR:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_CHAR, "Char",
            PERLWIN32GUI_ARGTYPE_LONG, lParam,
            PERLWIN32GUI_ARGTYPE_LONG, wParam,
            -1);
        break;
    case WM_COMMAND:
    case WM_NOTIFY :
        if (perlud->dwPlStyle & PERLWIN32GUI_CONTAINER) {
            HWND hwndParent = (HWND) GetWindowLong(hwnd, GWL_HWNDPARENT);
            SendMessage(hwndParent, uMsg, wParam, lParam);
            return 0;
        }
    case WM_MOUSEHOVER :
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_MOUSEOVER, "MouseOver", -1);
        if(PerlResult) {
            TRACKMOUSEEVENT tme;
            tme.cbSize = sizeof(TRACKMOUSEEVENT);
            tme.hwndTrack = hwnd;
            tme.dwFlags = TME_QUERY;
            if(_TrackMouseEvent( &tme )) {
                _TrackMouseEvent( &tme );
            }
        }
        break;
    case WM_MOUSELEAVE :
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_MOUSEOUT, "MouseOut", -1);
        if(PerlResult) {
            TRACKMOUSEEVENT tme;
            tme.cbSize = sizeof(TRACKMOUSEEVENT);
            tme.hwndTrack = hwnd;
            tme.dwFlags = TME_QUERY;
            if(_TrackMouseEvent( &tme )) {
                _TrackMouseEvent( &tme );
            }
        }
        break;
    case WM_DROPFILES:
        PerlResult = DoEvent(NOTXSCALL perlud, PERLWIN32GUI_NEM_DROPFILE, "DropFiles",
                        PERLWIN32GUI_ARGTYPE_LONG, (LONG) wParam,
                        -1);
    }

    if (perlud->avHooks != NULL)
        DoHook(NOTXSCALL perlud, uMsg,wParam,lParam,&PerlResult);


    if (PerlResult != 0) {
        PerlResult = CommonMsgLoop(NOTXSCALL hwnd, uMsg, wParam, lParam, perlud->WndProc);
    }
    else if ( perlud->forceResult != 0) {
        return perlud->forceResult;
    }

    return PerlResult;
}

    /*
    ###########################################################################
    # (@)INTERNAL:ContainerMsgLoop(hwnd, uMsg, wParam, lParam)
    # message loop for container control class
    # Only Set CONTAINER flag at creation and call ControlMsgLoop.
    */
LRESULT CALLBACK ContainerMsgLoop(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    /*
     * WM_CREATE && WM_NCCREATE
     * If we handle this message we are using a custom control class (See RegisterClassEx).
     */
    if(uMsg == WM_CREATE || uMsg == WM_NCCREATE)
    {
        LPPERLWIN32GUI_USERDATA perlud = (LPPERLWIN32GUI_USERDATA) ((CREATESTRUCT *) lParam)->lpCreateParams;
        if(perlud != NULL) {
            PERLUD_FETCH;
            SetWindowLong(hwnd, GWL_USERDATA, (long) perlud);
            SwitchBit(perlud->dwPlStyle, PERLWIN32GUI_CUSTOMCLASS, 1);  // Set Custom class flag
            SwitchBit(perlud->dwPlStyle, PERLWIN32GUI_CONTAINER  , 1);  // Set Container flag

            // Search for an extend MsgLoop procedure (-extends option in RegisterClassEx)
            perlud->WndProc = (LWNDPROC_CAST) GetDefClassProc (NOTXSCALL ((CREATESTRUCT *) lParam)->lpszClass);
            if (perlud->WndProc) {
                return CallWindowProc((WNDPROC_CAST) perlud->WndProc, hwnd, uMsg, wParam, lParam);
            }
        }

        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    // Call ControlMsgLoop.
    return CallWindowProc((WNDPROC_CAST) ControlMsgLoop, hwnd, uMsg, wParam, lParam);
}

    /*
    ###########################################################################
    # (@)INTERNAL:CustomMsgLoop(hwnd, uMsg, wParam, lParam)
    # Special message loop (for Win32::GUI::Splitter objects)
    # All event are handle in OnEvent function
    */
LRESULT CALLBACK CustomMsgLoop(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LPPERLWIN32GUI_USERDATA perlud;
    int PerlResult = 1;

    /*
     * WM_CREATE && WM_NCCREATE
     * If we handle this message we are using a custom control class (See RegisterClassEx).
     */
    if(uMsg == WM_CREATE || uMsg == WM_NCCREATE)
    {
        perlud = (LPPERLWIN32GUI_USERDATA) ((CREATESTRUCT *) lParam)->lpCreateParams;
        if(perlud != NULL) {
            PERLUD_FETCH;
            SetWindowLong(hwnd, GWL_USERDATA, (long) perlud);
            SwitchBit(perlud->dwPlStyle, PERLWIN32GUI_CUSTOMCLASS, 1);  // Set Custom class flag

            // Search for an extend MsgLoop procedure (-extends option in RegisterClassEx)
            perlud->WndProc = (LWNDPROC_CAST) GetDefClassProc (NOTXSCALL ((CREATESTRUCT *) lParam)->lpszClass);
            if (perlud->WndProc) {
                return CallWindowProc((WNDPROC_CAST) perlud->WndProc, hwnd, uMsg, wParam, lParam);
            }
        }

        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(hwnd, GWL_USERDATA);
    if( !ValidUserData(perlud)) {
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    perlud->forceResult = 0;

    PERLUD_FETCH;

    /*
     * WM_DESTROY
     */
    if(uMsg == WM_DESTROY) {
        if (perlud->WndProc)
            PerlResult = CallWindowProc((WNDPROC_CAST) perlud->WndProc, hwnd, uMsg, wParam, lParam);
        else
            PerlResult = DefWindowProc(hwnd, uMsg, wParam, lParam);
        PERLUD_FREE;
        return PerlResult;
    }

    // Call class event Handler
    PerlResult = OnEvent[perlud->iClass](NOTXSCALL perlud, uMsg, wParam, lParam);

    // Hook for non interactive control
    if (perlud->avHooks != NULL && !(perlud->dwPlStyle & PERLWIN32GUI_INTERACTIVE))
        DoHook(NOTXSCALL perlud, uMsg,wParam,lParam,&PerlResult);

    if (PerlResult != 0) {
        // If interactive control, call ControlMsgLoop
        if (perlud->dwPlStyle & PERLWIN32GUI_INTERACTIVE)
            PerlResult = CallWindowProc((WNDPROC_CAST) ControlMsgLoop, hwnd, uMsg, wParam, lParam);
        else
            PerlResult = CommonMsgLoop(NOTXSCALL hwnd, uMsg, wParam, lParam, perlud->WndProc);
    }
    else if (perlud->forceResult != 0) {
        return perlud->forceResult;
    }

    return PerlResult;
}
