The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
###############################################################################
#
# Win32::GUI - Perl-Win32 Graphical User Interface Extension
#
# 29 Jan 1997 by Aldo Calpini <dada@perl.it>
#
# Version: 1.0 (12 Nov 2004)
#
# Copyright (c) 1997..2004 Aldo Calpini. All rights reserved.
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
# $Id: GUI.xs,v 1.62 2006/07/16 13:02:29 robertemay Exp $
#
###############################################################################
 */

#include "GUI.h"
#ifdef __CYGWIN__
#include <sys/cygwin.h>
#endif

    /*
    ###########################################################################
    # (@)PACKAGE:Win32::GUI
    ###########################################################################
     */

MODULE = Win32::GUI     PACKAGE = Win32::GUI

PROTOTYPES: DISABLE

     ##########################################################################
     # (@)INTERNAL:_constant(NAME)
DWORD
_constant(name)
    char *name
CODE:
    if (strEQ(name, "WIN32__GUI__WINDOW"))
        RETVAL = WIN32__GUI__WINDOW;
    else if (strEQ(name, "WIN32__GUI__DIALOG"))
        RETVAL = WIN32__GUI__DIALOG;
    else if (strEQ(name, "WIN32__GUI__STATIC"))
        RETVAL = WIN32__GUI__STATIC;
    else if (strEQ(name, "WIN32__GUI__BUTTON"))
        RETVAL = WIN32__GUI__BUTTON;
    else if (strEQ(name, "WIN32__GUI__EDIT"))
        RETVAL = WIN32__GUI__EDIT;
    else if (strEQ(name, "WIN32__GUI__LISTBOX"))
        RETVAL = WIN32__GUI__LISTBOX;
    else if (strEQ(name, "WIN32__GUI__COMBOBOX"))
        RETVAL = WIN32__GUI__COMBOBOX;
    else if (strEQ(name, "WIN32__GUI__CHECKBOX"))
        RETVAL = WIN32__GUI__CHECKBOX;
    else if (strEQ(name, "WIN32__GUI__RADIOBUTTON"))
        RETVAL = WIN32__GUI__RADIOBUTTON;
    else if (strEQ(name, "WIN32__GUI__TOOLBAR"))
        RETVAL = WIN32__GUI__TOOLBAR;
    else if (strEQ(name, "WIN32__GUI__PROGRESS"))
        RETVAL = WIN32__GUI__PROGRESS;
    else if (strEQ(name, "WIN32__GUI__STATUS"))
        RETVAL = WIN32__GUI__STATUS;
    else if (strEQ(name, "WIN32__GUI__TAB"))
        RETVAL = WIN32__GUI__TAB;
    else if (strEQ(name, "WIN32__GUI__RICHEDIT"))
        RETVAL = WIN32__GUI__RICHEDIT;
    else if (strEQ(name, "WIN32__GUI__LISTVIEW"))
        RETVAL = WIN32__GUI__LISTVIEW;
    else if (strEQ(name, "WIN32__GUI__TREEVIEW"))
        RETVAL = WIN32__GUI__TREEVIEW;
    else if (strEQ(name, "WIN32__GUI__TRACKBAR"))
        RETVAL = WIN32__GUI__TRACKBAR;
    else if (strEQ(name, "WIN32__GUI__UPDOWN"))
        RETVAL = WIN32__GUI__UPDOWN;
    else if (strEQ(name, "WIN32__GUI__TOOLTIP"))
        RETVAL = WIN32__GUI__TOOLTIP;
    else if (strEQ(name, "WIN32__GUI__ANIMATION"))
        RETVAL = WIN32__GUI__ANIMATION;
    else if (strEQ(name, "WIN32__GUI__REBAR"))
        RETVAL = WIN32__GUI__REBAR;
    else if (strEQ(name, "WIN32__GUI__HEADER"))
        RETVAL = WIN32__GUI__HEADER;
    else if (strEQ(name, "WIN32__GUI__COMBOBOXEX"))
        RETVAL = WIN32__GUI__COMBOBOXEX;
    else if (strEQ(name, "WIN32__GUI__DTPICK"))
        RETVAL = WIN32__GUI__DTPICK;
    else if (strEQ(name, "WIN32__GUI__GRAPHIC"))
        RETVAL = WIN32__GUI__GRAPHIC;
    else if (strEQ(name, "WIN32__GUI__GROUPBOX"))
        RETVAL = WIN32__GUI__GROUPBOX;
    else if (strEQ(name, "WIN32__GUI__SPLITTER"))
        RETVAL = WIN32__GUI__SPLITTER;
    else if (strEQ(name, "WIN32__GUI__MDIFRAME"))
        RETVAL = WIN32__GUI__MDIFRAME;
    else if (strEQ(name, "WIN32__GUI__MDICLIENT"))
        RETVAL = WIN32__GUI__MDICLIENT;
    else if (strEQ(name, "WIN32__GUI__MDICHILD"))
        RETVAL = WIN32__GUI__MDICHILD;
    else if (strEQ(name, "WIN32__GUI__MONTHCAL"))
        RETVAL = WIN32__GUI__MONTHCAL;
    else
        RETVAL = 0xFFFFFFFF;
OUTPUT:
    RETVAL


    ##########################################################################
    # (@)METHOD:GetAsyncKeyState(keyCode)
    # Retrieve the status of the specified virtual key at the time the function
    # is called. The status specifies whether the key is up or down.
    #
    # keyCode -- If A..Z0..9, use the ASCII code. Otherwise, use 
    # a virtual key code. Example: VK_SHIFT
    #
    # Return 1 if the key is depressed, 0 if it's not.
LONG
GetAsyncKeyState(key)
    int key
CODE:
    RETVAL = (GetAsyncKeyState(key) & 0x8000) >>15;
OUTPUT:
    RETVAL
   
    ##########################################################################
    # (@)METHOD:GetKeyState(keyCode)
    # Retrieve the status of the specified virtual key at the time the last
    # keyboard message was retrieved from the message queue.
    #
    # In scalar context returns a value specifying whether the key is up(0)
    # or down(1). In list context, returns a 2 element list with the first
    # element as in scalar context and the second member specifying whether
    # the key is toggled(1) or not(0) - this is only meaningful for keys that
    # have a toggled state: Caps Lock, Num Lock etc.
    #
    # keyCode -- If A..Z0..9, use the ASCII code. Otherwise, use 
    # a virtual key code. Example: VK_SHIFT
void
GetKeyState(key)
    int key
PREINIT:
    USHORT result;
PPCODE:
    result = (USHORT)GetKeyState(key);
    if(GIMME_V == G_ARRAY) {
        /* list context */
        EXTEND(SP, 2);
        XST_mIV(0, (UV) ((result & 0x8000) >> 15));
        XST_mIV(1, (UV) (result & 0x0001));
        XSRETURN(2);
    }
    else {
        /* scalar(and void) context */
        XSRETURN_IV((UV) ((result & 0x8000) >> 15));
    }

    ##########################################################################
    # (@)METHOD:GetKeyboardState()
    # Return array ref with the status of the 256 virtual keys. 
    #
    # The index in the array is the virtual key code. If the value 
    # is true, that key is depressed.
    #
    # Example: 
    #   $key=Win32::GUI::GetKeyboardState;
    #   print 'CTRL is down' if $key->[0x11];
    
AV*
GetKeyboardState()
PREINIT:
     AV   *array;
     BYTE keys[256];
     int  i;
CODE:
     GetKeyboardState(keys);
     array = (AV*)sv_2mortal((SV*)newAV());
     for(i = 0; i <= 256; i++) {
       av_push(array, newSViv(keys[i] & 128));
     }
     RETVAL = array;
OUTPUT:
     RETVAL

    ##########################################################################
    # (@)METHOD:LoadLibrary(NAME)
    # The LoadLibrary function maps the specified executable module into the
    # address space of the calling process.
    #
    # The return value is a handle to the module, or undef on failure.
    #
    # Directory seperators are normalised to windows seperators (C<\>).
    #
    # Under Cygwin, cygwin paths are converted to windows paths
HINSTANCE
LoadLibrary(name)
    char *name;
PREINIT:
    char buffer[MAX_PATH+1];
    int i;
CODE:
#ifdef __CYGWIN__
    /* Under Cygwin, convert paths to windows
     * paths. E.g. convert /usr/local... and /cygdrive/c/...
     */
    if(cygwin_conv_to_win32_path(name,buffer) != 0)
        XSRETURN_UNDEF;
#else
    /* LoadLibrary on Win98 (at least) doesn't like unix
     * path seperators, so normalise to windows path seperators
     */
    for(i=0; *name && (i<MAX_PATH); ++name,++i) {
        buffer[i] = (*name == '/' ? '\\' : *name);
    }
    if(*name) {
        /* XXX Path too long - although this appears to be what
         * LoadLibrary would return with such a path, it might be
         * better to find a more specific error code.  E.g.
         * ENAMETOOLONG?
         */
        SetLastError(ERROR_FILE_NOT_FOUND);
        errno = ENOENT;
        XSRETURN_UNDEF;
    }
    buffer[i] = 0;
#endif
    RETVAL = LoadLibrary(buffer);
    if(!RETVAL)
        XSRETURN_UNDEF;
OUTPUT:
    RETVAL 
     
     ##########################################################################
     # (@)METHOD:FreeLibrary(LIBRARY)
     # The FreeLibrary function decrements the reference count of the loaded dynamic-link library (DLL) module.
bool
FreeLibrary(library)
    HINSTANCE library;
CODE:
    RETVAL = FreeLibrary(library);
OUTPUT:
    RETVAL

     ##########################################################################
     # (@)METHOD:GetEvent(NAME)
     # Retrieves an event. If the New Event Model is being used, this will
     # return the code-reference of the event you named, otherwise it will
     # return undef.
void
GetEvent(handle,name)
    HWND handle
    char * name
PREINIT:
    SV** eventhandler;
    LPPERLWIN32GUI_USERDATA perlud;
PPCODE:
    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong((HWND) handle, GWL_USERDATA);
    if(perlud == NULL || perlud->hvEvents == NULL) XSRETURN_UNDEF;
    eventhandler = hv_fetch(perlud->hvEvents,name,strlen(name),0);
    if(eventhandler != NULL) {
        SvREFCNT_inc(*eventhandler);
        XPUSHs(*eventhandler);
        XSRETURN(1);
    }
    XSRETURN_UNDEF;

     ##########################################################################
     # (@)METHOD:SetEvent(NAME,HANDLER)
     # Sets an event. If the New Event Model is being used, this will enable
     # the specified event and set it to be handled by the specified C<HANDLER>,
     # which should be a code-reference.
void
SetEvent(handle,name,event)
    HWND handle
    char * name
    SV* event
PREINIT:
    LPPERLWIN32GUI_USERDATA perlud;
    PERLWIN32GUI_CREATESTRUCT perlcs;
PPCODE:
    ZeroMemory(&perlcs, sizeof(PERLWIN32GUI_CREATESTRUCT));
    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong((HWND) handle, GWL_USERDATA);
    if(perlud == NULL) XSRETURN_UNDEF;

    if ( perlud->hvEvents == NULL ) {
         perlud->hvEvents = newHV();
         perlud->dwEventMask = 0;
    }
    if(perlud->hvEvents == NULL) XSRETURN_UNDEF;

    perlcs.iClass = perlud->iClass;
    perlcs.hvEvents = perlud->hvEvents;
    perlcs.dwEventMask = perlud->dwEventMask;

    ParseNEMEvent(NOTXSCALL &perlcs, name, event);

    perlud->hvEvents = perlcs.hvEvents;
    perlud->dwEventMask = perlcs.dwEventMask;
    SwitchBit(perlud->dwPlStyle, PERLWIN32GUI_NEM, (perlud->dwEventMask != 0));

    XSRETURN_YES;

     ##########################################################################
     # (@)METHOD:LoadResource(NAME)
     # Loads a generic resource from the EXE file that your perl process is
     # running as. This is for use when distributing your application. Resources
     # can be packed into the EXE file using many tools including ResHacker.
     #
     # Note that packing resources into a PAR executable will not work. You must
     # first pack the resources into par.exe then use PAR to build your
     # executable.
     #
     # For this routine to work, any resources you wish to load with it must
     # be added to the executable with the RCDATA resource type.
     #
     # If the resource is not found in the EXE, this function will return NULL,
     # otherwise it will return a scalar containing the raw resource data.
void
LoadResource(filename)
    LPCSTR filename
PPCODE:
    HINSTANCE myInstance;
    HRSRC resInfo;
    HGLOBAL resHandle;
    char * resData;
    DWORD resSize;

    myInstance = GetModuleHandle(NULL);
    if(myInstance == NULL) XSRETURN_UNDEF;

    resInfo = FindResource(myInstance,filename,RT_RCDATA);
    if(resInfo == NULL) XSRETURN_UNDEF;

    resSize = SizeofResource(myInstance,resInfo);
    resHandle = LoadResource(myInstance,resInfo);
    if(resHandle == NULL) XSRETURN_UNDEF;

    resData = (char *) LockResource(resHandle);
    if(resData != NULL) {
        // Whew, we have a pointer to our resource data.
        // We would free here, but according to MSDN we don't have to (?!)
        XPUSHs(newSVpvn(resData, resSize));
        XSRETURN(1);
    }
    XSRETURN_UNDEF;


     ##########################################################################
     # (@)METHOD:GetPerlWindow()
     # Returns the handle of the command prompt window your perl script is
     # running in; if called in an array context, returns the handle and the
     # HINSTANCE of your perl process.
void
GetPerlWindow()
PPCODE:
    char OldPerlWindowTitle[1024];
    char NewPerlWindowTitle[1024];
    HWND hwndFound;
    HINSTANCE hinstanceFound;
    // this is an hack from M$'s Knowledge Base
    // to get the HWND of the console in which
    // Perl is running (and Hide() it :-).
    GetConsoleTitle(OldPerlWindowTitle, 1024);
    wsprintf(NewPerlWindowTitle,
             "PERL-%d-%d",
             GetTickCount(),
             GetCurrentProcessId());

    SetConsoleTitle(NewPerlWindowTitle);
    Sleep(40);
    hwndFound = FindWindow(NULL, NewPerlWindowTitle);

    // another hack to get the program's instance
#ifdef NT_BUILD_NUMBER
    hinstanceFound = GetModuleHandle("GUI.PLL");
#else
    hinstanceFound = GetModuleHandle("GUI.DLL");
#endif
    // hinstanceFound = (HINSTANCE) GetWindowLong(hwndFound, GWL_HINSTANCE);
    // sv_hinstance = perl_get_sv("Win32::GUI::hinstance", TRUE);
    // sv_setiv(sv_hinstance, (IV) hinstanceFound);
    SetConsoleTitle(OldPerlWindowTitle);
    if(GIMME == G_ARRAY) {
        EXTEND(SP, 2);
        XST_mIV(0, (long) hwndFound);
        XST_mIV(1, (long) hinstanceFound);
        XSRETURN(2);
    } else {
        XSRETURN_IV((long) hwndFound);
    }


     ##########################################################################
     # (@)INTERNAL:RegisterClassEx(%OPTIONS)
     # used by new Win32::GUI::Class
void
RegisterClassEx(...)
PPCODE:
    WNDCLASSEX wcx;
    HINSTANCE hinstance;
    
    char * option;
    int i, next_i;

    WNDPROC DefClassProc = NULL;

    ZeroMemory(&wcx, sizeof(WNDCLASSEX));
    wcx.cbSize = sizeof(WNDCLASSEX);

    wcx.style = CS_HREDRAW | CS_VREDRAW; // TODO (default class style...)
    wcx.cbClsExtra = 0;
    wcx.cbWndExtra = 0;
    wcx.lpfnWndProc = WindowMsgLoop;
#ifdef NT_BUILD_NUMBER
    hinstance = GetModuleHandle("GUI.PLL");
#else
    hinstance = GetModuleHandle("GUI.DLL");
#endif
    wcx.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_DEFAULTICON));
    wcx.hIconSm = NULL;
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcx.lpszMenuName = NULL;

    for(i = 0; i < items; i++) {
        if(strcmp(SvPV_nolen(ST(i)), "-extends") == 0) {
            next_i = i + 1;
            if(!GetClassInfoEx((HINSTANCE) NULL, (LPCTSTR) SvPV_nolen(ST(next_i)), &wcx)) {
                W32G_WARN("Win32::GUI: Class '%s' not found!", SvPV_nolen(ST(next_i)));
                XSRETURN_NO;
            }
            DefClassProc = wcx.lpfnWndProc;
        }
    }

    next_i = -1;
    for(i = 0; i < items; i++) {
        if(next_i == -1) {
            option = SvPV_nolen(ST(i));
            if(strcmp(option, "-name") == 0) {
                next_i = i + 1;
                wcx.lpszClassName = (char *) SvPV_nolen(ST(next_i));
            } else if(strcmp(option, "-color") == 0) {
                next_i = i + 1;
                wcx.hbrBackground = (HBRUSH) SvCOLORREF(NOTXSCALL ST(next_i));
            } else if(strcmp(option, "-brush") == 0) {
                next_i = i + 1;
                wcx.hbrBackground = (HBRUSH) handle_From(NOTXSCALL ST(next_i));
            } else if(strcmp(option, "-visual") == 0) {
                next_i = i + 1;
                // -visual => 0 is obsolete
            } else if(strcmp(option, "-widget") == 0) {
                next_i = i + 1;
                if(strcmp(SvPV_nolen(ST(next_i)), "Container") == 0) {
                    wcx.lpfnWndProc = ContainerMsgLoop;
                } else if(strcmp(SvPV_nolen(ST(next_i)), "Graphic") == 0) {
                    wcx.lpfnWndProc = CustomMsgLoop;
                } else if(strcmp(SvPV_nolen(ST(next_i)), "Splitter") == 0) {
                    wcx.lpfnWndProc = CustomMsgLoop;
                    wcx.hCursor = LoadCursor(hinstance, MAKEINTRESOURCE(IDC_HSPLIT));
                } else if(strcmp(SvPV_nolen(ST(next_i)), "SplitterH") == 0) {
                    wcx.lpfnWndProc = CustomMsgLoop;
                    wcx.hCursor = LoadCursor(hinstance, MAKEINTRESOURCE(IDC_VSPLIT));
                } else if(strcmp(SvPV_nolen(ST(next_i)), "MDIFrame") == 0) {
                    wcx.lpfnWndProc = MDIFrameMsgLoop;
                } else if(strcmp(SvPV_nolen(ST(next_i)), "MDIChild") == 0) {
                    wcx.lpfnWndProc = MDIChildMsgLoop;
                } else {
                    wcx.lpfnWndProc = ControlMsgLoop;
                }
            } else if(strcmp(option, "-style") == 0) {
                next_i = i + 1;
                wcx.style = SvIV(ST(next_i));
            } else if(strcmp(option, "-icon") == 0) {
                next_i = i + 1;
                wcx.hIcon = (HICON) handle_From(NOTXSCALL ST(next_i));
            } else if(strcmp(option, "-cursor") == 0) {
                next_i = i + 1;
                wcx.hCursor = (HCURSOR) handle_From(NOTXSCALL ST(next_i));
            } else if(strcmp(option, "-menu") == 0) {
                next_i = i + 1;
                wcx.lpszMenuName = (char *) SvPV_nolen(ST(next_i));
            }
        } else {
            next_i = -1;
        }
    }

    // Register the window class.
    if(RegisterClassEx(&wcx)) {
        
        if (DefClassProc != NULL && DefClassProc != wcx.lpfnWndProc)
            SetDefClassProc (NOTXSCALL wcx.lpszClassName, DefClassProc);

        XSRETURN_YES;
    // Try to reregister
    } else if ( UnregisterClass( wcx.lpszClassName, wcx.hInstance ) ) {
        if( RegisterClassEx(&wcx) ) {
            if (DefClassProc != NULL && DefClassProc != wcx.lpfnWndProc)
                SetDefClassProc (NOTXSCALL wcx.lpszClassName, DefClassProc);

            XSRETURN_YES;
        }
    }    
    XSRETURN_NO;

     ##########################################################################
     # (@)INTERNAL:CreateWindowEx(%OPTIONS)
     # obsoleted, use Create() instead
void
CreateWindowEx(...)
PPCODE:
    HWND myhandle;
    int i, next_i;
    HWND  hParent;
    HMENU hMenu;
    HINSTANCE hInstance;
    LPVOID pPointer;
    DWORD dwStyle;
    DWORD dwExStyle;
    LPCTSTR szClassname;
    LPCTSTR szText;
    int nX, nY, nWidth, nHeight;
    char * option;

    hParent = NULL;
    hMenu = NULL;
    hInstance = NULL;
    pPointer = NULL;
    dwStyle = 0;
    dwExStyle = 0;
    szText = NULL;

    next_i = -1;
    for(i = 0; i < items; i++) {
        if(next_i == -1) {
            option = SvPV_nolen(ST(i));
            if(strcmp(option, "-exstyle") == 0) {
                next_i = i + 1;
                dwExStyle = (DWORD) SvIV(ST(next_i));
            }
            if(strcmp(option, "-class") == 0) {
                next_i = i + 1;
                szClassname = (LPCTSTR) SvPV_nolen(ST(next_i));
            }
            if(strcmp(option, "-text") == 0
            || strcmp(option, "-title") == 0) {
                next_i = i + 1;
                szText = (LPCTSTR) SvPV_nolen(ST(next_i));
            }
            if(strcmp(option, "-style") == 0) {
                next_i = i + 1;
                dwStyle = (DWORD) SvIV(ST(next_i));
            }

            if(strcmp(option, "-left") == 0) {
                next_i = i + 1;
                nX = (int) SvIV(ST(next_i));
            }
            if(strcmp(option, "-top") == 0) {
                next_i = i + 1;
                nY = (int) SvIV(ST(next_i));
            }
            if(strcmp(option, "-height") == 0) {
                next_i = i + 1;
                nHeight = (int) SvIV(ST(next_i));
            }
            if(strcmp(option, "-width") == 0) {
                next_i = i + 1;
                nWidth = (int) SvIV(ST(next_i));
            }
            if(strcmp(option, "-parent") == 0) {
                next_i = i + 1;
                hParent = (HWND) handle_From(NOTXSCALL ST(next_i));
            }
            if(strcmp(option, "-menu") == 0) {
                next_i = i + 1;
                hMenu = (HMENU) handle_From(NOTXSCALL ST(next_i));
            }
            if(strcmp(option, "-instance") == 0) {
                next_i = i + 1;
                hInstance = INT2PTR(HINSTANCE,SvIV(ST(next_i)));
            }
            if(strcmp(option, "-data") == 0) {
                next_i = i + 1;
                pPointer = (LPVOID) SvPV_nolen(ST(next_i));
            }

        } else {
            next_i = -1;
        }
    }
#ifdef PERLWIN32GUI_DEBUG
    printf("XS(CreateWindowEx): Done parsing parameters...\n");
    printf("XS(CreateWindowEx): dwExStyle = 0x%x\n", dwExStyle);
    printf("XS(CreateWindowEx): szClassname = %s\n", szClassname);
    printf("XS(CreateWindowEx): szText = %s\n", szText);
    printf("XS(CreateWindowEx): dwStyle = 0x%x\n", dwStyle);
    printf("XS(CreateWindowEx): nX = %d\n", nX);
    printf("XS(CreateWindowEx): nY = %d\n", nY);
    printf("XS(CreateWindowEx): nWidth = %d\n", nWidth);
    printf("XS(CreateWindowEx): nHeight = %d\n", nHeight);
    printf("XS(CreateWindowEx): hParent = 0x%x\n", hParent);
    printf("XS(CreateWindowEx): hMenu = 0x%x\n", hMenu);
    printf("XS(CreateWindowEx): hInstance = 0x%x\n", hInstance);
    printf("XS(CreateWindowEx): pPointer = 0x%x\n", pPointer);
#endif
    if(myhandle = CreateWindowEx(dwExStyle,
                                 szClassname,
                                 szText,
                                 dwStyle,
                                 nX,
                                 nY,
                                 nWidth,
                                 nHeight,
                                 hParent,
                                 hMenu,
                                 hInstance,
                                 pPointer)) {
        XSRETURN_IV((long) myhandle);
    } else {
        XSRETURN_NO;
    }


    ###########################################################################
    # (@)INTERNAL:Create(%OPTIONS)
    # this is where all the windows are created
void
Create(...)
PPCODE:
    HWND myhandle;
    int first_i;
    PERLWIN32GUI_CREATESTRUCT perlcs;
    LPVOID pPointer;
    SV* self;
    SV** stored;
    SV* storing;
    SV** font;
    LPPERLWIN32GUI_USERDATA perlud;

    ZeroMemory(&perlcs, sizeof(PERLWIN32GUI_CREATESTRUCT));

    self = newSVsv(ST(0));
    sv_rvweaken(self);

    perlcs.cs.hInstance = GetModuleHandle(NULL);
    perlcs.hvSelf = (HV*) SvRV(self);
    perlcs.iClass = SvIV(ST(1));
    perlcs.clrForeground = CLR_INVALID;
    perlcs.clrBackground = CLR_INVALID;
    perlcs.iMinWidth = -1;
    perlcs.iMaxWidth = -1;
    perlcs.iMinHeight = -1;
    perlcs.iMaxHeight = -1;    

    // #### fill the default parameters for classes
    OnPreCreate[perlcs.iClass](NOTXSCALL &perlcs);

    first_i = 2;
    if(SvROK(ST(2))) {
        perlcs.cs.hwndParent = (HWND) handle_From(NOTXSCALL ST(2));
        perlcs.hvParent = (HV*) SvRV(ST(2));
        first_i = 3;
    }

    // #### options parsing loop
    ParseWindowOptions(NOTXSCALL sp, mark, ax, items, first_i, &perlcs);

    // No event model set, then force default event model.
    if ( !(perlcs.dwPlStyle & PERLWIN32GUI_OEM) & !(perlcs.dwPlStyle & PERLWIN32GUI_NEM)) {
        SwitchBit(perlcs.dwPlStyle, PERLWIN32GUI_OEM, 1);
    }

    // #### post-processing default parameters
    switch(perlcs.iClass) {
    case WIN32__GUI__BUTTON:
        CalcControlSize(NOTXSCALL &perlcs, 16, 8);
        break;
    case WIN32__GUI__CHECKBOX:
    case WIN32__GUI__RADIOBUTTON:
        CalcControlSize(NOTXSCALL &perlcs, 24, 8);
        break;
    case WIN32__GUI__STATIC:
        CalcControlSize(NOTXSCALL &perlcs, 0, 0);
        break;
    }

    // #### default styles for all controls
    if(perlcs.iClass != WIN32__GUI__WINDOW && 
       perlcs.iClass != WIN32__GUI__DIALOG &&
       perlcs.iClass != WIN32__GUI__MDIFRAME &&
       perlcs.iClass != WIN32__GUI__MDICHILD &&
       perlcs.iClass != WIN32__GUI__TOOLTIP ) {
        SwitchBit(perlcs.cs.style, WS_CHILD, 1);
    }
#ifdef PERLWIN32GUI_STRONGDEBUG
    printf("XS(Create): Done parsing parameters...\n");
    printf("XS(Create): dwExStyle = 0x%x\n", perlcs.cs.dwExStyle);
    printf("XS(Create): szClassname = '%s'\n", perlcs.cs.lpszClass);
    printf("XS(Create): szName = '%s'\n", perlcs.cs.lpszName);
    printf("XS(Create): dwStyle = 0x%x\n", perlcs.cs.style);
    printf("XS(Create): nX = %d\n", perlcs.cs.x);
    printf("XS(Create): nY = %d\n", perlcs.cs.y);
    printf("XS(Create): nWidth = %d\n", perlcs.cs.cx);
    printf("XS(Create): nHeight = %d\n", perlcs.cs.cy);
    printf("XS(Create): hParent = 0x%x\n", perlcs.cs.hwndParent);
    printf("XS(Create): hMenu = 0x%x\n", perlcs.cs.hMenu);
    printf("XS(Create): hInstance = 0x%x\n", perlcs.cs.hInstance);
    printf("XS(Create): dwPlStyle = 0x%x\n", perlcs.dwPlStyle);
#endif

    // #### prepare the ground for the window
    Newz(0, perlud, 1, PERLWIN32GUI_USERDATA);
    perlud->dwSize = sizeof(PERLWIN32GUI_USERDATA);
    PERLUD_STORE;
    perlud->svSelf = self;
    if (NULL != perlcs.szWindowName) {
        strcpy( perlud->szWindowName, perlcs.szWindowName);
    } else {
        sprintf(perlud->szWindowName, "#%x", perlud);
        perlcs.szWindowName = perlud->szWindowName;
    }
    perlud->iClass = perlcs.iClass;
    perlud->hAcc = perlcs.hAcc;
    perlud->hCursor = perlcs.hCursor;
    perlud->dwPlStyle = perlcs.dwPlStyle;
    perlud->iMinWidth = perlcs.iMinWidth;
    perlud->iMaxWidth = perlcs.iMaxWidth;
    perlud->iMinHeight = perlcs.iMinHeight;
    perlud->iMaxHeight = perlcs.iMaxHeight;
    perlud->clrForeground = perlcs.clrForeground;
    perlud->clrBackground = perlcs.clrBackground;
    perlud->hBackgroundBrush = perlcs.hBackgroundBrush;
    perlud->hvEvents = perlcs.hvEvents;
    perlud->dwEventMask = perlcs.dwEventMask;
    perlud->dwData = (DWORD) perlcs.cs.lpCreateParams;
    pPointer = perlud;

    // #### the following can be vital for the window
    // #### because as soon as it is created the message
    // #### loop is activated and data needs to be there
    storing = newSViv((long) perlcs.iClass);
    stored = hv_store_mg(NOTXSCALL perlcs.hvSelf, "-type", 5, storing, 0);  // TODO : used ?
    storing = newSVpv((char *)perlcs.szWindowName, 0);
    stored = hv_store_mg(NOTXSCALL perlcs.hvSelf, "-name", 5, storing, 0);

    // Specific MDI_CLIENT : Send CLIENTCREATESTRUCT as LPARAM
    if(perlcs.iClass == WIN32__GUI__MDICLIENT ) {
        pPointer = perlcs.cs.lpCreateParams;
    }

    // #### and finally, creation of the window
#ifdef PERLWIN32GUI_STRONGDEBUG
    printf("XS(Create): Done initialization of USERDATA struct...\n");
#endif
    if(myhandle = CreateWindowEx(
        perlcs.cs.dwExStyle,
        perlcs.cs.lpszClass,
        perlcs.cs.lpszName,
        perlcs.cs.style,
        perlcs.cs.x,
        perlcs.cs.y,
        perlcs.cs.cx,
        perlcs.cs.cy,
        perlcs.cs.hwndParent,
        perlcs.cs.hMenu,
        perlcs.cs.hInstance,
        pPointer
    )) {
        // #### ok, we can fill this object's hash
#ifdef PERLWIN32GUI_STRONGDEBUG
        printf("XS(Create): storing -handle...\n");
#endif
        storing = newSViv((long) myhandle);
        stored = hv_store_mg(NOTXSCALL perlcs.hvSelf, "-handle", 7, storing, 0);
        // #### set the font for the control
#ifdef PERLWIN32GUI_STRONGDEBUG
        printf("XS(Create): storing -font...\n");
#endif
        if(perlcs.hFont != NULL) {
            storing = newSViv((long) perlcs.hFont);
            stored = hv_store_mg(NOTXSCALL perlcs.hvSelf, "-font", 5, storing, 0);
            SendMessage(myhandle, WM_SETFONT, (WPARAM) perlcs.hFont, 0);
        } else if(perlcs.cs.hwndParent != NULL) {
            font = hv_fetch_mg(NOTXSCALL perlcs.hvParent, "-font", 5, FALSE);
            if(font != NULL && SvOK(*font)) {
                perlcs.hFont = (HFONT) handle_From(NOTXSCALL *font);
                SendMessage(myhandle, WM_SETFONT, (WPARAM) perlcs.hFont, 0);
            } else {
                perlcs.hFont = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
                SendMessage(myhandle, WM_SETFONT, (WPARAM) perlcs.hFont, 0);
            }
        }
        if(NULL == perlcs.hAcc) {
#ifdef PERLWIN32GUI_STRONGDEBUG
            printf("XS(Create): storing -accel...\n");
#endif
            stored = hv_store_mg(NOTXSCALL perlcs.hvSelf, "-accel", 6, newSViv(0), 0);
        }

        // #### add (or create) the tooltip
        if(perlcs.szTip != NULL) {
            if(perlcs.hvParent != NULL) {
                if(perlcs.hTooltip == NULL) {
                    SV** t;
                    t = hv_fetch_mg(NOTXSCALL perlcs.hvParent, "-tooltip", 8, 0);
                    if(t != NULL && SvOK( *t )) {
                        perlcs.hTooltip = INT2PTR(HWND,SvIV(*t));
                    }
                }
                if(perlcs.hTooltip == NULL) {
#ifdef PERLWIN32GUI_STRONGDEBUG
                    printf("XS(Create): creating -tooltip...\n");
#endif
                    perlcs.hTooltip = CreateTooltip(NOTXSCALL perlcs.hvParent);
                }
            }
#ifdef PERLWIN32GUI_STRONGDEBUG
            printf("XS(Create): adding -tooltip...\n");
#endif
            TOOLINFO ti;
            ZeroMemory(&ti, sizeof(TOOLINFO));
            ti.cbSize = sizeof(TOOLINFO);
            ti.uFlags = TTF_IDISHWND | TTF_CENTERTIP | TTF_SUBCLASS;
            ti.hwnd = perlcs.cs.hwndParent;
            ti.uId = (WPARAM) myhandle;
            ti.lpszText = perlcs.szTip;
            SendMessage(perlcs.hTooltip, TTM_ADDTOOL, 0, (LPARAM) &ti);
        }

        // #### store the child in the parent hash
#ifdef PERLWIN32GUI_STRONGDEBUG
        printf("XS(Create): storing child into parent...\n");
#endif
        if(perlcs.hvParent != NULL && perlcs.szWindowName != NULL) {
            // storing = newSVsv(ST(0));
            // sv_rvweaken(storing);
            storing = SvREFCNT_inc (self);
            stored = hv_store_mg(NOTXSCALL perlcs.hvParent, perlcs.szWindowName, strlen(perlcs.szWindowName), storing, 0);
        }

        // #### other post-creation class-specific initializations...
#ifdef PERLWIN32GUI_STRONGDEBUG
        printf("XS(Create): post-creation phase...\n");
#endif
        OnPostCreate[perlcs.iClass](NOTXSCALL myhandle, &perlcs);

        // #### store a pointer to the Perl object in the window's USERDATA
#ifdef PERLWIN32GUI_STRONGDEBUG
        printf("XS(Create): storing GWL_USERDATA...\n");
#endif

        // Specific MDI_CLIENT : SubClass Window
        // We need subclass after window creation for IDFirstChild work.
        if(perlcs.iClass == WIN32__GUI__MDICLIENT ) {
            perlud->dwPlStyle |= PERLWIN32GUI_CUSTOMCLASS;
            perlud->WndProc = (WNDPROC) SetWindowLong(myhandle, GWL_WNDPROC, (LONG) MDIClientMsgLoop);
            SetWindowLong(myhandle, GWL_USERDATA, (LONG) perlud);
        }

        // Sub class all standard window control as child control (no WM_CREATE or WN_NCCREATE catch)
        if( !(perlud->dwPlStyle & PERLWIN32GUI_CUSTOMCLASS) ) {
            LPPERLWIN32GUI_USERDATA testud;
            testud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(myhandle, GWL_USERDATA);
            if (!ValidUserData(testud) ) {
                perlud->WndProc = (WNDPROC) SetWindowLong(myhandle, GWL_WNDPROC, (LONG) ControlMsgLoop);
                SetWindowLong(myhandle, GWL_USERDATA, (LONG) perlud);
            }
        }

        // #### (try to) figure out which MsgLoop procedure to use
        if (perlcs.hvParent != NULL) {
            LPPERLWIN32GUI_USERDATA parentud;
            parentud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(perlcs.cs.hwndParent, GWL_USERDATA);
            if( ValidUserData(parentud) ) {
                if(parentud->iClass != WIN32__GUI__WINDOW && 
                   parentud->iClass != WIN32__GUI__DIALOG &&
                   parentud->iClass != WIN32__GUI__MDIFRAME &&
                   parentud->iClass != WIN32__GUI__MDICLIENT &&
                   !(parentud->dwPlStyle & PERLWIN32GUI_CONTAINER)) {
                    SwitchBit(parentud->dwPlStyle, PERLWIN32GUI_CONTAINER, 1);
                }
            }
        }
#ifdef PERLWIN32GUI_STRONGDEBUG
        printf("XS(Create): DONE!\n");
#endif
        XSRETURN_IV((long) myhandle);
    } else {
#ifdef PERLWIN32GUI_STRONGDEBUG
        printf("XS(Create): CreateWindowEx failed, returning undef\n");
#endif
        XSRETURN_NO;
    }

    ###########################################################################
    # (@)METHOD:Change(HANDLE, %OPTIONS)
    # Change most of the options used when the object was created.
void
Change(...)
PPCODE:
    HWND handle;
    PERLWIN32GUI_CREATESTRUCT perlcs;
    LPPERLWIN32GUI_USERDATA perlud;

    handle = (HWND) handle_From(NOTXSCALL ST(0));
    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(handle, GWL_USERDATA);

    ZeroMemory(&perlcs, sizeof(PERLWIN32GUI_CREATESTRUCT));
    if( ! ValidUserData(perlud) ) {
        XSRETURN_UNDEF;
    }

    perlcs.hvSelf = (HV*) SvRV(perlud->svSelf);
    perlcs.cs.style = GetWindowLong(handle, GWL_STYLE);
    perlcs.cs.dwExStyle = GetWindowLong(handle, GWL_EXSTYLE);
    if(perlcs.hvSelf != NULL) {
        // #### retrieve windows data
        perlcs.iClass = perlud->iClass;
        perlcs.hAcc = perlud->hAcc;
        perlcs.hCursor = perlud->hCursor;
        perlcs.dwPlStyle= perlud->dwPlStyle;
        perlcs.iMinWidth = perlud->iMinWidth;
        perlcs.iMaxWidth = perlud->iMaxWidth;
        perlcs.iMinHeight = perlud->iMinHeight;
        perlcs.iMaxHeight = perlud->iMaxHeight;
        perlcs.clrForeground = perlud->clrForeground;
        perlcs.clrBackground = perlud->clrBackground;
        perlcs.hBackgroundBrush = perlud->hBackgroundBrush;
        perlcs.hvEvents    = perlud->hvEvents;
        perlcs.dwEventMask = perlud->dwEventMask;
#ifdef PERLWIN32GUI_STRONGDEBUG
        printf("XS(Change): BEFORE dwExStyle = 0x%x\n", perlcs.cs.dwExStyle);
        printf("XS(Change): BEFORE szClassname = %s\n", perlcs.cs.lpszClass);
        printf("XS(Change): BEFORE szName = %s\n", perlcs.cs.lpszName);
        printf("XS(Change): BEFORE dwStyle = 0x%x\n", perlcs.cs.style);
        printf("XS(Change): BEFORE nX = %d\n", perlcs.cs.x);
        printf("XS(Change): BEFORE nY = %d\n", perlcs.cs.y);
        printf("XS(Change): BEFORE nWidth = %d\n", perlcs.cs.cx);
        printf("XS(Change): BEFORE nHeight = %d\n", perlcs.cs.cy);
        printf("XS(Change): BEFORE hParent = 0x%x\n", perlcs.cs.hwndParent);
        printf("XS(Change): BEFORE hMenu = 0x%x\n", perlcs.cs.hMenu);
        printf("XS(Change): BEFORE hInstance = 0x%x\n", perlcs.cs.hInstance);
        printf("XS(Change): BEFORE clrForeground = 0x%x\n", perlcs.clrForeground);
        printf("XS(Change): BEFORE clrBackground = 0x%x\n", perlcs.clrBackground);
        printf("XS(Change): BEFORE hBackgroundBrush = 0x%x\n", perlcs.hBackgroundBrush);
#endif
        // #### parse new window options
        ParseWindowOptions(NOTXSCALL sp, mark, ax, items, 1, &perlcs);

        // #### default styles for all controls
        if(perlcs.iClass != WIN32__GUI__WINDOW && 
           perlcs.iClass != WIN32__GUI__DIALOG &&
           perlcs.iClass != WIN32__GUI__MDIFRAME &&
           perlcs.iClass != WIN32__GUI__MDICHILD) {
            SwitchBit(perlcs.cs.style, WS_CHILD, 1);
        }
#ifdef PERLWIN32GUI_STRONGDEBUG
        printf("XS(Change): AFTER dwExStyle = 0x%x\n", perlcs.cs.dwExStyle);
        printf("XS(Change): AFTER szClassname = %s\n", perlcs.cs.lpszClass);
        printf("XS(Change): AFTER szName = %s\n", perlcs.cs.lpszName);
        printf("XS(Change): AFTER dwStyle = 0x%x\n", perlcs.cs.style);
        printf("XS(Change): AFTER nX = %d\n", perlcs.cs.x);
        printf("XS(Change): AFTER nY = %d\n", perlcs.cs.y);
        printf("XS(Change): AFTER nWidth = %d\n", perlcs.cs.cx);
        printf("XS(Change): AFTER nHeight = %d\n", perlcs.cs.cy);
        printf("XS(Change): AFTER hParent = 0x%x\n", perlcs.cs.hwndParent);
        printf("XS(Change): AFTER hMenu = 0x%x\n", perlcs.cs.hMenu);
        printf("XS(Change): AFTER hInstance = 0x%x\n", perlcs.cs.hInstance);
        printf("XS(Change): AFTER clrForeground = 0x%x\n", perlcs.clrForeground);
        printf("XS(Change): AFTER clrBackground = 0x%x\n", perlcs.clrBackground);
        printf("XS(Change): AFTER hBackgroundBrush = 0x%x\n", perlcs.hBackgroundBrush);
#endif
        // #### Perform changes
        if(NULL != perlcs.szWindowName) {
            strcpy(perlud->szWindowName, perlcs.szWindowName);
        }

        perlud->iClass = perlcs.iClass;
        perlud->hAcc = perlcs.hAcc;
        perlud->hCursor = perlcs.hCursor;
        perlud->dwPlStyle=  perlcs.dwPlStyle;
        perlud->iMinWidth = perlcs.iMinWidth;
        perlud->iMaxWidth = perlcs.iMaxWidth;
        perlud->iMinHeight = perlcs.iMinHeight;
        perlud->iMaxHeight = perlcs.iMaxHeight;
        perlud->clrForeground = perlcs.clrForeground;
        perlud->clrBackground = perlcs.clrBackground;
        perlud->hBackgroundBrush = perlcs.hBackgroundBrush;
        perlud->hvEvents    = perlcs.hvEvents;
        perlud->dwEventMask = perlcs.dwEventMask;
       
        if(perlcs.cs.lpszName != NULL)
            SetWindowText(handle, perlcs.cs.lpszName);

        SetWindowLong(handle, GWL_STYLE, perlcs.cs.style);
        SetWindowLong(handle, GWL_EXSTYLE, perlcs.cs.dwExStyle);

        if(perlcs.cs.x != 0 || perlcs.cs.y != 0)
            SetWindowPos(handle, (HWND) NULL, perlcs.cs.x, perlcs.cs.y, 0, 0,
                                 SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE);
        if(perlcs.cs.cx != 0 || perlcs.cs.cy != 0)
            SetWindowPos(handle, (HWND) NULL, 0, 0, perlcs.cs.cx, perlcs.cs.cy,
                                 SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE);
        if(perlcs.cs.hMenu != NULL)
            SetMenu(handle, perlcs.cs.hMenu);

        if(perlcs.hFont != NULL) {
            hv_store_mg(NOTXSCALL perlcs.hvSelf, "-font", 5, newSViv((long) perlcs.hFont), 0);
            SendMessage(handle, WM_SETFONT, (WPARAM) perlcs.hFont, 0);
        }

        // ### Class Post creation
        OnPostCreate[perlcs.iClass](NOTXSCALL handle, &perlcs);

        /* TODO: change class ???
        if(perlcs.cs.iClass != NULL)
            SetWindowLong(handle, GWL_
        */

        XSRETURN_YES;
    } else {
        XSRETURN_NO;
    }

    ###########################################################################
    # (@)METHOD:Dialog()
    # Enter the GUI dialog phase: the script halts, the user can interact with
    # the created windows and events subroutines are triggered as necessary;
    # note that this function must be called without ANY parameter or
    # instantiation (eg. don't call it as method of a created object):
    #
    #   Win32::GUI::Dialog(); # correct
    #   $Window->Dialog();    # !!!WRONG!!!
    #
    # Win32::GUI::Dialog(); does a similar thing to
    #    while(Win32::GUI::DoEvents() != -1) {};
    #
    # See also DoEvents()
    # See also DoModal()
DWORD
Dialog(hwnd=NULL)
    HWND hwnd
PREINIT:
    MSG msg;
    HWND phwnd;
    HWND thwnd;
    int stayhere;
    BOOL fIsDialog;
    BOOL fIsMDI;
    HACCEL acc;
    LPPERLWIN32GUI_USERDATA perlud;
CODE:
    stayhere = 1;
    fIsDialog = FALSE;
    while (stayhere) {

        ENTER;
        SAVETMPS;

        stayhere = GetMessage(&msg, hwnd, 0, 0);

        if(msg.message == WM_EXITLOOP) {
            stayhere = 0;
            msg.wParam = (WPARAM) -1;
        } else if(stayhere == -1) {
            stayhere = 0;
            msg.wParam = (WPARAM) -2; // an error occurred...
        } else {
            // #### trace back to the window's parent
            phwnd = msg.hwnd;
            while((thwnd = GetParent(phwnd)) && IsChild(thwnd, phwnd) ) {
                phwnd = thwnd;
            }
            // #### now see if the parent window is a DialogBox
            fIsDialog = fIsMDI = FALSE;
            acc = NULL;
            perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(phwnd, GWL_USERDATA);
            if( ValidUserData(perlud) ) {
                fIsDialog = perlud->dwPlStyle & PERLWIN32GUI_DIALOGUI;
                fIsMDI    = perlud->dwPlStyle & (PERLWIN32GUI_MDIFRAME | PERLWIN32GUI_HAVECHILDWINDOW);
                acc = perlud->hAcc;
            }

            if( !( (fIsMDI && TranslateMDISysAccel((HWND)perlud->dwData, &msg)) ||
                   (acc && TranslateAccelerator(phwnd, acc, &msg))              ||
                   (fIsDialog && IsDialogMessage(phwnd, &msg)) ) 
              ){
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }

        FREETMPS;
        LEAVE;
    }

    RETVAL = msg.wParam;
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:DoEvents(hwnd=NULL,wMsgFilterMin=0,wMsgFilterMax=0,wRemoveMsg=PM_REMOVE)
    # Performs all pending GUI events and returns the status. If DoEvents()
    # returns -1, your GUI has normally terminated.
    #
    # You can call $window->DoEvents() to process pending events relating to a
    # specific window, or Win32::GUI::DoEvents() to process pending events for all
    # windows.
    #
    # see also Dialog()
DWORD
DoEvents(hwnd=NULL,wMsgFilterMin=0,wMsgFilterMax=0,wRemoveMsg=PM_REMOVE)
    HWND hwnd
    UINT wMsgFilterMin
    UINT wMsgFilterMax
    UINT wRemoveMsg
PREINIT:
    MSG msg;
    HWND phwnd;
    HWND thwnd;
    int stayhere;
    BOOL fIsDialog;
    BOOL fIsMDI;
    HACCEL acc;
    LPPERLWIN32GUI_USERDATA perlud;
CODE:
    stayhere = 1;
    fIsDialog = FALSE;
    while(stayhere) {
        stayhere = PeekMessage(&msg, hwnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
#ifdef PERLWIN32GUI_STRONGDEBUG
        printf("XS(DoEvents): PeekMessage returned %d\n", stayhere);
#endif
        if (stayhere) {
            if(msg.message == WM_EXITLOOP) {
                stayhere = 0;
                msg.wParam = (WPARAM) -1;
            } else  {
                // #### trace back to the window's parent
                phwnd = msg.hwnd;
                while((thwnd = GetParent(phwnd)) && IsChild(thwnd, phwnd) ) {
                    phwnd = thwnd;
                }
                // #### now see if the parent window is a DialogBox
                fIsDialog = fIsMDI = FALSE;
                acc = NULL;
                perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(phwnd, GWL_USERDATA);
                if( ValidUserData(perlud) ) {
                    fIsDialog = perlud->dwPlStyle & PERLWIN32GUI_DIALOGUI;
                    fIsMDI    = perlud->dwPlStyle & (PERLWIN32GUI_MDIFRAME | PERLWIN32GUI_HAVECHILDWINDOW);
                    acc = perlud->hAcc;
                }

                if( !( (fIsMDI && TranslateMDISysAccel((HWND)perlud->dwData, &msg)) ||
                       (acc && TranslateAccelerator(phwnd, acc, &msg))              ||
                       (fIsDialog && IsDialogMessage(phwnd, &msg)) ) 
                  ){
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
            }
        }
        else
            msg.wParam = (WPARAM) 0;
    }
    RETVAL = msg.wParam;
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:DoModal([DISABLE_ALL=FALSE])
    # Enter the GUI dialog phase for a specific window: the script halts, the
    # user can interact with the window, events subroutines are triggered as
    # necessary, but no other windows in the application will accept input.
    # DoModal() also brings the window on top of all other windows.
    #
    # B<DISABLE_ALL> flag can set for deactivate all top window and not only parent/active window.
    #
    # The correct usage is:
    #   $window->DoModal(1);
    #
    # To exit from the GUI dialog phase of the modal window, return -1 from the event handler.
    #
    # See also Dialog()
    # See also DoEvents()
BOOL
DoModal(handle, all=FALSE)
    HWND handle
    BOOL all
PREINIT:
    LPPERLWIN32GUI_USERDATA perlud;
    MSG msg;
    int stayhere;
    HWND phwnd;
    HWND thwnd;
    BOOL fIsDialog;
    BOOL fIsMDI;
    HACCEL acc;
    HWND parent;
CODE:
    // Set ISMODAL flag
    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(handle, GWL_USERDATA);
    if( !ValidUserData(perlud) || (perlud->dwPlStyle & PERLWIN32GUI_ISMODAL) )
        XSRETURN_NO;

    perlud->dwPlStyle |= PERLWIN32GUI_ISMODAL;

    // Find its owner window if any or use ActiveWindow
    parent = GetWindow(handle, GW_OWNER);
    if (parent == NULL) {
        parent = GetActiveWindow(); 
    }

    // Disable parent window or all top window
    if (all)
        EnumThreadWindows (GetWindowThreadProcessId(parent, NULL), (WNDENUMPROC) EnableWindowsProc, (LPARAM) FALSE);
    else
        EnableWindow (parent, FALSE);

    // Enable/Show Dialog
    EnableWindow (handle, TRUE);
    ShowWindow(handle, SW_SHOWNORMAL);
    SetActiveWindow(handle);

    // Go to message loop
    stayhere = 1;
    while (stayhere) {

        ENTER;
        SAVETMPS;

        stayhere = GetMessage(&msg, NULL, 0, 0);
 
        if(msg.message == WM_EXITLOOP || msg.message == WM_QUIT) {
            stayhere = 0;
            msg.wParam = (WPARAM) 0;  // Don't return -1 for a DoModal
        } else if(stayhere == -1) {
            stayhere = 0;
            msg.wParam = (WPARAM) -1; // an error occurred...
        } else {

            // #### trace back to the window's parent
            phwnd = msg.hwnd;
            while((thwnd = GetParent(phwnd)) && IsChild(thwnd, phwnd) ) {
                phwnd = thwnd;
            }
            // #### now see if the parent window is a DialogBox
            fIsDialog = fIsMDI = FALSE;
            acc = NULL;
            perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(phwnd, GWL_USERDATA);
            if( ValidUserData(perlud) ) {
                fIsDialog = perlud->dwPlStyle & PERLWIN32GUI_DIALOGUI;
                fIsMDI    = perlud->dwPlStyle & (PERLWIN32GUI_MDIFRAME | PERLWIN32GUI_HAVECHILDWINDOW);
                acc = perlud->hAcc;
            }

            if( !( (fIsMDI && TranslateMDISysAccel((HWND)perlud->dwData, &msg)) ||
                   (acc && TranslateAccelerator(phwnd, acc, &msg))              ||
                   (fIsDialog && IsDialogMessage(phwnd, &msg)) ) 
              ){
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }

        FREETMPS;
        LEAVE;
    }

    // Enable parent or all active topwindow
    if (all)
        EnumThreadWindows (GetWindowThreadProcessId(parent, NULL), (WNDENUMPROC) EnableWindowsProc, (LPARAM) TRUE);
    else
        EnableWindow (parent, TRUE);

    // Hide DialogBox
    ShowWindow(handle, SW_HIDE);
    
    // Active parent
    SetActiveWindow(parent);
    // UnSet ISMODAL flag
    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(handle, GWL_USERDATA);
    if( ValidUserData(perlud))
       perlud->dwPlStyle &= ~PERLWIN32GUI_ISMODAL;

    RETVAL = msg.wParam;
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:Scroll(scrollbar,operation,position[,SB_THUMBTRACK_flag])
    # Handles scrollbar scrolling if you don't want to do it yourself. This is
    # most useful in the Scroll event handler for a window or dialog box.
    #
    # B<scrollbar> can be:
    #   0 : Horizontal scrollbar
    #   1 : Vertical scrollbar
    #
    # B<operation> is an identifier for the operation being performed on the
    # scrollbar, this can be:
    #   SB_LINEUP, SB_LINELEFT, SB_LINEDOWN, SB_LINERIGHT, SB_PAGEUP
    #   SB_PAGELEFT, SB_PAGEDOWN, SB_PAGERIGHT, SB_THUMBPOSITION,
    #   SB_THUMBTRACK, SB_TOP, SB_LEFT, SB_BOTTOM, SB_RIGHT, or SB_ENDSCROLL
    #
    # Returns the position of the scrollbar or undef on failure.
    #
DWORD
Scroll(handle, scrollbar, operation, position, ... )
    HWND handle
    int scrollbar
    int operation
    int position
PREINIT:
    SCROLLINFO si;
CODE:
    si.cbSize = sizeof(SCROLLINFO);
    si.fMask = SIF_ALL;
    if(GetScrollInfo(handle,scrollbar,&si)) {
        si.fMask = SIF_POS;
        switch(operation) {
            case SB_THUMBTRACK:
                if (items <= 4  ||  ! SvIV(ST(4)))
                { break;
                }
            /* fall through */
            case SB_THUMBPOSITION:
                if (position == -1)
                { si.nPos = si.nTrackPos;
                } else
                { si.nPos = position;
                }
            break;
            case SB_LINEUP:
                si.nPos--;
            break;
            case SB_LINEDOWN:
                si.nPos++;
            break;
            case SB_PAGEUP:
                si.nPos -= si.nPage;
            break;
            case SB_PAGEDOWN:
                si.nPos += si.nPage;
            break;
            case SB_TOP:
                si.nPos = si.nMin;
            break;
            case SB_BOTTOM:
                si.nPos = si.nMax;
            break;
        }
        RETVAL = SetScrollInfo(handle, scrollbar, &si, 1);
    }
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:ScrollRange(scrollbar,[min, max])
    # Sets / Gets range for a window scrollbar (if enabled). 
    # B<scrollbar> argument should be set as follows:
    #   0 : Horizontal scrollbar
    #   1 : Vertical scrollbar
    #
    # Returns the scrollbar range as an array, or undef on failure.
void
ScrollRange(handle, scrollbar,...)
    HWND handle
    int scrollbar
PREINIT:
    SCROLLINFO si;  // We use scrollinfo because SetScrollRange is deprecated.
CODE:
    si.cbSize = sizeof(SCROLLINFO);
    si.fMask = SIF_RANGE;
    if(scrollbar > 1) XSRETURN_UNDEF;
    if(items > 2) {
        si.nMin = SvIV(ST(2));
        si.nMax = SvIV(ST(3));
        SetScrollInfo(handle, scrollbar, &si, 1);
    }
    if(GetScrollInfo(handle,scrollbar,&si)) {
        EXTEND(SP, 2);
        XST_mIV(0, si.nMin);
        XST_mIV(1, si.nMax);
        XSRETURN(2);
    }
    else {
        XSRETURN_UNDEF;
    }

    ###########################################################################
    # (@)METHOD:ScrollPage(scrollbar,[pagesize])
    # Sets / Gets page size of a window scrollbar (if enabled). 
    # B<scrollbar> argument should be set as follows:
    #   0 : Horizontal scrollbar
    #   1 : Vertical scrollbar
    #
    # Returns the scrollbar page size or undef on failure.
DWORD
ScrollPage(handle, scrollbar,...)
    HWND handle
    int scrollbar
PREINIT:
    SCROLLINFO si;
CODE:
    si.cbSize = sizeof(SCROLLINFO);
    if(scrollbar > 1) XSRETURN_UNDEF;
    si.fMask = SIF_PAGE;
    if(items > 2) {
        si.nPage = SvIV(ST(2));
        SetScrollInfo(handle, scrollbar, &si, 1);
    }
    if(GetScrollInfo(handle,scrollbar,&si)) {
        RETVAL = si.nPage;
    }
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:ScrollPos(scrollbar,[pos])
    # Sets / Gets position of a window scrollbar (if enabled). 
    # B<scrollbar> argument should be set as follows:
    #   0 : Horizontal scrollbar
    #   1 : Vertical scrollbar
    #
    # Returns the scrollbar position or undef on failure.
DWORD
ScrollPos(handle, scrollbar,...)
    HWND handle
    int scrollbar
PREINIT:
    SCROLLINFO si;
CODE:
    si.cbSize = sizeof(SCROLLINFO);
    if(scrollbar > 1) XSRETURN_UNDEF;
    if(items > 2) {
        si.fMask = SIF_POS;
        si.nPos = SvIV(ST(2));
        SetScrollInfo(handle, scrollbar, &si, 1);
    }
    si.fMask = SIF_POS;
    if(GetScrollInfo(handle,scrollbar,&si)) {
        RETVAL = si.nPos;
    }
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)INTERNAL:LoadCursorFromFile(FILENAME)
HCURSOR
LoadCursorFromFile(filename)
    LPCTSTR filename
CODE:
    RETVAL = LoadCursorFromFile(filename);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:LoadCursor(ID)
    #This function loads one of the default cursors. ID can be one of:
    #
    #  32650 IDC_APPSTARTING  Standard arrow and small hourglass
    #  32512 IDC_ARROW        Standard arrow
    #  32515 IDC_CROSS        Crosshair
    #  32649 IDC_HAND         Windows 98/Me, Windows 2000/XP: Hand
    #  32651 IDC_HELP         Arrow and question mark
    #  32513 IDC_IBEAM        I-beam
    #  32641 IDC_ICON         Obsolete for applications marked version 4.0 or later.
    #  32648 IDC_NO           Slashed circle
    #  32640 IDC_SIZE         Obsolete for applications marked version 4.0 or later. Use IDC_SIZEALL.
    #  32646 IDC_SIZEALL      Four-pointed arrow pointing north, south, east, and west
    #  32643 IDC_SIZENESW     Double-pointed arrow pointing northeast and southwest
    #  32645 IDC_SIZENS       Double-pointed arrow pointing north and south
    #  32642 IDC_SIZENWSE     Double-pointed arrow pointing northwest and southeast
    #  32644 IDC_SIZEWE       Double-pointed arrow pointing west and east
    #  32516 IDC_UPARROW      Vertical arrow
    #  32514 IDC_WAIT         Hourglass
    # 
    #On success returns a Win32::GUI::Cursor object, on failure undef.
    #
    #Example:
    #
    #my $hourglass=Win32::GUI::LoadCursor(32514);
    #    
    #NOTE: it is better to use Win32::GUI::Cursor->new(ID);
void
LoadCursor(ID)
    long ID
PREINIT:
    HCURSOR cursor;
PPCODE:
    cursor = LoadCursor(NULL, MAKEINTRESOURCE(ID));
    if (cursor== NULL) XSRETURN_UNDEF;
    XPUSHs(CreateObjectWithHandle(NOTXSCALL "Win32::GUI::Cursor", (HWND) cursor));
    
     ##########################################################################
     # (@)METHOD:LoadString(ID)
     # The LoadString method loads a string resource from the executable file 
     
LPTSTR
LoadString(uID)
    UINT uID
PREINIT:
    char  lpBuffer[256];
    HINSTANCE moduleHandle;
CODE:
    moduleHandle = GetModuleHandle(NULL);
    if(LoadString(moduleHandle,uID,lpBuffer,256)) {
        RETVAL = (LPTSTR)  lpBuffer;
    } else {
        RETVAL = "";
    }
OUTPUT:
    RETVAL
    

    ###########################################################################
    # (@)INTERNAL:LoadImage(FILENAME, [TYPE, X, Y, FLAGS])
HBITMAP
LoadImage(filename,iType=IMAGE_BITMAP,iX=0,iY=0,iFlags=LR_DEFAULTCOLOR)
    SV *filename
    UINT iType
    int iX
    int iY
    UINT iFlags
PREINIT:
    HINSTANCE moduleHandle;
    HBITMAP bitmap = NULL;
CODE:
    /* Try to find the resource in the current EXE */
    moduleHandle = GetModuleHandle(NULL);

    /* If filename looks like a string, attempt to load from current EXE: */
    if((bitmap ==NULL) && SvPOK(filename) && !(iFlags & LR_LOADFROMFILE)) {
        bitmap = (HBITMAP) LoadImage((HINSTANCE) moduleHandle,
                SvPV_nolen(filename), iType, iX, iY, iFlags);
    }
    
    /* If filename looks like a number, try it as a resource id from the current EXE */
    if((bitmap == NULL) && SvIOK(filename) && !(iFlags & LR_LOADFROMFILE)) {
        bitmap = (HBITMAP) LoadImage((HINSTANCE) moduleHandle,
                MAKEINTRESOURCE(SvIV(filename)), iType, iX, iY, iFlags);
    }

    /* Try to find the resource from GUI.dll */
    moduleHandle = GetModuleHandle("GUI.dll");

    /* If filename looks like a string, try it as a resource name from GUI.dll */
    if((bitmap == NULL) && SvPOK(filename) && !(iFlags & LR_LOADFROMFILE)) {
        bitmap = (HBITMAP) LoadImage((HINSTANCE) moduleHandle,
                SvPV_nolen(filename), iType, iX, iY, iFlags);
    }

    /* If filename looks like a number, try it as a resource id from GUI.dll */
    if((bitmap == NULL) && SvIOK(filename) && !(iFlags & LR_LOADFROMFILE)) {
        bitmap = (HBITMAP) LoadImage((HINSTANCE) moduleHandle,
                MAKEINTRESOURCE(SvIV(filename)), iType, iX, iY, iFlags);
    }

    /* Try to load from file or as an OEM resource */
    moduleHandle = NULL;

    /* if filename looks like a string, try it as a file name */
    if((bitmap == NULL) && SvPOK(filename)) {
        bitmap = (HBITMAP) LoadImage((HINSTANCE) moduleHandle,
                SvPV_nolen(filename), iType, iX, iY, iFlags|LR_LOADFROMFILE);
    }

    /* If filename looks like a number, try it as an OEM resource id */
    if((bitmap == NULL) && SvIOK(filename) && !(iFlags & LR_LOADFROMFILE)) {
        bitmap = (HBITMAP) LoadImage((HINSTANCE) moduleHandle,
                MAKEINTRESOURCE(SvIV(filename)), iType, iX, iY, iFlags);
    }

    /* Finally, if it looks like a number, try it as a pre-defined resource */
    if((bitmap == NULL) && SvIOK(filename)) {
        if(iType == IMAGE_BITMAP) {
            bitmap = (HBITMAP)LoadBitmap(NULL, MAKEINTRESOURCE(SvIV(filename)));
        }
        else if (iType == IMAGE_ICON) {
            bitmap = (HBITMAP)LoadIcon(NULL, MAKEINTRESOURCE(SvIV(filename)));
        }
        else if (iType == IMAGE_CURSOR) {
            bitmap = (HBITMAP)LoadCursor(NULL, MAKEINTRESOURCE(SvIV(filename)));
        }
    }

    RETVAL = bitmap;
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)INTERNAL:DestroyCursor()
BOOL
DestroyCursor(cursor)
    HCURSOR cursor
CODE:
    RETVAL = DestroyCursor(cursor);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:SetCursor(CURSOR)
    # Draws the specified B<CURSOR> (a Win32::GUI::Cursor object). Returns the
    # handle of the previously displayed cursor. Note that the cursor will
    # change back to the default one as soon as the mouse moves or a system
    # command is performed. To change the cursor stablily, use ChangeCursor().
    #
    # see also ChangeCursor()
HCURSOR
SetCursor(cursor)
    HCURSOR cursor
CODE:
    RETVAL = SetCursor(cursor);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:GetCursor()
    # Returns the handle of the current cursor.
HCURSOR
GetCursor()
CODE:
    RETVAL = GetCursor();
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:ChangeCursor(CURSOR)
    # Changes the default cursor for a window to B<CURSOR> (a Win32::GUI::Cursor
    # object). Returns the handle of the previous default cursor.
    #
    # see also new Win32::GUI::Cursor
HCURSOR
ChangeCursor(handle, cursor)
    HWND handle
    HCURSOR cursor
CODE:
    RETVAL = (HCURSOR) SetClassLong(handle, GCL_HCURSOR, (LONG) cursor);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:GetCursorPos()
    # Gets the absolute mouse cursor position. Returns an array containing
    # x and y co-ordinates.
    #
    # Usage:
    #   ($x, $y) = Win32::GUI::GetCursorPos;
    #
    # See also ScreenToClient()
    # See also SetCursorPos()
void
GetCursorPos()
PREINIT:
    POINT point;
PPCODE:
    if(GetCursorPos(&point)) {
        EXTEND(SP, 2);
        XST_mIV(0, point.x);
        XST_mIV(1, point.y);
        XSRETURN(2);
    } else {
        XSRETURN_UNDEF;
    }

    ###########################################################################
    # (@)METHOD:SetCursorPos(X, Y)
    # Moves the mouse cursor to the specified screen coordinates.
    #
    # see also GetCursorPos()
BOOL
SetCursorPos(x, y)
    int x
    int y
CODE:
    RETVAL = SetCursorPos(x, y);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:ClipCursor([LEFT, TOP, RIGHT, BOTTOM])
    # Confines the cursor to the specified screen rectangle. Call it without
    # parameters to release the cursor. Returns nonzero on success
BOOL
ClipCursor(left=0, top=0, right=0, bottom=0)
    LONG left
    LONG top
    LONG right
    LONG bottom
PREINIT:
    RECT r;
CODE:
    if(items == 0) {
        RETVAL = ClipCursor(NULL);
    } else {
        r.left = left;
        r.top = top;
        r.right = right;
        r.bottom = bottom;
        RETVAL = ClipCursor(&r);
    }
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:ChangeIcon(ICON)
    # Changes the default icon for a window to B<ICON> (a Win32::GUI::Icon
    # object). Returns the handle of the previous default icon.
HICON
ChangeIcon(handle, icon)
    HWND handle
    HICON icon
CODE:
    SetClassLong(handle, GCL_HICONSM, (LONG) icon);
    RETVAL = (HICON) SetClassLong(handle, GCL_HICON, (LONG) icon);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:ChangeSmallIcon(ICON)
    # Changes the default small icon for a window to B<ICON> (a Win32::GUI::Icon
    # object). Returns the handle of the previous default small icon.
HICON
ChangeSmallIcon(handle, icon)
    HWND handle
    HICON icon
CODE:
    RETVAL = (HICON) SetClassLong(handle, GCL_HICONSM, (LONG) icon);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)INTERNAL:DestroyIcon()
BOOL
DestroyIcon(icon)
    HICON icon
CODE:
    RETVAL = DestroyIcon(icon);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:GetClassName()
    # Returns the class name for a window or control.
void
GetClassName(handle)
    HWND handle
PREINIT:
    LPTSTR lpClassName;
    int nMaxCount;
PPCODE:
    nMaxCount = 256;
    lpClassName = (LPTSTR) safemalloc(nMaxCount);
    if(GetClassName(handle, lpClassName, nMaxCount) > 0) {
        EXTEND(SP, 1);
        XST_mPV(0, lpClassName);
        safefree(lpClassName);
        XSRETURN(1);
    } else {
        safefree(lpClassName);
        XSRETURN_NO;
    }

    ###########################################################################
    # (@)METHOD:GetParent()
    # Returns the parent window for this child control/window. If there is no
    # parent window or there has been an error, undef is returned.
void
GetParent(handle)
    HWND handle
PREINIT:
  HWND parentHandle;
  SV* SvParent;
PPCODE:
  parentHandle=GetParent(handle);
  if (parentHandle!=NULL) {
    SvParent = SV_SELF_FROM_WINDOW(parentHandle);
    if (SvParent != NULL && SvROK(SvParent)) {
      XPUSHs(SvParent);
    }
    else {
      XSRETURN_UNDEF;
    }
  } 
  else {
    XSRETURN_UNDEF;
  }

    ###########################################################################
    # (@)INTERNAL:GetWindowObject()
    # Returns the perl window object from a window handle.  If the window handle
    # passed is a handle to a window created by Win32::GUI, returns the perl
    # object reference, else returns undef.
void
GetWindowObject(handle)
    HWND handle
PREINIT:
  SV* SvObject;
PPCODE:
  if (IsWindow(handle)) {
    SvObject = SV_SELF_FROM_WINDOW(handle);
    if (SvObject != NULL && SvROK(SvObject)) {
      XPUSHs(SvObject);
    }
    else {
      XSRETURN_UNDEF;
    }
  } 
  else {
    XSRETURN_UNDEF;
  }
  
    ###########################################################################
    # (@)INTERNAL:_UserData()
    # Return a reference to an HV, stored in the perlud.UserData member
    # of the PERLWIN32GUI_USERDATA struct
HV *
_UserData(handle)
    HWND handle
PREINIT:
    LPPERLWIN32GUI_USERDATA perlud;
CODE:
    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(handle, GWL_USERDATA);
    if( ! ValidUserData(perlud) ) {
        XSRETURN_UNDEF;
    }

    if(perlud->userData == NULL)
        perlud->userData = (SV*)newHV();

    RETVAL = (HV*)perlud->userData;
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:FindWindow(CLASSNAME, WINDOWNAME)
    # Returns the handle of the window whose class name and window name match
    # the specified strings; both strings can be empty. Note that the function
    # does not search child windows, only top level windows.
    #
    # If no matching windows is found, the return value is zero.
HWND
FindWindow(classname,windowname)
    LPCTSTR classname
    LPCTSTR windowname
CODE:
    if(strlen(classname) == 0) classname = NULL;
    if(strlen(windowname) == 0) windowname = NULL;
    RETVAL = FindWindow(classname, windowname);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:GetWindowLong(INDEX)
    # Retrieves a windows property; for more info consult the original API
    # documentation.
LONG
GetWindowLong(handle,index)
    HWND handle
    int index
CODE:
    RETVAL = GetWindowLong(handle, index);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:SetWindowLong(INDEX, VALUE)
    # Sets a windows property; for more info consult the original API
    # documentation.
LONG
SetWindowLong(handle,index,value)
    HWND handle
    int  index
    LONG value
CODE:
    RETVAL = SetWindowLong(handle, index, value);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:SetWindowPos(INSERTAFTER,X,Y,cx,cy,FLAGS)
    # The SetWindowPos function changes the size, position,
    # and Z order of a child, pop-up, or top-level
    # window. Child, pop-up, and top-level windows are
    # ordered according to their appearance on the
    # screen. The topmost window receives the highest rank
    # and is the first window in the Z order.
    #
    # INSERTAFTER - window to precede the positioned window
    # in the Z order. This parameter must be a window object,
    # a window handle or one of the following integer values.
    #   HWND_BOTTOM
    #     Places the window at the bottom of the Z order. If
    #     the WINDOW parameter identifies a topmost window,
    #     the window loses its topmost status and is placed
    #     at the bottom of all other windows.
    #   HWND_NOTOPMOST
    #     Places the window above all non-topmost windows
    #     (that is, behind all topmost windows). This flag
    #     has no effect if the window is already a
    #     non-topmost window.
    #   HWND_TOP
    #     Places the window at the top of the Z order.
    #   HWND_TOPMOST
    #     Places the window above all non-topmost
    #     windows. The window maintains its topmost position
    #     even when it is deactivated.
BOOL
SetWindowPos(handle,insertafter,X,Y,cx,cy,flags)
    HWND handle
    HWND insertafter
    int X
    int Y
    int cx
    int cy
    int flags
CODE:
    RETVAL = SetWindowPos(handle, insertafter, X, Y, cx, cy, flags);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:GetWindow(COMMAND)
    # Returns handle of the window that has the specified
    # relationship (given by B<COMMAND>) with the specified window.
    #
    # Available B<COMMAND> are:
    #   GW_CHILD
    #   GW_HWNDFIRST
    #   GW_HWNDLAST
    #   GW_HWNDNEXT
    #   GW_HWNDPREV
    #   GW_OWNER
    #
    # Example:
    #     $Button->GetWindow(GW_OWNER);
HWND
GetWindow(handle,command)
    HWND handle
    UINT command
CODE:
    RETVAL = GetWindow(handle, command);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:Show([COMMAND=SW_SHOWNORMAL])
    # Shows a window (or change its showing state to B<COMMAND>); 
    #
    # Available B<COMMAND> are:
    #
    #   0  : SW_HIDE
    #   1  : SW_SHOWNORMAL
    #   1  : SW_NORMAL
    #   2  : SW_SHOWMINIMIZED
    #   3  : SW_SHOWMAXIMIZED
    #   3  : SW_MAXIMIZE
    #   4  : SW_SHOWNOACTIVATE
    #   5  : SW_SHOW
    #   6  : SW_MINIMIZE
    #   7  : SW_SHOWMINNOACTIVE
    #   8  : SW_SHOWNA
    #   9  : SW_RESTORE
    #   10 : SW_SHOWDEFAULT
    #   11 : SW_FORCEMINIMIZE
    #   11 : SW_MAX
    #
BOOL
Show(handle,command=SW_SHOWNORMAL)
    HWND handle
    int command
CODE:
    RETVAL = ShowWindow(handle, command);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)INTERNAL:_Animate(HANDLE, TIME, FLAGS)
    # Wrapper for Win32 AnimateWindow call.  See Win32::GUI::Animate in GUI.pm
    # for more details.
BOOL
_Animate(handle, time, flags)
    HWND handle
    DWORD time
    DWORD flags
CODE:
    RETVAL = AnimateWindow(handle, time, flags);
OUTPUT:
    RETVAL
    

    ###########################################################################
    # (@)METHOD:Hide()
    # Hides a window or control.
BOOL
Hide(handle)
    HWND handle
CODE:
    RETVAL = ShowWindow(handle, SW_HIDE);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:Maximize()
    # Maximizes a window.
BOOL
Maximize(handle)
    HWND handle
CODE:
    RETVAL = ShowWindow(handle, SW_SHOWMAXIMIZED);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:SetWindowRgn(region,flag)
    # The SetWindowRgn method sets the window region of a window. The window region determines the area 
    # within the window where the system permits drawing. The system does not display any portion of a window 
    # that lies outside of the window region 
    #
    # flag : Specifies whether the system redraws the window after setting the window region. If flag is TRUE, 
    # the system does so; otherwise, it does not. 
    # Typically, you set flag to TRUE if the window is visible. 
BOOL
SetWindowRgn(handle, region, flag=1)
    HWND handle
    HRGN region
    BOOL flag
CODE:
    RETVAL = SetWindowRgn(handle, region, flag);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:Update()
    # Repaints a window if it's update region is not empty.
    #
    # see also Redraw()
    # see also InvalidateRect()
BOOL
Update(handle)
    HWND handle
CODE:
    RETVAL = UpdateWindow(handle);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:Redraw()
    # Repaints a window
    #
    # See also Update()
    # See also InvalidateRect()
BOOL
Redraw(handle, ...)
    HWND handle
PREINIT:
    RECT rect;
    LPRECT lpRect;
    UINT flags;
CODE:
    if(items != 2 && items != 6) {
        CROAK("Usage: Redraw(handle, flag);\n   or: Redraw(handle, left, top, right, bottom, flag);\n");
    }
    if(items == 2) {
        lpRect = (LPRECT) NULL;
        flags = (UINT) SvIV(ST(1));
    }
    else {
        rect.left = SvIV(ST(1));
        rect.top = SvIV(ST(2));
        rect.right = SvIV(ST(3));
        rect.bottom = SvIV(ST(4));
        flags = (UINT) SvIV(ST(5));
        lpRect = &rect;
    }
    RETVAL = RedrawWindow(handle,lpRect, (HRGN) NULL, flags);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:LockWindowUpdate(flag)
    # The LockWindowUpdate method disables or enables drawing in the specified window. Only one window 
    # can be locked at a time. 
    #
    # If an application with a locked window (or any locked child windows) calls the GetDC function,
    # the called function returns a device context with a visible region that is empty. This will 
    # occur until the application unlocks the window by calling LockWindowUpdate method specifying a 
    # value for the flag.
    # Example:
    # $win->LockWindowUpdate;     #Locks window
    # $win->LockWindowUpdate(1);  #Unlocks window
BOOL
LockWindowUpdate(handle, flag=0)
    HWND handle
    int flag
CODE:
    RETVAL = LockWindowUpdate(flag == 0 ? handle : NULL);
OUTPUT:
    RETVAL
    

    ###########################################################################
    # (@)METHOD:InvalidateRect(...)
    # Forces a refresh of a window, or a rectangle of it. 
    #
    # The parameters can be B<(FLAG)> for the whole area of the window, or B<(LEFT, TOP, RIGHT, BOTTOM,
    # [FLAG])> to specify a rectangle. If the B<FLAG> parameter is set to TRUE, the
    # background is erased before the window is refreshed (this is the default).
    #
    # See also Redraw()
    # See also Update()
BOOL
InvalidateRect(handle, ...)
    HWND handle
PREINIT:
    RECT rect;
    LPRECT lpRect;
    BOOL bErase;
CODE:
    if(items != 2 && items && items != 6) {
        CROAK("Usage: InvalidateRect(handle, flag);\n   or: InvalidateRect(handle, left, top, right, bottom, [flag]);\n");
    }
    if(items == 2) {
        lpRect = (LPRECT) NULL;
        bErase = (BOOL) SvIV(ST(1));
    } else {
        rect.left   = SvIV(ST(1));
        rect.top    = SvIV(ST(2));
        rect.right  = SvIV(ST(3));
        rect.bottom = SvIV(ST(4));
        if(items == 5)
            bErase      = TRUE;
        else
            bErase      = (BOOL) SvIV(ST(5));
        lpRect      = &rect;
    }
    RETVAL = InvalidateRect(handle, lpRect, bErase);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)INTERNAL:DestroyWindow()
BOOL
DestroyWindow(handle)
    HWND handle
CODE:
    RETVAL = DestroyWindow(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:GetMessage([MIN=0, MAX=0])
    # Retrieves a message sent to the window, optionally considering only
    # messages identifiers in the range B<MIN..MAX>. 
    #
    # If a message is found, the function returns a 7 elements array containing:
    #
    #   - the result code of the message
    #   - the message identifier
    #   - the wParam argument
    #   - the lParam argument
    #   - the time when message occurred
    #   - the x coordinate at which message occurred
    #   - the y coordinate at which message occurred
    #
    # If the result code of the message was -1 the function returns undef. Note
    # that this function should not be normally used unless you know very well
    # what you're doing.
void
GetMessage(handle,min=0,max=0)
    HWND handle
    UINT min
    UINT max
PREINIT:
    MSG msg;
    BOOL result;
PPCODE:
    result = GetMessage(&msg, handle, min, max);
    if(result == -1) {
        XSRETURN_UNDEF;
    } else {
        EXTEND(SP, 7);
        XST_mIV(0, result);
        XST_mIV(1, msg.message);
        XST_mIV(2, msg.wParam);
        XST_mIV(3, msg.lParam);
        XST_mIV(4, msg.time);
        XST_mIV(5, msg.pt.x);
        XST_mIV(6, msg.pt.y);
        XSRETURN(7);
    }


    ###########################################################################
    # (@)METHOD:SendMessage(MSG, WPARAM, LPARAM)
    # Sends a message to a window.
LRESULT
SendMessage(handle,msg,wparam,lparam)
    HWND handle
    UINT msg
    WPARAM wparam
    LPARAM lparam
CODE:
    RETVAL = SendMessage(handle, msg, wparam, lparam);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:PostMessage(MSG, WPARAM, LPARAM)
    # Posts a message to a window.
LRESULT
PostMessage(handle,msg,wparam,lparam)
    HWND handle
    UINT msg
    WPARAM wparam
    LPARAM lparam
CODE:
    RETVAL = PostMessage(handle, msg, wparam, lparam);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:WaitMessage()
    # The WaitMessage function yields control to other threads when a thread 
    # has no other messages in its message queue. The WaitMessage function suspends 
    # the thread and does not return until a new message is placed in the thread's 
    # message queue.
BOOL
WaitMessage()
CODE:
  RETVAL = WaitMessage();
OUTPUT:
  RETVAL

    ###########################################################################
    # (@)METHOD:SendMessageTimeout(MSG, WPARAM, LPARAM, [FLAGS=SMTO_NORMAL], TIMEOUT)
    # Sends a message to a window and wait for it to be processed or until the
    # specified B<TIMEOUT> (number of milliseconds) elapses. 
    #
    # Returns the result code of the processed message or undef on errors.
    #
    # If undef is returned and a call to Win32::GetLastError() returns 0,
    # then the window timed out processing the message.
    #
    # The B<FLAGS> parameter is optional, possible values are:
    #  0 : SMTO_NORMAL 
    #      The calling thread can process other requests while waiting; this is the default setting.
    #  1 : SMTO_BLOCK
    #      The calling thread does not process other requests.
    #  2 : SMTO_ABORTIFHUNG
    #      Returns without waiting if the receiving process seems to be "hung".
void
SendMessageTimeout(handle,msg,wparam,lparam,flags=SMTO_NORMAL,timeout)
    HWND handle
    UINT msg
    WPARAM wparam
    LPARAM lparam
    UINT flags
    UINT timeout
PREINIT:
    DWORD result;
PPCODE:
    if(SendMessageTimeout(handle, msg, wparam, lparam, flags, timeout, &result) == 0) {
        XSRETURN_UNDEF;
    } else {
        XSRETURN_IV(result);
    }


    ###########################################################################
    # (@)METHOD:PostQuitMessage([EXITCODE])
    # Sends a quit message to a window, optionally with an B<EXITCODE>;
    # if no B<EXITCODE> is given, it defaults to 0.
void
PostQuitMessage(...)
PPCODE:
    int exitcode;
    if(items > 0)
        exitcode = SvIV(ST(items-1));
    else
        exitcode = 0;
    PostQuitMessage(exitcode);


    ###########################################################################
    # (@)METHOD:PeekMessage([MIN, MAX, MESSAGE])
    # Inspects the window's message queue and eventually returns data
    # about the message it contains; it can optionally check only for message
    # identifiers in the range B<MIN..MAX>; the last B<MESSAGE parameter>, if
    # specified, must be an array reference.
    #
    # If a message is found, the function puts in that array 7 elements
    # containing:
    #   - the handle of the window to which the message is addressed
    #   - the message identifier
    #   - the wParam argument
    #   - the lParam argument
    #   - the time when message occurs
    #   - the x coordinate at which message occurs
    #   - the y coordinate at which message occurs
    #
BOOL
PeekMessage(handle, min=0, max=0, message=&PL_sv_undef)
    HWND handle
    UINT min
    UINT max
    SV* message
PREINIT:
    MSG msg;
CODE:
    ZeroMemory(&msg, sizeof(msg));
    RETVAL = PeekMessage(&msg, handle, min, max, PM_NOREMOVE);
    if(message != &PL_sv_undef) {
        if(SvROK(message) && SvTYPE(SvRV(message)) == SVt_PVAV) {
            av_clear((AV*) SvRV(message));
            av_push((AV*) SvRV(message), newSViv((long) msg.hwnd));
            av_push((AV*) SvRV(message), newSViv(msg.message));
            av_push((AV*) SvRV(message), newSViv(msg.wParam));
            av_push((AV*) SvRV(message), newSViv(msg.lParam));
            av_push((AV*) SvRV(message), newSViv(msg.time));
            av_push((AV*) SvRV(message), newSViv(msg.pt.x));
            av_push((AV*) SvRV(message), newSViv(msg.pt.y));
        } else {
            W32G_WARN("Win32::GUI: fourth parameter to PeekMessage is not an array reference");
        }
    }
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:Hook(MSG,CODEREF)
    # Adds a new handler to the list of handlers for a particular window
    # message / command / notification.
    #
    # Hook() can be used with the New Event Model and the Old Event Model. You
    # can Hook() normal window messages, WM_COMMAND codes and WM_NOTIFY codes.
    # You can add as many hooks for one message as you like. To remove hooks
    # see UnHook().
    #
    # Here's an example Perl handler routine:
    #
    #  sub click_handler {
    #     ($object, $wParam, $lParam, $type, $msgcode) = @_;
    #     print "Click handler called!\n";
    #  }
    #
    # Here, $object is the Perl object for the widget, $wParam and $lParam are
    # the parameters received with the message, $type is the type of message
    # (0, WM_NOTIFY or WM_COMMAND, see below), and $msgcode is the original
    # numeric code for the message.
    #
    # If you call Hook() on a child widget, such as a button, the Hook will be
    # called if the parent window receives WM_COMMAND and the code given with
    # WM_COMMAND matches the MSG argument you passed to Hook(). Put simply,
    # what this means is that things like
    #
    #     $button->Hook(BN_CLICKED, \&button_clicked);
    #
    # will work. When your handler is called it will be passed a $type argument
    # of WM_COMMAND (numeric 273).
    #
    # The same is true for WM_NOTIFY messages, although handlers defined for
    # those are passed a $type argument of WM_NOTIFY (numeric 78).
    #
    # Any message that was not WM_NOTIFY or WM_COMMAND gets passed a $type of 0.
    # It is important to check your $type argument. Certain codes for WM_COMMAND
    # messages may conflict with codes for WM_NOTIFY messages or regular window
    # messages, meaning the handler you defined for a particular WM_NOTIFY code
    # may get triggered by a WM_COMMAND code. The $type argument is there to
    # allow you to check this. The rule is to just return immediately if $type
    # is not what you were expecting.
    #
    # If Hook() successfully added a hook for the event, it returns true. If
    # the hook already exists (the coderef you gave is already in the list of
    # hooks for the specified event), or if there was an error in creating the
    # new hook, it returns false.
    #

void
Hook(handle,msg,coderef)
    HWND handle
    I32 msg
    SV* coderef
PREINIT:
    LPPERLWIN32GUI_USERDATA perlud;
    SV** arrayref;
    AV* newarray;
    int i;
    SV** value;
PPCODE:
    if(msg < 0) { msg = 0 - msg; }; // Looks wrong but if hooks are used correctly this should be OK.
    if(SvOK(coderef) && SvROK(coderef) && SvTYPE(SvRV(coderef)) == SVt_PVCV) {
        // We have a code reference.
        perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(handle, GWL_USERDATA);
        if(perlud->avHooks == NULL) {
            perlud->avHooks = newAV();
        }

        // Check if array value for this message exists already:
        arrayref = av_fetch(perlud->avHooks, msg, 0);
        if(arrayref == NULL) {
            // No array reference for this msg yet, so make one and insert our
            // handler ref:
            newarray = newAV();
            av_push(newarray, coderef);
            SvREFCNT_inc(coderef);
            if(av_store(perlud->avHooks, msg, newRV_noinc((SV*) newarray)) == NULL) {
                SvREFCNT_dec((SV*) newarray);
                CROAK("AddHook failed to store new array reference.\n");
                XSRETURN_NO;
            }
        }
        else {
            // There IS an array reference already, so add the handler.
            // First, check the handler isn't already there:
            for(i = 0; i <= (int) av_len((AV*) SvRV(*arrayref)); i++) {
                value = av_fetch((AV*) SvRV(*arrayref), i,0);
                if(sv_eq(*value,coderef)) {
                    XSRETURN_NO;
                }
            }
            // Add the coderef:
            av_push((AV*) SvRV(*arrayref), coderef);
            SvREFCNT_inc(coderef);
        }
        XSRETURN_YES;
    }
    else {
        CROAK("Usage: Hook(message, coderef)\n");
        XSRETURN_NO;
    }

    ###########################################################################
    # (@)METHOD:UnHook(MSG,[CODEREF])
    # Removes a specific code reference from the hooks listing for the given
    # message, or removes all code references for the given message if no
    # coderef is specified.
    #
    # Returns true on success, and false on failure (no such hook).
    #
    # See Hook() documentation for more information on hooks and their
    # usage.
    #

void
UnHook(handle,msg,coderef = NULL)
    HWND handle
    I32 msg
    SV* coderef
PREINIT:
    LPPERLWIN32GUI_USERDATA perlud;
    SV** arrayref;
    SV** removedvalue;
    int i;
    int count = 0;
PPCODE:
    if(msg < 0) { msg = 0 - msg; };
    if(coderef != NULL && !(SvOK(coderef) && SvROK(coderef) && SvTYPE(SvRV(coderef)) == SVt_PVCV)) {
        CROAK("Usage: UnHook(message,[coderef]). What you gave as a coderef was not a code reference\n");
        XSRETURN_NO;
    }
    else {
    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(handle, GWL_USERDATA);
        if(perlud->avHooks == NULL) {
            XSRETURN_NO;
        }
        else {
            arrayref = av_fetch(perlud->avHooks, msg, 0);
            if(arrayref == NULL) {
            XSRETURN_NO;
        }
            else {
                for(i = 0; i <= (int) av_len((AV*) SvRV(*arrayref)); i++) {
                    removedvalue = av_fetch((AV*) SvRV(*arrayref), i,0);
                    if(removedvalue != NULL && (coderef == NULL || sv_eq(*removedvalue,coderef)) ) {
                        /* SvREFCNT_dec(*removedvalue);
                        av_delete((AV*) SvRV(*arrayref), i, 0); */
                        av_delete((AV*) SvRV(*arrayref), i, G_DISCARD);
                        count++;
                    }
                }
                if(coderef == NULL || av_len((AV*) SvRV(*arrayref)) == -1) {
                    removedvalue = av_fetch(perlud->avHooks, msg, 0);
                    /* SvREFCNT_dec(*removedvalue);
                    av_delete(perlud->avHooks, msg, 0); */
                    av_delete(perlud->avHooks, msg, G_DISCARD);
                }
                XSRETURN_IV(count);
            }
        }
    }

    ###########################################################################
    # (@)METHOD:Result(HANDLE, RESULT)
    # Explicitly set the result to be returned from a handler. For safety and
    # backwards compatibility, results returned from Win32::GUI handlers are
    # discarded. You can use this method (with GREAT CARE) to explicitly force
    # a return value for your handler. Consult the API documentation for valid
    # return values as they vary from message to message.

void
Result(handle, result)
    HWND handle
    int result
PREINIT:
    LPPERLWIN32GUI_USERDATA perlud;
CODE:
    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(handle, GWL_USERDATA);
    if(ValidUserData(perlud)) {
        perlud->forceResult = (LRESULT) result;
        XSRETURN_YES;
    }
    else
        XSRETURN_NO;


    ###########################################################################
    # (@)METHOD:SetFont(FONT)
    # Sets the font of the window (FONT is a Win32::GUI::Font object).

LRESULT
SetFont(handle, font)
    HWND   handle
    HFONT  font
CODE:
    RETVAL = SendMessage(handle, WM_SETFONT, (WPARAM) font, 0);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:GetFont(FONT)
    # Gets the font of the window (returns an handle; use to get font details).
    #
    #   $Font = $W->GetFont();
    #   %details = Win32::GUI::Font::Info( $Font );

LRESULT
GetFont(handle)
    HWND   handle
CODE:
    RETVAL = SendMessage(handle, WM_GETFONT, 0, 0);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:SetRedraw(FLAG)
    # Determines if a window is automatically redrawn when its content changes.
    #
    # B<FLAG> can be a true value to allow redraw, false to prevent it.

LRESULT
SetRedraw(handle, value)
    HWND   handle
    WPARAM value
CODE:
    RETVAL = SendMessage(handle, WM_SETREDRAW, value, 0);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:SetIcon(ICON, [TYPE])
    # Sets the icon of the window; B<TYPE> can be 0 for the small icon, 1 for
    # the big icon. Default is the same icon for small and big.
LRESULT
SetIcon(handle, icon, type=ICON_SMALL)
    HWND   handle
    HICON  icon
    WPARAM type    
CODE:
    if (items > 2)
        RETVAL = SendMessage(handle, WM_SETICON, type, (LPARAM) icon);
    else {
        SendMessage(handle, WM_SETICON, ICON_SMALL, (LPARAM) icon);
        RETVAL = SendMessage(handle, WM_SETICON, ICON_BIG, (LPARAM) icon);
    }
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:Text([TEXT])
    # (@)METHOD:Caption([TEXT])
    # Sets or gets the text associated with a window or control. For example,
    # for windows, this is the text in the titlebar of the window. For button
    # controls, it's the text on the button, and so on. Text() and Caption()
    # are synonymous with oneanother.
void
Text(handle,...)
    HWND handle
ALIAS:
    Win32::GUI::Caption = 1
PREINIT:
    char *myBuffer;
    int myLength;
PPCODE:
    if(items > 2) {
        CROAK("Usage: Text(handle, [value]);\n");
    }
    if(items == 1) {
        myLength = GetWindowTextLength(handle)+1;
        if(myLength) {
            myBuffer = (char *) safemalloc(myLength);
            if(GetWindowText(handle, myBuffer, myLength)) {
                EXTEND(SP, 1);
                XST_mPV(0, myBuffer);
                safefree(myBuffer);
                XSRETURN(1);
            }
            safefree(myBuffer);
        }
        XSRETURN_NO;
    } else {
        XSRETURN_IV((long) SetWindowText(handle, (LPCTSTR) SvPV_nolen(ST(1))));
    }


    ###########################################################################
    # (@)METHOD:Move(X, Y)
    # Moves a window to the given B<X> and B<Y> co-ordinates.
BOOL
Move(handle,x,y)
    HWND handle
    int x
    int y
CODE:
    RETVAL = SetWindowPos(handle, (HWND) NULL, x, y, 0, 0,
                          SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:Resize(WIDTH, HEIGHT)
    # Resizes a window to the given B<WIDTH> and B<HEIGHT>.
BOOL
Resize(handle,x,y)
    HWND handle
    int x
    int y
CODE:
    RETVAL = SetWindowPos(handle, (HWND) NULL, 0, 0, x, y,
                          SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:GetClientRect()
    # Gets the client area rectangle and returns an array of left, top, right,
    # and bottom co-ordinates if successful. Left and top will always be 0,
    # right and bottom are equivalent to the width and height of the client
    # area.
void
GetClientRect(handle)
    HWND handle
PREINIT:
    RECT myRect;
PPCODE:
    if(GetClientRect(handle, &myRect)) {
        EXTEND(SP, 4);
        XST_mIV(0, myRect.left);
        XST_mIV(1, myRect.top);
        XST_mIV(2, myRect.right);
        XST_mIV(3, myRect.bottom);
        XSRETURN(4);
    } else {
        XSRETURN_UNDEF;
    }

    ###########################################################################
    # (@)METHOD:GetAbsClientRect()
    # Gets the absolute screen co-ordinates of the client rectangle and returns
    # an array of left, top, right, and bottom co-ordinates.
void
GetAbsClientRect(handle)
    HWND handle
PREINIT:
    WINDOWINFO pwi;
PPCODE:
    pwi.cbSize = sizeof(WINDOWINFO);
    if(GetWindowInfo(handle, &pwi)) {
        EXTEND(SP, 4);
        XST_mIV(0, pwi.rcClient.left);
        XST_mIV(1, pwi.rcClient.top);
        XST_mIV(2, pwi.rcClient.right);
        XST_mIV(3, pwi.rcClient.bottom);
        XSRETURN(4);
    }
    else {
        XSRETURN_UNDEF;
    }

    ###########################################################################
    # (@)METHOD:GetWindowRect()
    # Returns a four elements array defining the windows rectangle (left, top,
    # right, bottom) in screen co-ordinates or undef on errors.
void
GetWindowRect(handle)
    HWND handle
PREINIT:
    RECT myRect;
PPCODE:
    if(GetWindowRect(handle, &myRect)) {
        EXTEND(SP, 4);
        XST_mIV(0, myRect.left);
        XST_mIV(1, myRect.top);
        XST_mIV(2, myRect.right);
        XST_mIV(3, myRect.bottom);
        XSRETURN(4);
    } else {
        XSRETURN_UNDEF;
    }


    ###########################################################################
    # (@)METHOD:Width([WIDTH])
    # Sets or retrieves the width of a window.
    #
    # See also Resize()
void
Width(handle,...)
    HWND handle
PREINIT:
    RECT myRect;
PPCODE:
    if(items > 2) {
        croak("Usage: Width(handle, [value]);\n");
    }

    if(!GetWindowRect(handle, &myRect)) XSRETURN_NO;

    if(items == 1) {
        EXTEND(SP, 1);
        XST_mIV(0, (myRect.right-myRect.left));
        XSRETURN(1);
    } else {
        if(SetWindowPos(handle, (HWND) NULL, 0, 0,
                        (int) SvIV(ST(1)),
                        (int) (myRect.bottom-myRect.top),
                        SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_DEFERERASE)) {
            XSRETURN_YES;
        } else {
            XSRETURN_NO;
        }
    }


    ###########################################################################
    # (@)METHOD:Height([HEIGHT])
    # Sets or retrieves the height of a window.
    #
    # See also Resize()
void
Height(handle,...)
    HWND handle
PREINIT:
    RECT myRect;
PPCODE:
    if(items > 2) {
        croak("Usage: Height(handle, [value]);\n");
    }

    if(!GetWindowRect(handle, &myRect)) XSRETURN_NO;

    if(items == 1) {
        EXTEND(SP, 1);
        XST_mIV(0, (myRect.bottom-myRect.top));
        XSRETURN(1);
    } else {
        if(SetWindowPos(handle, (HWND) NULL, 0, 0,
                        (int) (myRect.right-myRect.left),
                        (int) SvIV(ST(1)),
                        SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_DEFERERASE)) {
            XSRETURN_YES;
        } else {
            XSRETURN_NO;
        }
    }


    ###########################################################################
    # (@)METHOD:Left([LEFT])
    # Gets or sets the left co-ordinate of an object, relative to the object's
    # parent if it has one, or absolute screen co-ordinate if it doesn't.
    #
    # See also AbsLeft()
    # See also Move()
void
Left(handle,...)
    HWND handle
PREINIT:
    RECT myRect;
    HWND parent;
    POINT myPt;
PPCODE:
    if(items > 2) {
        croak("Usage: Left(handle, [value]);\n");
    }
    if(!GetWindowRect(handle, &myRect)) XSRETURN_NO;
    myPt.x = myRect.left;
    myPt.y = myRect.top;

    parent = GetParent(handle);
    if (parent && IsChild(parent, handle)) ScreenToClient(parent, &myPt);

    if(items == 1) {
        EXTEND(SP, 1);
        XST_mIV(0, myPt.x);
        XSRETURN(1);
    } else {
        if(SetWindowPos(
            handle, (HWND) NULL,
            (int) SvIV(ST(1)), (int) myPt.y,
            0, 0,
            SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_DEFERERASE
        )) {
            XSRETURN_YES;
        } else {
            XSRETURN_NO;
        }
    }


    ###########################################################################
    # (@)METHOD:Top([TOP])
    # Gets or sets the top co-ordinate of an object, relative to the object's
    # parent if it has one, or absolute screen co-ordinate if it doesn't.
    #
    # See also AbsTop()
    # See also Move()
void
Top(handle,...)
    HWND handle
PREINIT:
    RECT myRect;
    HWND parent;
    POINT myPt;
PPCODE:
    if(items > 2) {
        croak("Usage: Top(handle, [value]);\n");
    }
    if(!GetWindowRect(handle, &myRect)) XSRETURN_NO;
    myPt.x = myRect.left;
    myPt.y = myRect.top;

    parent = GetParent(handle);
    if (parent && IsChild(parent, handle)) ScreenToClient(parent, &myPt);

    if(items == 1) {
        EXTEND(SP, 1);
        XST_mIV(0, myPt.y);
        XSRETURN(1);
    } else {
        if(SetWindowPos(
            handle, (HWND) NULL,
            (int) myPt.x, (int) SvIV(ST(1)),
            0, 0,
            SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_DEFERERASE
        )) {
            XSRETURN_YES;
        } else {
            XSRETURN_NO;
        }
    }

    ###########################################################################
    # (@)METHOD:AbsLeft([LEFT])
    # Gets or sets the absolute left co-ordinate of an object.
    #
    # See also Left()
    # See also Move()
void
AbsLeft(handle,...)
    HWND handle
PREINIT:
    RECT myRect;
PPCODE:
    if(!GetWindowRect(handle, &myRect)) {
        XSRETURN_UNDEF;
    } else {
        if(items > 1) {
            if(SetWindowPos(
                handle, (HWND) NULL,
                (int) SvIV(ST(1)), (int) myRect.top, (int) myRect.right, (int) myRect.bottom,
                SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_DEFERERASE
            )) {
                XSRETURN_YES;
            }
            else {
                XSRETURN_NO;
            }
        }
        else {
            EXTEND(SP, 1);
            XST_mIV(0, myRect.left);
            XSRETURN(1);
        }
    }

    ###########################################################################
    # (@)METHOD:AbsTop([TOP])
    # Gets or sets the absolute top co-ordinate of an object.
    #
    # See also Top()
    # See also Move()
void
AbsTop(handle,...)
    HWND handle
PREINIT:
    RECT myRect;
PPCODE:
    if(!GetWindowRect(handle, &myRect)) {
        XSRETURN_UNDEF;
    } else {
        if(items > 1) {
            if(SetWindowPos(
                handle, (HWND) NULL,
                (int) myRect.left, (int) SvIV(ST(1)), (int) myRect.right, (int) myRect.bottom,
                SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_DEFERERASE
            )) {
                XSRETURN_YES;
            }
            else {
                XSRETURN_NO;
            }
        }
        else {
            EXTEND(SP, 1);
            XST_mIV(0, myRect.top);
            XSRETURN(1);
        }
    }

    ###########################################################################
    # (@)METHOD:ScreenToClient(X, Y)
    # Converts screen co-ordinates to client-area co-ordinates.
void
ScreenToClient(handle,x,y)
    HWND handle
    int x
    int y
PREINIT:
    POINT myPt;
PPCODE:
    myPt.x = x;
    myPt.y = y;
    ScreenToClient(handle, &myPt);
    EXTEND(SP, 2);
    XST_mIV(0, myPt.x);
    XST_mIV(1, myPt.y);
    XSRETURN(2);


    ###########################################################################
    # (@)METHOD:ClientToScreen(X, Y)
    # Converts client-area co-ordinates to screen co-ordinates.
void
ClientToScreen(handle,x,y)
    HWND handle
    int x
    int y
PREINIT:
    POINT myPt;
PPCODE:
    myPt.x = x;
    myPt.y = y;
    ClientToScreen(handle, &myPt);
    EXTEND(SP, 2);
    XST_mIV(0, myPt.x);
    XST_mIV(1, myPt.y);
    XSRETURN(2);
    
    ###########################################################################
    # (@)METHOD:ScaleWidth()
    # Returns the windows client area width.
DWORD
ScaleWidth(handle)
    HWND handle
PREINIT:
    RECT myRect;
CODE:
    if(GetClientRect(handle, &myRect)) {
        RETVAL = myRect.right;
    } else {
        RETVAL = 0;
    }
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:ScaleHeight()
    # Returns the windows client area height.
DWORD
ScaleHeight(handle)
    HWND handle
PREINIT:
    RECT myRect;
CODE:
    if(GetClientRect(handle, &myRect)) {
        RETVAL = myRect.bottom;
    } else {
        RETVAL = 0;
    }
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:BringWindowToTop()
    # Brings a window to the foreground (on top of other windows). This does
    # not make the window "always on top". If the window is already on top,
    # it is activated. If the window is a child window, the top-level
    # parent window associated with the child window is activated.
BOOL
BringWindowToTop(handle)
    HWND handle
CODE:
    RETVAL = BringWindowToTop(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:ArrangeIconicWindows()
    # Arranges all the minimized child windows of the specified parent window.
UINT
ArrangeIconicWindows(handle)
    HWND handle
CODE:
    RETVAL = ArrangeIconicWindows(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:GetDesktopWindow()
    # Returns the handle of the desktop window.
HWND
GetDesktopWindow(...)
CODE:
   RETVAL = GetDesktopWindow();
OUTPUT:
   RETVAL


    ###########################################################################
    # (@)METHOD:GetForegroundWindow()
    # Returns the handle of the foreground window.
HWND
GetForegroundWindow(...)
CODE:
   RETVAL = GetForegroundWindow();
OUTPUT:
   RETVAL


    ###########################################################################
    # (@)METHOD:SetForegroundWindow()
    # Brings the window to the foreground.
BOOL
SetForegroundWindow(handle)
    HWND handle
CODE:
    RETVAL = SetForegroundWindow(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:IsZoomed()
    # Returns TRUE if the window is maximized, FALSE otherwise.
BOOL
IsZoomed(handle)
    HWND handle
CODE:
    RETVAL = IsZoomed(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:IsIconic()
    # Returns TRUE if the window is minimized, FALSE otherwise.
BOOL
IsIconic(handle)
    HWND handle
CODE:
    RETVAL = IsIconic(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:IsWindow()
    # Returns TRUE if the window is a window, FALSE otherwise.
BOOL
IsWindow(handle)
    HWND handle
CODE:
    RETVAL = IsWindow(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:IsVisible()
    # Returns TRUE if the window is visible, FALSE otherwise.
BOOL
IsVisible(handle)
    HWND handle
CODE:
    RETVAL = IsWindowVisible(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:IsEnabled()
    # Returns TRUE if the window is enabled, FALSE otherwise.
BOOL
IsEnabled(handle)
    HWND handle
CODE:
    RETVAL = IsWindowEnabled(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:Enable([FLAG])
    # Enables (or disables) a window or control. Controls do not accept input
    # when they are disabled.
    #
    # B<FLAG> should be 0 to disable the control (the same as calling Disable() on
    # a control), or 1 to enable it.
    #
    # See also Disable()
BOOL
Enable(handle,flag=TRUE)
    HWND handle
    BOOL flag
CODE:
    RETVAL = EnableWindow(handle, flag);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:Disable()
    # Disables a window or control. Disabled widgets cannot be interacted with,
    # and often change appearance to indicate that they are disabled. This is
    # the same as Enable(0).
    #
    # See also Enable()
BOOL
Disable(handle)
    HWND handle
CODE:
    RETVAL = EnableWindow(handle, FALSE);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:OpenIcon()
    # (@)METHOD:Restore()
    # Restores a minimized window.
BOOL
OpenIcon(handle)
    HWND handle
ALIAS:
    Win32::GUI::Restore = 1
CODE:
    RETVAL = OpenIcon(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:CloseWindow()
    # (@)METHOD:Minimize()
    # Minimizes a window.
BOOL
CloseWindow(handle)
    HWND handle
ALIAS:
    Win32::GUI::Minimize = 1
CODE:
    RETVAL = CloseWindow(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:WindowFromPoint(X, Y)
    # Returns the handle of the window at the specified screen position.
HWND
WindowFromPoint(x,y)
    LONG x
    LONG y
PREINIT:
    POINT myPoint;
CODE:
    myPoint.x = x;
    myPoint.y = y;
    RETVAL = WindowFromPoint(myPoint);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:GetTopWindow()
    # Returns the handle of the foreground window.
HWND
GetTopWindow(handle)
    HWND handle
CODE:
    RETVAL = GetTopWindow(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:GetActiveWindow()
    # Returns the handle of the active window.
HWND
GetActiveWindow(...)
CODE:
    RETVAL = GetActiveWindow();
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:SetActiveWindow()
    # Activates a window.
    # Returns the handle of the previously active window or 0.
HWND
SetActiveWindow(handle)
    HWND handle
CODE:
    RETVAL = SetActiveWindow(handle);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:GetDlgItem(ID)
    # Returns the handle of a control in the dialog box given its B<ID>.
HWND
GetDlgItem(handle,identifier)
    HWND handle
    int identifier
CODE:
    RETVAL = GetDlgItem(handle, identifier);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:GetFocus()
    # Returns the handle of the window that has the keyboard focus.
HWND
GetFocus(...)
CODE:
    RETVAL = GetFocus();
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:SetFocus()
    # Set focus to a window.
HWND
SetFocus(handle)
    HWND handle
CODE:
    RETVAL = SetFocus(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:SetCapture()
    # Assigns the mouse capture to a window.
HWND
SetCapture(handle)
    HWND handle
CODE:
    RETVAL = SetCapture(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:GetCapture()
    # Returns the handle of the window that has the captured the mouse. 
    # If no window has captured the mouse zero is returned.
HWND
GetCapture()
CODE:
    RETVAL = GetCapture();
OUTPUT:
    RETVAL
    
    ###########################################################################
    # (@)METHOD:ReleaseCapture()
    # Releases the mouse capture.
BOOL
ReleaseCapture(...)
CODE:
    RETVAL = ReleaseCapture();
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:ShellExecute(window,operation,file,parameters,directory,showcmd)
    #
    # Performs an operation on a file.
    #
    # B<Operation>. A string that specifies the action to be performed. The set of available action verbs depends
    # on the particular file or folder. Generally, the actions available from an object's shortcut menu are
    # available verbs. 
    #   edit     - Launches an editor and opens the document for editing. If File is not a document file,
    #              the function will fail.
    #   explore  - Explores the folder specified by File.
    #   find     - Initiates a search starting from the specified directory.
    #   open     - Opens the file specified by the File parameter. The file can be an executable file,
    #              a document file, or a folder.
    #   print    - Prints the document file specified by lpFile. If lpFile is not a document file,
    #              the function will fail.
    #   ""(NULL) - For systems prior to Microsoft Windows 2000, the default verb is used if it is valid
    #              and available in the registry. If not, the "open" verb is used. For Windows 2000 and
    #              later systems, the default verb is used if available. If not, the "open" verb is used.
    #              If neither verb is available, the system uses the first verb listed in the registry.
    #
    # B<File>. A string that specifies the file or object on which to execute the specified verb. To specify
    # a Shell namespace object, pass the fully qualified parse name. Note that not all verbs are supported on
    # all objects. For example, not all document types support the "print" verb.
    #
    # B<Parameters>. If the File parameter specifies an executable file, Parameters is a string that specifies
    # the parameters to be passed to the application. The format of this string is determined by the verb that
    # is to be invoked. If File specifies a document file, Parameters should be NULL.
    #
    # B<Directory>. A string that specifies the default directory.
    #
    # B<ShowCmd>. Flags that speciow an application is to be displayed when it is opened. If File specifies a
    # document file, the flag is simply passed to the associated application. It is up to the application to
    # decide how to handle it.   
    #
    #   0  SW_HIDE            Hides the window and activates another window.
    #   3  SW_MAXIMIZE        Maximizes the specified window.
    #   6  SW_MINIMIZE        Minimizes the specified window and activates the next top-level window in the z-order.
    #   9  SW_RESTORE         Activates and displays the window. If the window is minimized or maximized,
    #                         Windows restores it to its original size and position. An application should specify
    #                         this flag when restoring a minimized window.
    #   5  SW_SHOW            Activates the window and displays it in its current size and position.
    #   10 SW_SHOWDEFAULT     Sets the show state based on the SW_ flag specified in the STARTUPINFO structure
    #                         passed to the CreateProcess function by the program that started the application.
    #                         An application should call ShowWindow with this flag to set the initial show state of
    #                         its main window.
    #   2  SW_SHOWMINIMIZED   Activates the window and displays it as a minimized window.
    #   7  SW_SHOWMINNOACTIVE Displays the window as a minimized window. The active window remains active.
    #   8  SW_SHOWNA          Displays the window in its current state. The active window remains active.
    #   4  SW_SHOWNOACTIVATE  Displays a window in its most recent size and position. The active window remains active.
    #   1  SW_SHOWNORMAL      Activates and displays a window. If the window is minimized or maximized, Windows
    #                         restores it to its original size and position. An application should specify this flag
    #                         when displaying the window for the first time.
    #
    # Returns a value greater than 32 if successful, or an error value that is less than or equal to 32 otherwise.
    # The following table lists the error values. 
    #
    #   0  The operating system is out of memory or resources. 
    #   3  ERROR_PATH_NOT_FOUND   The specified path was not found. 
    #   11 ERROR_BAD_FORMAT       The .exe file is invalid (non-Microsoft Win32 .exe or error in .exe image). 
    #   5  SE_ERR_ACCESSDENIED    The operating system denied access to the specified file. 
    #   27 SE_ERR_ASSOCINCOMPLETE The file name association is incomplete or invalid. 
    #   30 SE_ERR_DDEBUSY         The Dynamic Data Exchange (DDE) transaction could not be completed because
    #                             other DDE transactions were being processed. 
    #   29 SE_ERR_DDEFAIL         The DDE transaction failed. 
    #   28 SE_ERR_DDETIMEOUT      The DDE transaction could not be completed because the request timed out. 
    #   32 SE_ERR_DLLNOTFOUND     The specified dynamic-link library (DLL) was not found. 
    #   2  SE_ERR_FNF             The specified file was not found. 
    #   31 SE_ERR_NOASSOC         There is no application associated with the given file name extension. This
    #                             error will also be returned if you attempt to print a file that is not printable. 
    #   8  SE_ERR_OOM             There was not enough memory to complete the operation. 
    #   3  SE_ERR_PNF             The specified path was not found. 
    #   26 SE_ERR_SHARE           A sharing violation occurred. 
    #
    # Examples:
    #
    # Open a web page in the default browser
    #   my $exitval = $win->ShellExecute('open','http://www.perl.org','','',1);
    #
    # Open a text file in nodepad
    #   my $exitval = $win->ShellExecute('open','notepad.exe','readme.txt','',1) ;
int
ShellExecute(window,operation,file,parameters,directory,showcmd)
    HWND window
    LPCTSTR operation
    LPCTSTR file
    LPCTSTR parameters
    LPCTSTR directory
    int showcmd
CODE:
    RETVAL = (int) ShellExecute(window,operation,file,parameters,directory,showcmd);
OUTPUT:
    RETVAL
    
    ###########################################################################
    # (@)METHOD:GetWindowThreadProcessId()
    # Returns a two elements array containing the thread and the process
    # identifier for the specified window.
void
GetWindowThreadProcessId(handle)
    HWND handle
PREINIT:
    DWORD tid;
    DWORD pid;
PPCODE:
    tid = GetWindowThreadProcessId(handle, &pid);
    EXTEND(SP, 2);
    XST_mIV(0, tid);
    XST_mIV(1, pid);
    XSRETURN(2);


    ###########################################################################
    # (@)METHOD:AttachThreadInput(FROM, TO, [FLAG])
    # If you have multiple windows running in different threads, this function
    # allows you to attach one thread's input processor to a different thread.
    #
    # For example, you can redirect thread A's input to thread B, and then
    # call SetFocus on an object running in thread B to set the keyboard focus
    # to that object. You would not normally be able to do this.
    #
    # B<FROM> and B<TO> should be thread IDs. B<FLAG> should be non-zero to attach the
    # threads (the default operation if B<FLAG> is not specified), or zero to
    # detach the threads.
    #
    # see also GetWindowThreadProcessId()
BOOL
AttachThreadInput(from,to,flag=TRUE)
    DWORD from
    DWORD to
    BOOL flag
CODE:
    RETVAL = AttachThreadInput(from, to, flag);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:GetTextExtentPoint32(STRING, [FONT])
    # Returns a two elements array containing the x and y size of the
    # specified B<STRING> in the window (eventually with the speficied B<FONT>), or
    # undef on errors.
void
GetTextExtentPoint32(handle,string,font=NULL)
    HWND handle
    char * string
    HFONT font
PREINIT:
    STRLEN cbString;
    char *szString;
    HDC hdc;
    SIZE mySize;
PPCODE:
    szString = SvPV(ST(1), cbString);
    hdc = GetDC(handle);
#ifdef PERLWIN32GUI_DEBUG
    printf("XS(GetTextExtentPoint32).font=0x%x\n", font);
    printf("XS(GetTextExtentPoint32).string='%s'\n", string);
#endif
    if(font) SelectObject(hdc, (HGDIOBJ) font);
    if(GetTextExtentPoint32(hdc, szString, (int)cbString, &mySize)) {
        EXTEND(SP, 2);
        XST_mIV(0, mySize.cx);
        XST_mIV(1, mySize.cy);
        ReleaseDC(handle, hdc);
        XSRETURN(2);
    } else {
        ReleaseDC(handle, hdc);
        XSRETURN_UNDEF;
    }


    ###########################################################################
    # (@)METHOD:TrackPopupMenu(MENU [, X, Y [, LEFT, TOP, RIGHT, BOTTOM] [, FLAGS [, CODEREF]]])
    # Displays the menu B<MENU> at the specified co-ordinates (X,Y) and tracks the
    # selection of items on the menu. X and Y are absolute screen co-ordinates.
    #
    # If X and Y are not provided, uses the current cursor position.
    #
    # If LEFT, TOP, RIGHT and BOTTOM are provided they describe a rectangle in absolute screen
    # co-ordinates over which the menu will not be drawn (the excluded rectangle).
    #
    # The following flags can be set (combine flags with bitwise OR (|) )
    #   0x0000 TPM_LEFTBUTTON      Menu items can only be selected with left mouse button
    #   0x0002 TPM_RIGHTBUTTON     Menu items can be selected with either left or right mouse button
    #   0x0000 TPM_LEFTALIGN       Menu is aligned to the left of the given X co-ordinate
    #   0x0004 TPM_CENTERALIGN     Menu is centered on the given X co-ordinate
    #   0x0008 TPM_RIGHTALIGN      Menu is aligned to the right of the given X co-ordinate
    #   0x0000 TPM_TOPALIGN        Menu is aligned above the given Y co-ordinate
    #   0x0010 TPM_VCENTERALIGN    Menu is centered on the given Y co-ordinate
    #   0x0020 TPM_BOTTOMALIGN     Menu is aligned below the given Y co-ordinate
    #   0x0100 TPM_RETURNCMD       TrackPopupMenu returns the selected menu item identifier in the return value
    #   0x0400 TPM_HORPOSANIMATION Menu will be animated from left to right (ignored if menu fading is on)
    #   0x0800 TPM_HORNEGANIMATION Menu will be animated from right to left (ignored if menu fading is on)
    #   0x1000 TPM_VERPOSANIMATION Menu will be animated from top to bottom (ignored if menu fading is on)
    #   0x2000 TPM_VERNEGANIMATION Menu will be animated from bottom to top (ignored if menu fading is on)
    #   0x4000 TPM_NOANIMATION     Menu will not be animated and will not "fade" in and out even if menu
    #                              fading is enabled
    #   0x0001 TPM_RECURSE         Allows you to display a menu when another menu is already displayed.
    #                              This is intended to support context menus within a menu. (Windows 2000/XP only)
    #
    # The default flags are C<TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON>
    #
    # If an excluded rectangle is spefified then the following flags may also be used, and TPM_VERTICAL is added to
    # the default flags:
    #   0x0000 TPM_HORIZONTAL      If the menu cannot be shown at the specified location without overlapping
    #                              the excluded rectangle, the system tries to accommodate the requested
    #                              horizontal alignment before the requested vertical alignment.
    #   0x0040 TPM_VERTICAL        If the menu cannot be shown at the specified location without overlapping
    #                              the excluded rectangle, the system tries to accommodate the requested
    #                              vertical alignment before the requested horizontal alignment.
    #
    # If you specify C<TPM_RETURNCMD>, then the menu item ID of the selected item is returned. If an error
    # occurs or the user does not select an item, zero is returned.
    # If you do not specify C<TPM_RETURNCMD>, the return value will be nonzero on success or zero on failure.
    #
    # If B<CODEREF> is provided, then it is a code reference to a callback procedure that will be called for windows
    # events that occur while the menu is displayed (normally such events are not available, as TrackPopupMenu has
    # its own internal event loop).  The callback will recieve a reference to the Win32::GUI object used to call
    # TrackPopupMenu on, and the message code, wParam and lParam of the event that occured. The callback should
    # return nothing or 1 to allow the event to be processed normally, or 0 to prevent the event being passed to the
    # default event handler.  See MSDN documentation for SetWindowsHookEx with idHook set to WH_MSGFILTER for
    # the full gore.
    #
    # The callback prototype is:
    #
    #     sub callback {
    #         my ($self, $message, $wParam, $lParam) = @_;
    #
    #         # Process messages you are interested in
    #         
    #         return;
    #     }
    #

BOOL
TrackPopupMenu(handle,hmenu, ... )
    HWND handle
    HMENU hmenu
PREINIT:
    int x = 0;
    int y = 0;
    UINT flags = TPM_LEFTALIGN|TPM_TOPALIGN|TPM_LEFTBUTTON;
    LPTPMPARAMS lptpm = (LPTPMPARAMS) NULL;
    TPMPARAMS tpm;
    SV* coderef = (SV*) NULL;
    HHOOK hhook = NULL;
    LPPERLWIN32GUI_USERDATA perlud;
    AV* newarray;
    POINT pt;
CODE:

    switch (items) {
      case 10: coderef              = ST(9);
      case  9: flags                = SvIV(ST(8));
      case  8: tpm.rcExclude.bottom = SvIV(ST(7));
               tpm.rcExclude.right  = SvIV(ST(6));
               tpm.rcExclude.top    = SvIV(ST(5));
               tpm.rcExclude.left   = SvIV(ST(4));
               tpm.cbSize           = sizeof(TPMPARAMS);
               y                    = SvIV(ST(3));
               x                    = SvIV(ST(2));
           break;

      case  6: coderef              = ST(5);
      case  5: flags                = SvIV(ST(4));
      case  4: y                    = SvIV(ST(3));
               x                    = SvIV(ST(2));
           break;

      case  2: if(GetCursorPos(&pt)) {
               y                  = pt.y;
               x                  = pt.x;
             }
          break;

      default:
        CROAK("Usage: TrackPopupMenu(handle, menu, [x, y, [left, top, right, bottom], [flag, [coderef]]])\n");
      break;
    }

    // if given a coderef, check that it actually is one
    if(coderef != NULL && !(SvOK(coderef) && SvROK(coderef) && SvTYPE(SvRV(coderef)) == SVt_PVCV)) {
      W32G_WARN("TrackPopupMenu argument 'coderef' must be a code reference - callback not applied");
      coderef = NULL;  // don't set up the hook
    }

    // if we have a coderef, then store it temporarily in the hooks array so that we can access
    // it in the callback, and if successful, register the callback hook handler
    if(coderef != NULL) {

      perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(handle, GWL_USERDATA);
      if(ValidUserData(perlud)) {
        if(perlud->avHooks == NULL) {
          perlud->avHooks = newAV();
        }

        // Check if there is already a handler for this message (there shouldn't be)
        if(av_fetch(perlud->avHooks, WM_TRACKPOPUP_MSGHOOK, 0) == NULL) {
          // No array reference for this msg yet, so make one and insert our
          // handler ref:
          newarray = newAV();
          av_push(newarray, coderef);
          SvREFCNT_inc(coderef);  // needed so that the ref count remains the same after we free
          if(av_store(perlud->avHooks, WM_TRACKPOPUP_MSGHOOK, newRV_noinc((SV*) newarray)) == NULL) {
            // Failed to store new array
            SvREFCNT_dec((SV*) newarray);
            W32G_WARN("TrackPopupMenu failed to store 'coderef' - callback not applied");
            coderef = NULL;  // don't set up the hook
          }
        }
        else {
          // there's an existing hook for the message value we've chosen to use.
          // this means someone called hook() with the message code 0xBFFF !!
          W32G_WARN("TrackPopupMenu found an existing 'coderef' - callback not applied");
          coderef = NULL;  // don't set up the hook
        }
      }
      else {
        coderef = NULL;  // don't set up the hook
      }

      // only set up the hook if we installed the callback
      if(coderef != NULL) {
        hhook = SetWindowsHookEx(WH_MSGFILTER, (HOOKPROC)WindowsHookMsgProc, (HINSTANCE)NULL, GetWindowThreadProcessId(handle, (LPDWORD)NULL));
      }
    }

    // Display the menu
    SetForegroundWindow(handle);
    RETVAL = TrackPopupMenuEx(hmenu, flags, x, y, handle, lptpm);
    SetForegroundWindow(handle);

    if(coderef != NULL) {
      if(hhook != NULL) {
        UnhookWindowsHookEx(hhook); // release the windows hook
      }
      // remove the temporary value stored in the hooks array
      av_delete(perlud->avHooks, WM_TRACKPOPUP_MSGHOOK, G_DISCARD);
    }
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)INTERNAL:SetTimer(HANDLE, ID, ELAPSE)
UINT
SetTimer(handle,id,elapse)
    HWND handle
    UINT id
    UINT elapse
CODE:
    RETVAL = SetTimer(handle, id, elapse, NULL);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)INTERNAL:KillTimer(HANDLE, ID)
UINT
KillTimer(handle,id)
    HWND handle
    UINT id
CODE:
    RETVAL = KillTimer(handle, id);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:GetEffectiveClientRect(HANDLE, @CONTROLS)
    # Returns the left, top, right and bottom co-ordinates of a rectangle that
    # can accommodate all the controls specified. The elements of B<@CONTROLS>
    # should be control identifiers.
void
GetEffectiveClientRect(handle,...)
    HWND handle
PREINIT:
    LPINT controls;
    int i, c;
    RECT r;
PPCODE:
    c = 0;
    controls = (LPINT) safemalloc(sizeof(INT)*items*2);
    for(i=1;i<items;i++) {
        controls[c++] = 1;
        controls[c++] = (INT) SvIV(ST(i));
    }
    controls[c++] = 0;
    controls[c++] = 0;
    GetEffectiveClientRect(handle, &r, controls);
    EXTEND(SP, 4);
    XST_mIV(0, r.left);
    XST_mIV(1, r.top);
    XST_mIV(2, r.right);
    XST_mIV(3, r.bottom);
    XSRETURN(4);


    ###########################################################################
    # (@)METHOD:DialogUI(HANDLE, [FLAG])
    # Gets or sets whether a window accepts dialog-box style input (tab between
    # fields, accelerator keys etc). B<FLAG> should be 1 to enable this functionality,
    # or 0 to disable it.
    #
    # See also new Win32::GUI::DialogBox()
void
DialogUI(handle,...)
    HWND handle
PREINIT:
    LPPERLWIN32GUI_USERDATA perlud;
PPCODE:
    if(items > 2) {
        CROAK("Usage: DialogUI(handle, [value]);\n");
    }

    perlud = (LPPERLWIN32GUI_USERDATA) GetWindowLong(handle, GWL_USERDATA);
    if( ! ValidUserData(perlud) ) {
        XSRETURN_UNDEF;
    } else {
        if(items == 1) {
            XSRETURN_IV( (long) perlud->dwPlStyle & PERLWIN32GUI_DIALOGUI );
        } else {
            SwitchBit(perlud->dwPlStyle, PERLWIN32GUI_DIALOGUI, SvIV(ST(1)));
            XSRETURN_IV( (long) perlud->dwPlStyle & PERLWIN32GUI_DIALOGUI );
        }
    }


    ###########################################################################
    # (@)METHOD:TrackMouse([TIMEOUT=HOVER_DEFAULT, EVENTS=TME_HOVER|TME_LEAVE])
    # Causes the window to receive messages when the mouse pointer leaves a
    # window or hovers over a window for a specified amount of time (B<TIMEOUT>, in
    # milliseconds).
    #
    # B<EVENTS> can be set to one or more of the following values (ORed together):
    #
    #  0x0001 TME_HOVER     Makes the window receive WM_MOUSEHOVER messages when the mouse hovers over it.
    #  0x0002 TME_LEAVE     Makes the window receive a WM_MOUSELEAVE message when the mouse leaves it.
    #  0x0010 TME_NONCLIENT Specifies that the non-client area should be included when tracking the mouse.
    #                       The window will receive WM_NCMOUSEHOVER and WM_NCMOUSELEAVE messages depending
    #                       on whether TME_HOVER and/or TME_LEAVE are set.
    #
    # See also UntrackMouse()
BOOL
TrackMouse(handle, timeout=HOVER_DEFAULT, events=TME_HOVER|TME_LEAVE)
    HWND handle
    DWORD timeout
    DWORD events
PREINIT:
    TRACKMOUSEEVENT tme;
CODE:
    tme.cbSize = sizeof(TRACKMOUSEEVENT);
    tme.hwndTrack = handle;
    tme.dwFlags = events;
    tme.dwHoverTime = timeout;
    RETVAL = _TrackMouseEvent( &tme );
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:UntrackMouse()
    # Disables the tracking of mouse hover or leave events for a window.
    #
    # See also TrackMouse()
BOOL
UntrackMouse(handle)
    HWND handle
PREINIT:
    TRACKMOUSEEVENT tme;
CODE:
    tme.cbSize = sizeof(TRACKMOUSEEVENT);
    tme.hwndTrack = handle;
    tme.dwFlags = TME_QUERY;
    tme.dwHoverTime = 0;
    if(_TrackMouseEvent( &tme )) {
        tme.dwFlags = tme.dwFlags | TME_CANCEL;
        RETVAL = _TrackMouseEvent( &tme );
    } else {
        RETVAL = FALSE;
    }
OUTPUT:
    RETVAL

    # TODO: GetIconInfo

    ###########################################################################
    # DC-related functions (2D window graphic...)
    ###########################################################################


    ###########################################################################
    # (@)METHOD:PlayEnhMetaFile(FILENAME)
    # Displays the picture stored in the specified enhanced-format metafile.
    # This function use current window device context (-DC).
int
PlayEnhMetaFile(handle,filename)
    HWND handle
    LPCTSTR filename
PREINIT:
    HV* self;
    HDC hdc;
    SV** tmp;
    HENHMETAFILE hmeta;
    RECT rect;
CODE:
    self = (HV*) SvRV(ST(0));
    tmp = hv_fetch_mg(NOTXSCALL self, "-DC", 3, 0);
    if(tmp == NULL) {
        RETVAL = -1;
    } else {
        hdc = INT2PTR(HDC,SvIV(*tmp));
        if(hmeta = GetEnhMetaFile(filename)) {
            GetClientRect(handle, &rect);
            RETVAL = PlayEnhMetaFile(hdc, hmeta, &rect);
            DeleteEnhMetaFile(hmeta);
        } else {
#ifdef PERLWIN32GUI_DEBUG
                printf("XS(PlayEnhMetaFile): GetEnhMetaFile failed, error = %d\n", GetLastError());
#endif
            RETVAL = 0;
        }
    }
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:PlayWinMetaFile(FILENAME)
    # Displays the picture stored in the specified enhanced-format metafile. 
int
PlayWinMetaFile(handle,filename)
    HWND handle
    LPCTSTR filename
PREINIT:
    HDC hdc;
    HMETAFILE hwinmeta;
    HENHMETAFILE hmeta;
    RECT rect;
    UINT size;
    LPVOID data;
CODE:
#ifdef PERLWIN32GUI_DEBUG
    printf("XS(PlayWinMetaFile): filename = '%s'\n", filename);
#endif
    SetLastError(0);
    hwinmeta = GetMetaFile(filename);
#ifdef PERLWIN32GUI_DEBUG
    printf("XS(PlayWinMetaFile): hwinmeta = 0x%x\n", hwinmeta);
    printf("XS(PlayWinMetaFile): GetLastError = %ld\n", GetLastError());
#endif
    size = GetMetaFileBitsEx(hwinmeta, 0, NULL);
#ifdef PERLWIN32GUI_DEBUG
    printf("XS(PlayWinMetaFile): size = %d\n", size);
#endif
    data = (LPVOID) safemalloc(size);
    GetMetaFileBitsEx(hwinmeta, size, data);
    hmeta = SetWinMetaFileBits(size, (CONST BYTE *) data, NULL, NULL);
#ifdef PERLWIN32GUI_DEBUG
    printf("XS(PlayWinMetaFile): hmeta = 0x%x\n", hmeta);
#endif
    hdc = GetDC(handle);
    GetClientRect(handle, &rect);
    SetLastError(0);
    RETVAL = PlayEnhMetaFile(hdc, hmeta, &rect);
#ifdef PERLWIN32GUI_DEBUG
    printf("XS(PlayWinMetaFile): GetLastError after PlayEnhMetaFile = %d\n", GetLastError());
#endif
    DeleteEnhMetaFile(hmeta);
    ReleaseDC(handle, hdc);
    safefree(data);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:CreateEnhMetaFile(FILENAME, [DESCRIPTION])
    # Creates a device context for an enhanced-format metafile.
HDC
CreateEnhMetaFile(handle, filename, description=NULL)
    HWND handle
    LPCTSTR filename
    LPCTSTR description
PREINIT:
    HV* self;
    HDC hdc;
    SV** tmp;
    RECT rect;
    int iWidthMM, iHeightMM, iWidthPels, iHeightPels;
CODE:
    self = (HV*) SvRV(ST(0));
    tmp = hv_fetch_mg(NOTXSCALL self, "-DC", 3, 0);
    if(tmp == NULL) {
        RETVAL = 0;
    } else {
        hdc = INT2PTR(HDC,SvIV(*tmp));
        iWidthMM = GetDeviceCaps(hdc, HORZSIZE);
        iHeightMM = GetDeviceCaps(hdc, VERTSIZE);
        iWidthPels = GetDeviceCaps(hdc, HORZRES);
        iHeightPels = GetDeviceCaps(hdc, VERTRES);
        GetClientRect(handle, &rect);
        rect.left = (rect.left * iWidthMM * 100)/iWidthPels;
        rect.top = (rect.top * iHeightMM * 100)/iHeightPels;
        rect.right = (rect.right * iWidthMM * 100)/iWidthPels;
        rect.bottom = (rect.bottom * iHeightMM * 100)/iHeightPels;
        RETVAL = CreateEnhMetaFile(hdc, filename, &rect, description);
    }
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:CloseEnhMetaFile(HANDLE)
    # Closes an enhanced-metafile device context and returns a handle that identifies an enhanced-format metafile. 
HENHMETAFILE
CloseEnhMetaFile(hdc)
    HDC hdc
CODE:
    RETVAL = CloseEnhMetaFile(hdc);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:DeleteEnhMetaFile(HANDLE)
    # Deletes an enhanced-format metafile or an enhanced-format metafile handle. 
BOOL
DeleteEnhMetaFile(hmeta)
    HENHMETAFILE hmeta
CODE:
    RETVAL = DeleteEnhMetaFile(hmeta);
OUTPUT:
    RETVAL


    #HDC GetOrInitDC(SV* obj) {
    #    CPerl *pPerl;
    #    HDC hdc;
    #    HWND hwnd;
    #    SV** obj_dc;
    #    SV** obj_hwnd;
    #
    #    pPerl = theperl;
    #
    #    obj_dc = hv_fetch((HV*)SvRV(obj), "dc", 2, 0);
    #    if(obj_dc != NULL) {
    #        __DEBUG("!XS(GetOrInitDC): obj{dc} = %ld\n", SvIV(*obj_dc));
    #        return (HDC) SvIV(*obj_dc);
    #    } else {
    #        obj_hwnd = hv_fetch((HV*)SvRV(obj), "handle", 6, 0);
    #        hwnd = (HWND) SvIV(*obj_hwnd);
    #        hdc = GetDC(hwnd);
    #        __DEBUG("!XS(GetOrInitDC): GetDC = %ld\n", hdc);
    #        hv_store((HV*) SvRV(obj), "dc", 2, newSViv((long) hdc), 0);
    #        return hdc;
    #    }
    #}
    #
    #
    #XS(XS_Win32__GUI_DrawText) {
    #
    #    dXSARGS;
    #    if(items < 4 || items > 7) {
    #        CROAK("usage: DrawText($handle, $text, $left, $top, [$width, $height, $format]);\n");
    #    }
    #
    #    HDC hdc = GetOrInitDC(ST(0));
    #    RECT myRect;
    #
    #    strlen cbString;
    #    char *szString = SvPV(ST(1), cbString);
    #
    #    myRect.left   = (LONG) SvIV(ST(2));
    #    myRect.top    = (LONG) SvIV(ST(3));
    #
    #    if(items >4) {
    #        myRect.right  = (LONG) SvIV(ST(4));
    #        myRect.bottom = (LONG) SvIV(ST(5));
    #    } else {
    #        SIZE mySize;
    #        GetTextExtentPoint(hdc, szString, (int)cbString, &mySize);
    #        myRect.right  = myRect.left + (UINT) mySize.cx;
    #        myRect.bottom = myRect.top  + (UINT) mySize.cy;
    #    }
    #
    #    UINT uFormat = DT_LEFT;
    #
    #    if(items == 7) {
    #        uFormat = (UINT) SvIV(ST(6));
    #    }
    #
    #    BOOL result = DrawText(hdc,
    #                           szString,
    #                           cbString,
    #                           &myRect,
    #                           uFormat);
    #    XSRETURN_IV((long) result);
    #}
    #
    #
    #
    #
    #XS(XS_Win32__GUI_ReleaseDC) {
    #
    #    dXSARGS;
    #    if(items != 1) {
    #        CROAK("usage: ReleaseDC($handle);\n");
    #    }
    #
    #    HWND hwnd = (HWND) handle_From(NOTXSCALL ST(0));
    #    HDC hdc = GetOrInitDC(ST(0));
    #
    #    ReleaseDC(hwnd, hdc);
    #    hv_delete((HV*) SvRV(ST(0)), "dc", 2, 0);
    #
    #    XSRETURN_NO;
    #}
    #
    #

long
TextOut(handle, x, y, text)
    HWND handle
    int x
    int y
    char * text
PREINIT:
    HV* self;
    HDC hdc;
    SV** tmp;
    STRLEN textlen;
CODE:
    self = (HV*) SvRV(ST(0));
    tmp = hv_fetch_mg(NOTXSCALL self, "-DC", 3, 0);
    if(tmp == NULL) {
        RETVAL = -1;
    } else {
        hdc = INT2PTR(HDC,SvIV(*tmp));
        textlen = strlen(text);
        RETVAL = (long) TextOut(hdc, x, y, text, textlen);
    }
OUTPUT:
    RETVAL

long
SetTextColor(handle, color)
    HWND handle
    COLORREF color
PREINIT:
    HV* self;
    HDC hdc;
    SV** tmp;
CODE:
    self = (HV*) SvRV(ST(0));
    tmp = hv_fetch_mg(NOTXSCALL self, "-DC", 3, 0);
    if(tmp == NULL) {
        RETVAL = -1;
    } else {
        hdc = INT2PTR(HDC,SvIV(*tmp));
        RETVAL = SetTextColor(hdc, color);
    }
OUTPUT:
    RETVAL

long
GetTextColor(handle)
    HWND handle
PREINIT:
    HV* self;
    HDC hdc;
    SV** tmp;
CODE:
    self = (HV*) SvRV(ST(0));
    tmp = hv_fetch_mg(NOTXSCALL self, "-DC", 3, 0);
    if(tmp == NULL) {
        RETVAL = -1;
    } else {
        hdc = INT2PTR(HDC,SvIV(*tmp));
        RETVAL = GetTextColor(hdc);
    }
OUTPUT:
    RETVAL

long
SetBkMode(handle, mode)
    HWND handle
    int mode
PREINIT:
    HV* self;
    HDC hdc;
    SV** tmp;
CODE:
    self = (HV*) SvRV(ST(0));
    tmp = hv_fetch_mg(NOTXSCALL self, "-DC", 3, 0);
    if(tmp == NULL) {
        RETVAL = -1;
    } else {
        hdc = INT2PTR(HDC,SvIV(*tmp));
        RETVAL = (long) SetBkMode(hdc, mode);
    }
OUTPUT:
    RETVAL

int
GetBkMode(handle)
    HWND handle
PREINIT:
    HV* self;
    HDC hdc;
    SV** tmp;
CODE:
    self = (HV*) SvRV(ST(0));
    tmp = hv_fetch_mg(NOTXSCALL self, "-DC", 3, 0);
    if(tmp == NULL) {
        RETVAL = -1;
    } else {
        hdc = INT2PTR(HDC,SvIV(*tmp));
        RETVAL = GetBkMode(hdc);
    }
OUTPUT:
    RETVAL

long
MoveTo(handle, x, y)
    HWND handle
    int x
    int y
PREINIT:
    HV* self;
    HDC hdc;
    SV** tmp;
CODE:
    self = (HV*) SvRV(ST(0));
    tmp = hv_fetch_mg(NOTXSCALL self, "-DC", 3, 0);
    if(tmp == NULL) {
        RETVAL = -1;
    } else {
        hdc = INT2PTR(HDC,SvIV(*tmp));
        RETVAL = MoveToEx(hdc, x, y, NULL);
    }
OUTPUT:
    RETVAL

long
Circle(handle, x, y, width, height=-1)
    HWND handle
    int x
    int y
    int width
    int height
PREINIT:
    HV* self;
    HDC hdc;
    SV** tmp;
CODE:
    self = (HV*) SvRV(ST(0));
    tmp = hv_fetch_mg(NOTXSCALL self, "-DC", 3, 0);
    if(tmp == NULL) {
        RETVAL = -1;
    } else {
        hdc = INT2PTR(HDC,SvIV(*tmp));
        if(height == -1) {
            width *= 2;
            height = width;
        }
        RETVAL = (long) Arc(hdc, x, y, width-x, height-y, 0, 0, 0, 0);
    }
OUTPUT:
    RETVAL


long
LineTo(handle, x, y)
    HWND handle
    int x
    int y
PREINIT:
    HV* self;
    HDC hdc;
    SV** tmp;
CODE:
    self = (HV*) SvRV(ST(0));
    tmp = hv_fetch_mg(NOTXSCALL self, "-DC", 3, 0);
    if(tmp == NULL) {
        RETVAL = -1;
    } else {
        hdc = INT2PTR(HDC,SvIV(*tmp));
        RETVAL = LineTo(hdc, x, y);
    }
OUTPUT:
    RETVAL

    #}
    #
    #XS(XS_Win32__GUI_DrawEdge) {
    #
    #    dXSARGS;
    #    if(items != 7) {
    #        CROAK("usage: DrawEdge($handle, $left, $top, $width, $height, $edge, $flags);\n");
    #    }
    #
    #    HDC hdc = GetOrInitDC(ST(0));
    #    RECT myRect;
    #    myRect.left   = (LONG) SvIV(ST(1));
    #    myRect.top    = (LONG) SvIV(ST(2));
    #    myRect.right  = (LONG) SvIV(ST(3));
    #    myRect.bottom = (LONG) SvIV(ST(4));
    #
    #    XSRETURN_IV((long) DrawEdge(hdc,
    #                           &myRect,
    #                           (UINT) SvIV(ST(5)),
    #                           (UINT) SvIV(ST(6))));
    #}

void
BeginPaint(...)
PPCODE:
    HV* self;
    HWND hwnd;
    HDC hdc;
    int i;
    PAINTSTRUCT ps;
    char tmprgb[16];
    self = (HV*) SvRV(ST(0));
    hwnd = INT2PTR(HWND,SvIV(*hv_fetch(self, "-handle", 7, 0)));
    if(hwnd) {
        if(hdc = BeginPaint(hwnd, &ps)) {
            hv_store(self, "-DC", 3, newSViv((long) hdc), 0);
            hv_store(self, "-ps.hdc", 7, newSViv((long) ps.hdc), 0);
            hv_store(self, "-ps.fErase", 10, newSViv((long) ps.fErase), 0);
            hv_store(self, "-ps.rcPaint.left", 16, newSViv((long) ps.rcPaint.left), 0);
            hv_store(self, "-ps.rcPaint.top", 15, newSViv((long) ps.rcPaint.top), 0);
            hv_store(self, "-ps.rcPaint.right", 17, newSViv((long) ps.rcPaint.right), 0);
            hv_store(self, "-ps.rcPaint.bottom", 18, newSViv((long) ps.rcPaint.bottom), 0);
            hv_store(self, "-ps.fRestore", 12, newSViv((long) ps.fRestore), 0);
            hv_store(self, "-ps.fIncUpdate", 14, newSViv((long) ps.fIncUpdate), 0);
            for(i=0;i<=31;i++) {
                sprintf(tmprgb, "-ps.rgbReserved%02d", i);
                hv_store(self, tmprgb, 17, newSViv((long) ps.rgbReserved[i]), 0);
            }
            XSRETURN_YES;
        } else {
            XSRETURN_NO;
        }
    } else {
        XSRETURN_NO;
    }

void
EndPaint(...)
PPCODE:
    HV* self;
    HWND hwnd;
    SV** tmp;
    int i;
    PAINTSTRUCT ps;
    char tmprgb[16];
    BOOL result;

    self = (HV*) SvRV(ST(0));
    if(self) {
        tmp = hv_fetch(self, "-handle", 7, 0);
        if(tmp == NULL) XSRETURN_NO;
        hwnd = INT2PTR(HWND,SvIV(*tmp));
        tmp = hv_fetch(self, "-ps.hdc", 7, 0);
        if(tmp == NULL) XSRETURN_NO;
        ps.hdc = INT2PTR(HDC,SvIV(*tmp));
        tmp = hv_fetch(self, "-ps.fErase", 10, 0);
        if(tmp == NULL) XSRETURN_NO;
        ps.fErase = (BOOL) SvIV(*tmp);
        tmp = hv_fetch(self, "-ps.rcPaint.left", 16, 0);
        if(tmp == NULL) XSRETURN_NO;
        ps.rcPaint.left = (LONG) SvIV(*tmp);
        tmp = hv_fetch(self, "-ps.rcPaint.top", 15, 0);
        if(tmp == NULL) XSRETURN_NO;
        ps.rcPaint.top = (LONG) SvIV(*tmp);
        tmp = hv_fetch(self, "-ps.rcPaint.right", 17, 0);
        if(tmp == NULL) XSRETURN_NO;
        ps.rcPaint.right = (LONG) SvIV(*tmp);
        tmp = hv_fetch(self, "-ps.rcPaint.bottom", 18, 0);
        if(tmp == NULL) XSRETURN_NO;
        ps.rcPaint.bottom = (LONG) SvIV(*tmp);
        tmp = hv_fetch(self, "-ps.fRestore", 12, 0);
        if(tmp == NULL) XSRETURN_NO;
        ps.fRestore = (BOOL) SvIV(*tmp);
        tmp = hv_fetch(self, "-ps.fIncUpdate", 14, 0);
        if(tmp == NULL) XSRETURN_NO;
        ps.fIncUpdate = (BOOL) SvIV(*tmp);
        for(i=0;i<=31;i++) {
            sprintf(tmprgb, "-ps.rgbReserved%02d", i);
            tmp = hv_fetch(self, tmprgb, 17, 0);
            if(tmp == NULL) XSRETURN_NO;
            ps.rgbReserved[i] = (BYTE) SvIV(*tmp);
        }
        result = EndPaint(hwnd, &ps);
        hv_delete(self, "-DC", 3, 0);
        hv_delete(self, "-ps.hdc", 7, 0);
        hv_delete(self, "-ps.fErase", 10, 0);
        hv_delete(self, "-ps.rcPaint.left", 16, 0);
        hv_delete(self, "-ps.rcPaint.top", 15, 0);
        hv_delete(self, "-ps.rcPaint.right", 17, 0);
        hv_delete(self, "-ps.rcPaint.bottom", 18, 0);
        hv_delete(self, "-ps.fRestore", 12, 0);
        hv_delete(self, "-ps.fIncUpdate", 14, 0);
        for(i=0;i<=31;i++) {
            sprintf(tmprgb, "-ps.rgbReserved%02d", i);
            hv_delete(self, tmprgb, 17, 0);
        }
        XSRETURN_IV((long) result);
    } else {
        XSRETURN_NO;
    }


    ###########################################################################
    # (@)METHOD:SaveBMP(handle)
    # Saves the window content to a BMP file.
void
SaveBMP(handle)
    HWND handle
PREINIT:
    HDC hdc;
    HDC hdc2;
    RECT cr;
    HANDLE hf;
    BITMAP bmp;
    HBITMAP hbmp;
    PBITMAPINFO pbmi;
    PBITMAPINFOHEADER pbih;
    BITMAPFILEHEADER hdr;
    WORD cClrBits;
    LONG width, height;
    LPBYTE lpBits;
    BYTE *hp;
    DWORD cb;
    DWORD dwTmp;
    DWORD dwTotal;
    DWORD MAXWRITE;
PPCODE:
    hdc = GetDC(handle);
    GetClientRect(handle, &cr);
    width = cr.right - cr.left;
    height = cr.bottom - cr.top;
    MAXWRITE = 2048;

    hdc2 = CreateCompatibleDC(hdc);
    hbmp = CreateCompatibleBitmap(hdc, width, height);
    SelectObject(hdc2, hbmp);
    BitBlt(hdc2, 0, 0, width, height, hdc, 0, 0, SRCCOPY);
    if (!GetObject(hbmp, sizeof(BITMAP), (LPSTR)&bmp)) {
        XSRETURN_NO;
    }

    cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
    if (cClrBits == 1)       cClrBits = 1;
    else if (cClrBits <= 4)  cClrBits = 4;
    else if (cClrBits <= 8)  cClrBits = 8;
    else if (cClrBits <= 16) cClrBits = 16;
    else if (cClrBits <= 24) cClrBits = 24;
    else                     cClrBits = 32;

    if (cClrBits != 24)
        pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (2^cClrBits));
    else
        pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER));

    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pbmi->bmiHeader.biWidth = bmp.bmWidth;
    pbmi->bmiHeader.biHeight = bmp.bmHeight;
    pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
    pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
    if (cClrBits < 24) pbmi->bmiHeader.biClrUsed = 2^cClrBits;
    pbmi->bmiHeader.biCompression = BI_RGB;
    pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) /8
                                  * pbmi->bmiHeader.biHeight
                                  * cClrBits;
    pbmi->bmiHeader.biClrImportant = 0;

    pbih = (PBITMAPINFOHEADER) pbmi;

    lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

    if (!GetDIBits(hdc2, hbmp, 0, (WORD) pbih->biHeight, lpBits, pbmi, DIB_RGB_COLORS)) {
        XSRETURN_NO;
    }

    hf = CreateFile("SaveBMP.bmp",
                    GENERIC_READ | GENERIC_WRITE,
                    (DWORD) 0,
                    (LPSECURITY_ATTRIBUTES) NULL,
                    CREATE_ALWAYS,
                    FILE_ATTRIBUTE_NORMAL,
                    (HANDLE) NULL);
    if(hf == INVALID_HANDLE_VALUE) {
        XSRETURN_NO;
    }
    hdr.bfType = 0x4d42;        // #### 0x42 = "B" 0x4d = "M"

    // #### Compute the size of the entire file
    hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) +
                 pbih->biSize + pbih->biClrUsed
                 * sizeof(RGBQUAD) + pbih->biSizeImage);
    hdr.bfReserved1 = 0;
    hdr.bfReserved2 = 0;

    // #### Compute the offset to the array of color indices
    hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
                    pbih->biSize + pbih->biClrUsed
                    * sizeof (RGBQUAD);
    // #### Copy the BITMAPFILEHEADER into the .BMP file
    if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER),
       (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL)) {
       XSRETURN_NO;
    }
    // #### Copy the BITMAPINFOHEADER and RGBQUAD array into the file
    if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER)
                  + pbih->biClrUsed * sizeof (RGBQUAD),
                  (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL)) {
        XSRETURN_NO;
    }
    // #### Copy the array of color indices into the .BMP file
    dwTotal = cb = pbih->biSizeImage;     hp = lpBits;
    while (cb > MAXWRITE)  {
        if (!WriteFile(hf, (LPSTR) hp, MAXWRITE,
                          (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL)) {
            XSRETURN_NO;
        }
        cb -= MAXWRITE;
        hp += MAXWRITE;
    }
    if (!WriteFile(hf, (LPSTR) hp, (int) cb,
        (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL)) {
        XSRETURN_NO;
    }
    if (!CloseHandle(hf)) {
        XSRETURN_NO;
    }

    // #### Free memory
    GlobalFree((HGLOBAL)lpBits);
    DeleteDC(hdc2);
    ReleaseDC(handle, hdc);
    DeleteObject(hbmp);
    XSRETURN_YES;


    ###########################################################################
    # (@)METHOD:GetFontName()
    # Returns the name of the font that is currently assigned to a window or
    # control
LPTSTR
GetFontName(handle)
    HWND handle
PREINIT:
    HDC hdc;
    char facename[256];
CODE:
    hdc = GetDC(handle);
    if(GetTextFace(hdc, 256, facename)) {
        RETVAL = (LPTSTR) facename;
    } else {
        RETVAL = "";
    }
    ReleaseDC(handle, hdc);
OUTPUT:
    RETVAL


    # void
    # EnumChilds(handle)
    #     HWND handle
    # PREINIT:
    #     UINT number;
    # PPCODE:
    #     if(EnumChildWindows(handle, EnumChildsProc, (LPARAM) &number))
    #         XSRETURN(number);
    #     else
    #         XSRETURN_NO;

    ###########################################################################
    # (@)METHOD:EnumMyWindows()
    # Returns a list of top-level window handles created by your application.
    #
    # Usage:
    #    @windows = Win32::GUI::EnumMyWindows();
    #
void
EnumMyWindows()
PREINIT:
    AV* ary;
    int i;
PPCODE:
    ary = newAV();
    EnumWindows( (WNDENUMPROC) EnumMyWindowsProc, (LPARAM) ary);
    for(i=0; i<av_len(ary)+1; i++) {
        //printf("XS(EnumMyWindows): ary[%d] = %ld\n", i, SvIV(*(av_fetch(ary, i, 0))));
        XST_mIV(i, SvIV(*(av_fetch(ary, i, 0))));
    }
    SvREFCNT_dec((SV*)ary);
    XSRETURN(i);
  
    ###########################################################################
    # (@)METHOD:GetSystemMetrics(INDEX)
    # Retrieves various system metrics (dimensions of display elements) and
    # configuration settings. Too numerous to list here. See WinUser.h for
    # a complete list of SM_* constants that you can use for INDEX.
int
GetSystemMetrics(index)
    int index
CODE:
    RETVAL = GetSystemMetrics(index);
OUTPUT:
    RETVAL

    ###########################################################################
    # Common Dialog Boxes
    ###########################################################################


    ###########################################################################
    # (@)METHOD:MessageBox([HANDLE], TEXT, [CAPTION], [TYPE])
    # Shows a standard Windows message box and waits for the user to dismiss it.
    # You can set the window that the message box corresponds to by passing
    # a specific B<HANDLE> (window handle or object). The given B<TEXT> will appear
    # in the message box client area, and the given B<CAPTION> text will appear
    # in the message box titlebar. B<TYPE> specifies various flags that change
    # the appearance of the message box. These are:
    #
    # To set which buttons appear on the message box, specify one of the following values:
    #  0x0000 MB_OK show an OK button
    #  0x0001 MB_OKCANCEL show an OK button and a Cancel button
    #  0x0002 MB_ABORTRETRYIGNORE show Abort, Retry and Ignore buttons
    #  0x0003 MB_YESNOCANCEL show Yes, No and Cancel buttons
    #  0x0004 MB_YESNO show Yes and No buttons
    #  0x0005 MB_RETRYCANCEL show Retry and Cancel buttons
    #  0x0006 MB_CANCELTRYCONTINUE show Cancel, Try Again and Continue buttons (2000/XP only)
    #
    # To add a help button to the message box, specify the following value:
    #  0x4000 MB_HELP
    #
    # To show an icon in the message box, specify one of these values:
    #  0x0010 MB_ICONHAND show a stop-sign icon (used for errors)
    #  0x0020 MB_ICONQUESTION show a question mark icon
    #  0x0030 MB_ICONEXCLAMATION show an exclamation mark icon (used for warnings)
    #  0x0040 MB_ICONASTERISK show an asterisk icon (the letter "i" in a circle) (used for information)
    #
    # To set a default button, specify one of these values (if none of these are specified, the first
    # button will be the default button):
    #  0x0100 MB_DEFBUTTON2 The second button is default
    #  0x0200 MB_DEFBUTTON3 The third button is default
    #  0x0300 MB_DEFBUTTON4 The fourth button is default
    #
    # To specify how the message box affects other windows and various other flags, use one or
    # more of the following values:
    #  0x0000   MB_APPLMODAL The user must dismiss the message box before continuing work in
    #              the window specified by HANDLE (this is the default)
    #  0x1000   MB_SYSTEMMODAL Same as MB_APPLMODAL but the window appears on top of all other
    #              windows on the desktop.
    #  0x2000   MB_TASKMODAL Same as MB_APPLMODAL except that all the top-level windows belonging
    #              to the current thread are disabled if no HANDLE is specified
    #  0x8000   MB_NOFOCUS Does not give the message box input focus
    #  0x10000  MB_SETFOREGROUND Makes the message box become the foreground window
    #  0x20000  MB_DEFAULT_DESKTOP_ONLY If the current desktop is not the default
    #              desktop, MessageBox will not return until the user switches to the default desktop
    #  0x40000  MB_TOPMOST Makes the message box become the topmost window
    #  0x80000  MB_RIGHT Makes text in the message box right-aligned
    #  0x100000 MB_RTLREADING Displays message and caption text using right-to-left reading
    #              order on Hebrew and Arabic systems.
    #  0x200000 MB_SERVICE_NOTIFICATION Displays the message box even if no user is logged
    #              in on Windows NT/2000/XP. You should not specify a HANDLE when using this.
    #
    # To combine several values together, use a bitwise OR operator (|).
    #
    # MessageBox will return one of the following values depending on the user's action:
    #   1 IDOK       The user clicked the OK button.
    #   2 IDCANCEL   The user clicked the Cancel button.
    #   3 IDABORT    The user clicked the Abort button.
    #   4 IDRETRY    The user clicked the Retry button.
    #   5 IDIGNORE   The user clicked the Ignore button.
    #   6 IDYES      The user clicked the Yes button.
    #   7 IDNO       The user clicked the No button.
    #   8 IDCLOSE    The user closed the message box.
    #   9 IDHELP     The user clicked the Help button.
    #  10 IDTRYAGAIN The user clicked the Try Again button.
    #  11 IDCONTINUE The user clicked the Continue button.
    #
    # The default B<TYPE> value is a warning icon with an OK button (MB_ICONEXCLAMATION|MB_OK).
int
MessageBox(handle=NULL, text, caption=NULL, type=MB_ICONEXCLAMATION|MB_OK)
    HWND handle
    LPCTSTR text
    LPCTSTR caption
    UINT type
CODE:
    RETVAL = MessageBox(handle, text, caption, type);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:MessageBeep([TYPE=MB_OK])
    # Plays a sound.
    # 
    # B<TYPE> specifies the sound type :
    #  MB_OK : Play SystemDefault sound.
    #  MB_ICONASTERISK : Play SystemAsterisk sound.
    #  MB_ICONEXCLAMATION : Play SystemExclamation sound.
    #  MB_ICONHAND : Play SystemHand sound.
    #  MB_ICONQUESTION : Play SystemQuestion sound.
    #  0xFFFFFFFF Play Standard beep using the computer speaker 

BOOL 
MessageBeep(type=MB_OK)
    UINT type
CODE:
    RETVAL = MessageBeep(type);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)METHOD:GetOpenFileName(%OPTIONS)
    # Allowed B<%OPTIONS> are:
    #  -owner => WINDOW
    #      Identifies the window that owns the dialog box.
    #  -title => STRING
    #      The title for the dialog
    #  -directory => STRING
    #      Specifies the initial directory
    #  -file => STRING
    #      Specifies a name that will appear on the dialog's edit field
    #  -filter => ARRAY REFERENCE
    #      Specifies an array containing pairs of filter strings.
    #      The first string in each pair is a display string that describes the filter
    #      (for example, "Text Files"), and the second string specifies the filter pattern
    #      (for example, "*.TXT"). To specify multiple filter patterns for a single display
    #      string, use a semicolon to separate the patterns (for example, "*.TXT;*.DOC;*.BAK").
    #      A pattern string can be a combination of valid filename characters and the
    #      asterisk (*) wildcard character. Do not include spaces in the pattern string.
    #  -defaultextension => STRING
    #      Contains the default extension. GetOpenFileName append this extension to the
    #      filename if the user fails to type an extension. This string can be any length,
    #      but only the first three characters are appended. The string should not contain a
    #      period (.).
    #  -defaultfilter => NUMBER
    #      Specifies the index of the currently selected filter in the File Types control.
    #      The first pair of strings has an index value of 0, the second pair 1, and so on.    
    #  -createprompt => 0/1 (default 0)
    #      If the user specifies a file that does not exist, this flag causes the dialog box
    #      to prompt the user for permission to create the file. If the user chooses to create
    #      the file, the dialog box closes and the function returns the specified name; otherwise,
    #      the dialog box remains open. If you use this flag with the -multisel flag, the dialog
    #      box allows the user to specify only one nonexistent file.
    #  -multisel => 0/1 (default 0)
    #      Allow multiple file selection
    #      If the user selects more than one file then return filename with full path.
    #      If the user selects more than one file then return an array with the path
    #      to the current directory followed by the filenames of the selected files.
    #  -explorer => 0/1 (default 1)
    #      Explorer look.
    #  -extensiondifferent => 0/1 (default 0)
    #      Specifies that the user can typed a filename extension that differs from the extension
    #      specified by -defaultextension.
    #  -filemustexist => 0/1 (default 0)
    #      Specifies that the user can type only names of existing files in the File Name entry
    #      field. If this flag is specified and the user enters an invalid name, the dialog box
    #      procedure displays a warning in a message box.
    #  -hidereadonly => 0/1 (default 1)
    #      Hides the Read Only check box. If -hidereadonly is set to 0, the read only status is
    #      return only in array context as last value.
    #  -nochangedir => 0/1 (default 0)
    #      Restores the current directory to its original value if the user changed the directory
    #      while searching for files.
    #  -nodeferencelinks => 0/1 (default 0)
    #      Directs the dialog box to return the path and filename of the selected
    #      shortcut (.LNK) file. If this value is not given, the dialog box returns the
    #      path and filename of the file referenced by the shortcut.
    #  -nonetwork  => 0/1 (default 0)
    #      Hides and disables the Network button
    #  -noreadonlyreturn => 0/1 (default 0)
    #      Specifies that the returned file does not have the Read Only check box checked and is
    #      not in a write-protected directory.
    #  -pathmustexist => 0/1 (default 0)
    #      Specifies that the user can type only valid paths and filenames.
    #      If this flag is used and the user types an invalid path and filename in the File Name
    #      entry field, the dialog box function displays a warning in a message box.
    #  -readonly => 0/1 (default 0)
    #      Causes the Read Only check box to be checked initially when the dialog box is created.
    #
void
GetOpenFileName(...)
PPCODE:
    OPENFILENAME ofn;
    BOOL retval;
    int i, next_i;
    unsigned int fnlen = MAX_PATH;
    char *fname_in = NULL;
    char *fname;
    char *option;
    char *filter;

    ZeroMemory(&ofn, sizeof(OPENFILENAME));
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = NULL;
    ofn.lpstrFilter = NULL;
    ofn.lpstrCustomFilter = NULL;
    ofn.nFilterIndex = 0;
    ofn.lpstrFileTitle = NULL;
    ofn.lpstrInitialDir = NULL;
    ofn.lpstrTitle = NULL;
    ofn.lpstrDefExt = NULL;
    ofn.lpTemplateName = NULL;
    ofn.Flags = OFN_HIDEREADONLY | OFN_EXPLORER;

    next_i = -1;
    for(i = 0; i < items; i++) {
        if(next_i == -1) {
            option = SvPV_nolen(ST(i));
            if(strcmp(option, "-owner") == 0) {
                next_i = i + 1;
                ofn.hwndOwner = (HWND) handle_From(NOTXSCALL ST(next_i));
            } else if(strcmp(option, "-title") == 0) {
                next_i = i + 1;
                ofn.lpstrTitle = SvPV_nolen(ST(next_i));
            } else if(strcmp(option, "-directory") == 0) {
                next_i = i + 1;
                ofn.lpstrInitialDir = SvPV_nolen(ST(next_i));
            } else if(strcmp(option, "-defaultextension") == 0
                      # leave misspelling below for compatibility...
                  ||  strcmp(option, "-defaultextention") == 0 ) {
                next_i = i + 1;
                ofn.lpstrDefExt = SvPV_nolen(ST(next_i));
            } else if(strcmp(option, "-file") == 0) {
                next_i = i + 1;
                fname_in = SvPV_nolen(ST(next_i));
            } else if(strcmp(option, "-filter") == 0) {
                next_i = i + 1;
                if(SvROK(ST(next_i)) && SvTYPE(SvRV(ST(next_i))) == SVt_PVAV) {
                    AV* filters;
                    SV** t;
                    int i, filterlen = 0;
                    char *fpointer;
                    filters = (AV*)SvRV(ST(next_i));
                    for(i=0; i<=av_len(filters); i++) {
                        t = av_fetch(filters, i, 0);
                        if(t != NULL) {
                            filterlen += SvCUR(*t) + 1;
                        }
                    }
                    filterlen += 2;
                    filter = (char *) safemalloc(filterlen);
                    fpointer = filter;
                    for(i=0; i<=av_len(filters); i++) {
                        t = av_fetch(filters, i, 0);
                        if(t != NULL) {
                            strcpy(fpointer, SvPV_nolen(*t));
                            fpointer += SvCUR(*t);
                            *fpointer++ = 0;
                        }
                    }
                    *fpointer = 0;
                    ofn.lpstrFilter = (LPCTSTR) filter;
                } else {
                    W32G_WARN("Win32::GUI: argument to -filter is not an array reference!");
                }
            } else if(strcmp(option, "-defaultfilter") == 0 ) {
                next_i = i + 1;
                ofn.nFilterIndex = SvIV(ST(next_i)) + 1;
            } else if(strcmp(option, "-flags") == 0) {
                next_i = i + 1;
                ofn.Flags = SvIV(ST(next_i));
            } else BitmaskOption( "-multisel", ofn.Flags, OFN_ALLOWMULTISELECT )
                if(fnlen < (unsigned) 4000 * SvIV(ST(next_i))) {
                  fnlen = (unsigned) 4000 * SvIV(ST(next_i));
                }
            } else BitmaskOption( "-createprompt", ofn.Flags, OFN_CREATEPROMPT )
            } else BitmaskOption( "-explorer", ofn.Flags, OFN_EXPLORER )
            } else BitmaskOption( "-extensiondifferent", ofn.Flags, OFN_EXTENSIONDIFFERENT )
            } else BitmaskOption( "-filemustexist", ofn.Flags, OFN_FILEMUSTEXIST )
            } else BitmaskOption( "-hidereadonly", ofn.Flags, OFN_HIDEREADONLY )
            } else BitmaskOption( "-nochangedir", ofn.Flags, OFN_NOCHANGEDIR )
            } else BitmaskOption( "-nodeferencelinks", ofn.Flags, OFN_NODEREFERENCELINKS )
            } else BitmaskOption( "-nonetwork", ofn.Flags, OFN_NONETWORKBUTTON )
            } else BitmaskOption( "-noreadonlyreturn", ofn.Flags, OFN_NOREADONLYRETURN )
            } else BitmaskOption( "-pathmustexist", ofn.Flags, OFN_PATHMUSTEXIST )
            } else BitmaskOption( "-readonly", ofn.Flags, OFN_READONLY )
            }
        } else {
            next_i = -1;
        }
    }

    
    fname = (char *) safemalloc(fnlen);
    fname[0] = 0;
    if(fname_in  &&  fnlen > strlen(fname_in)) {
      strcpy(fname, fname_in);
    }
    ofn.lpstrFile = fname;
    ofn.nMaxFile = fnlen;

    retval = GetOpenFileName(&ofn);

    if(ofn.lpstrFilter != NULL) safefree((void *)filter);

    if(retval) {
      if (ofn.Flags & OFN_ALLOWMULTISELECT) {
         i = 0;
         char * ptr = (char *) ofn.lpstrFile;

         // Count files
         while (*ptr) {
           i ++;
           ptr += strlen(ptr) + 1;
         }

         if ( !(ofn.Flags & OFN_HIDEREADONLY) )
            i++;

         EXTEND(SP, i);

         i = 0;
         ptr = (char *) ofn.lpstrFile;
         while (*ptr) {
            XST_mPV(i, ptr);
            i ++;
            ptr += strlen(ptr) + 1;
         }

         if ( !(ofn.Flags & OFN_HIDEREADONLY) ) {
            XST_mIV( i, (ofn.Flags & OFN_READONLY) );
            i ++;
         }

         safefree((void *)fname);
         XSRETURN(i);
      }
      else {
        if ((ofn.Flags & OFN_HIDEREADONLY) || GIMME_V == G_SCALAR) {
            EXTEND(SP, 1);
            XST_mPV( 0, ofn.lpstrFile);
            safefree((void *)fname);
            XSRETURN(1);
        }
        else {
            EXTEND(SP, 2);
            XST_mPV( 0, ofn.lpstrFile);
            XST_mIV( 1, (ofn.Flags & OFN_READONLY) );
            safefree((void *)fname);
            XSRETURN(2);
        }
      }
    } else {
        safefree((void *)fname);
        XSRETURN_UNDEF;
    }


    ###########################################################################
    # (@)METHOD:GetSaveFileName(%OPTIONS)
    # Allowed B<%OPTIONS> are:
    #  -owner => WINDOW
    #      Identifies the window that owns the dialog box.
    #  -title => STRING
    #      The title for the dialog
    #  -directory => STRING
    #      Specifies the initial directory
    #  -file => STRING
    #      Specifies a name that will appear on the dialog's edit field
    #  -filter => ARRAY REFERENCE
    #      Specifies an array containing pairs of filter strings.
    #      The first string in each pair is a display string that describes the filter
    #      (for example, "Text Files"), and the second string specifies the filter pattern
    #      (for example, "*.TXT"). To specify multiple filter patterns for a single display
    #      string, use a semicolon to separate the patterns (for example, "*.TXT;*.DOC;*.BAK").
    #      A pattern string can be a combination of valid filename characters and the asterisk (*)
    #      wildcard character. Do not include spaces in the pattern string.
    #  -defaultextension => STRING
    #      Contains the default extension. GetSaveFileName append this extension to the filename if the user
    #      fails to type an extension. This string can be any length, but only the first three characters are
    #      appended. The string should not contain a period (.).
    #  -defaultfilter => NUMBER
    #      Specifies the index of the currently selected filter in the File Types control.
    #      The first pair of strings has an index value of 0, the second pair 1, and so on.
    #  -createprompt => 0/1 (default 0)
    #      If the user specifies a file that does not exist, this flag causes the dialog box to prompt
    #      the user for permission to create the file. If the user chooses to create the file, the dialog box
    #      closes and the function returns the specified name; otherwise, the dialog box remains open.
    #      If you use this flag with the -multisel flag, the dialog box allows the user to specify
    #      only one nonexistent file.
    #  -explorer => 0/1 (default 1)
    #      Explorer look.
    #  -extensiondifferent => 0/1 (default 0)
    #      Specifies that the user can typed a filename extension that differs from the extension
    #      specified by -defaultextension.
    #  -filemustexist => 0/1 (default 0)
    #      Specifies that the user can type only names of existing files in the File Name entry field.
    #      If this flag is specified and the user enters an invalid name, the dialog box procedure displays
    #      a warning in a message box.
    #  -nochangedir => 0/1 (default 0)
    #      Restores the current directory to its original value if the user changed the directory
    #      while searching for files.
    #  -nodeferencelinks => 0/1 (default 0)
    #      Directs the dialog box to return the path and filename of the selected shortcut (.LNK) file.
    #      If this value is not given, the dialog box returns the path and filename of the file
    #      referenced by the shortcut.
    #  -nonetwork  => 0/1 (default 0)
    #      Hides and disables the Network button
    #  -noreadonlyreturn => 0/1 (default 0)
    #      Specifies that the returned file is not in a write-protected directory.
    #  -pathmustexist => 0/1 (default 1)
    #      Specifies that the user can type only valid paths and filenames.
    #      If this flag is used and the user types an invalid path and filename in the File Name
    #      entry field, the dialog box function displays a warning in a message box.
    #  -overwriteprompt => 0/1 (default 1)
    #      Generate a message box if the selected file already exists. The user must confirm8
    #      whether to overwrite the file.
void
GetSaveFileName(...)
PPCODE:
    OPENFILENAME ofn;
    BOOL retval;
    int i, next_i;
    char filename[MAX_PATH];
    char *option;
    char *filter;

    ZeroMemory(&ofn, sizeof(OPENFILENAME));
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.hwndOwner = NULL;
    ofn.lpstrFilter = NULL;
    ofn.lpstrCustomFilter = NULL;
    ofn.nFilterIndex = 0;
    ofn.lpstrFileTitle = NULL;
    ofn.lpstrInitialDir = NULL;
    ofn.lpstrTitle = NULL;
    ofn.lpstrDefExt = NULL;
    ofn.lpTemplateName = NULL;
    ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST | OFN_EXPLORER;
    filename[0] = 0;
    ofn.lpstrFile = filename;
    ofn.nMaxFile = MAX_PATH;

    next_i = -1;
    for(i = 0; i < items; i++) {
        if(next_i == -1) {
            option = SvPV_nolen(ST(i));
            if(strcmp(option, "-owner") == 0) {
                next_i = i + 1;
                ofn.hwndOwner = (HWND) handle_From(NOTXSCALL ST(next_i));
            } else if(strcmp(option, "-title") == 0) {
                next_i = i + 1;
                ofn.lpstrTitle = SvPV_nolen(ST(next_i));
            } else if(strcmp(option, "-directory") == 0) {
                next_i = i + 1;
                ofn.lpstrInitialDir = SvPV_nolen(ST(next_i));
            } else if(strcmp(option, "-defaultextension") == 0
                      # leave misspelling below for compatibility...
                  ||  strcmp(option, "-defaultextention") == 0 ) {
                next_i = i + 1;
                ofn.lpstrDefExt = SvPV_nolen(ST(next_i));
            } else if(strcmp(option, "-file") == 0) {
                next_i = i + 1;
                strcpy(filename, SvPV_nolen(ST(next_i)));
            } else if(strcmp(option, "-filter") == 0) {
                next_i = i + 1;
                if(SvROK(ST(next_i)) && SvTYPE(SvRV(ST(next_i))) == SVt_PVAV) {
                    AV* filters;
                    SV** t;
                    int i, filterlen = 0;
                    char *fpointer;
                    filters = (AV*)SvRV(ST(next_i));
                    for(i=0; i<=av_len(filters); i++) {
                        t = av_fetch(filters, i, 0);
                        if(t != NULL) {
                            filterlen += SvCUR(*t) + 1;
                        }
                    }
                    filterlen += 2;
                    filter = (char *) safemalloc(filterlen);
                    fpointer = filter;
                    for(i=0; i<=av_len(filters); i++) {
                        t = av_fetch(filters, i, 0);
                        if(t != NULL) {
                            strcpy(fpointer, SvPV_nolen(*t));
                            fpointer += SvCUR(*t);
                            *fpointer++ = 0;
                        }
                    }
                    *fpointer = 0;
                    ofn.lpstrFilter = (LPCTSTR) filter;
                } else {
                    W32G_WARN("Win32::GUI: argument to -filter is not an array reference!");
                }
            } else if(strcmp(option, "-defaultfilter") == 0 ) {
                next_i = i + 1;
                ofn.nFilterIndex = SvIV(ST(next_i)) + 1;
            } else if(strcmp(option, "-flags") == 0) {
                next_i = i + 1;
                ofn.Flags = SvIV(ST(next_i));
            } else BitmaskOption( "-createprompt", ofn.Flags, OFN_CREATEPROMPT )
            } else BitmaskOption( "-explorer", ofn.Flags, OFN_EXPLORER )
            } else BitmaskOption( "-extensiondifferent", ofn.Flags, OFN_EXTENSIONDIFFERENT )
            } else BitmaskOption( "-filemustexist", ofn.Flags, OFN_FILEMUSTEXIST )
            } else BitmaskOption( "-nochangedir", ofn.Flags, OFN_NOCHANGEDIR )
            } else BitmaskOption( "-nodeferencelinks", ofn.Flags, OFN_NODEREFERENCELINKS )
            } else BitmaskOption( "-nonetwork", ofn.Flags, OFN_NONETWORKBUTTON )
            } else BitmaskOption( "-noreadonlyreturn", ofn.Flags, OFN_NOREADONLYRETURN )
            } else BitmaskOption( "-pathmustexist", ofn.Flags, OFN_PATHMUSTEXIST )
            } else BitmaskOption( "-overwriteprompt", ofn.Flags, OFN_OVERWRITEPROMPT )
            } else BitmaskOption( "-overdriveprompt", ofn.Flags, OFN_OVERWRITEPROMPT ) // deprecated, but retained for compatibility
            }
        } else {
            next_i = -1;
        }
    }
    retval = GetSaveFileName(&ofn);
    if(retval) {
        EXTEND(SP, 1);
        XST_mPV( 0, ofn.lpstrFile);
        if(ofn.lpstrFilter != NULL) safefree((void *)filter);
        XSRETURN(1);
    } else {
        if(ofn.lpstrFilter != NULL) safefree((void *)filter);
        XSRETURN_UNDEF;
    }


    ###########################################################################
    # (@)METHOD:BrowseForFolder(%OPTIONS)
    # Displays the standard "Browse For Folder" dialog box. Returns the
    # selected item's name, or undef if no item was selected or an error
    # occurred.
    #
    # Allowed B<%OPTIONS> are:
    #  -title => STRING
    #      the title for the dialog
    #  -computeronly => 0/1 (default 0)
    #      only enable computers to be selected
    #  -domainonly => 0/1 (default 0)
    #      only enable computers in the current domain or workgroup
    #  -driveonly => 0/1 (default 0)
    #      only enable drives to be selected
    #  -editbox => 0/1 (default 0)
    #      if 1, the dialog will include an edit field in which
    #      the user can type the name of an item
    #  -folderonly => 0/1 (default 0)
    #      only enable folders to be selected
    #  -includefiles => 0/1 (default 0)
    #      the list will include files as well folders
    #  -owner => WINDOW
    #      A Win32::GUI::Window or Win32::GUI::DialogBox object specifiying the
    #      owner window for the dialog box
    #  -printeronly => 0/1 (default 0)
    #      only enable printers to be selected
    #  -root => PATH or CONSTANT
    #      the root directory for browsing; this can be either a
    #      path or one of the following constants (minimum operating systems or
    #      Internet Explorer versions that support the constant are shown in
    #      square brackets. NT denotes Windows NT 4.0, Windows 2000, XP, etc.):
    #
    #       CSIDL_FLAG_CREATE (0x8000)
    #           [2000/ME] Combining this with any of the constants below will create the folder if it does not already exist.
    #       CSIDL_ADMINTOOLS (0x0030)
    #           [2000/ME] Administrative Tools directory for current user
    #       CSIDL_ALTSTARTUP (0x001d)
    #           [All] Non-localized Startup directory in the Start menu for current user
    #       CSIDL_APPDATA (0x001a)
    #           [IE4] Application data directory for current user
    #       CSIDL_BITBUCKET (0x000a)
    #           [All] Recycle Bin
    #       CSIDL_CDBURN_AREA (0x003b)
    #           [XP] Windows XP directory for files that will be burned to CD
    #       CSIDL_COMMON_ADMINTOOLS (0x002f)
    #           [2000/ME] Administrative Tools directory for all users
    #       CSIDL_COMMON_ALTSTARTUP (0x001e)
    #           [All] Non-localized Startup directory in the Start menu for all users
    #       CSIDL_COMMON_APPDATA (0x0023)
    #           [2000/ME] Application data directory for all users
    #       CSIDL_COMMON_DESKTOPDIRECTORY (0x0019)
    #           [NT] Desktop directory for all users
    #       CSIDL_COMMON_DOCUMENTS (0x002e)
    #           [IE4] My Documents directory for all users
    #       CSIDL_COMMON_FAVORITES (0x001f)
    #           [NT] Favorites directory for all users
    #       CSIDL_COMMON_MUSIC (0x0035)
    #           [XP] Music directory for all users
    #       CSIDL_COMMON_PICTURES (0x0036)
    #           [XP] Image directory for all users
    #       CSIDL_COMMON_PROGRAMS (0x0017)
    #           [NT] Start menu "Programs" directory for all users
    #       CSIDL_COMMON_STARTMENU (0x0016)
    #           [NT] Start menu root directory for all users
    #       CSIDL_COMMON_STARTUP (0x0018)
    #           [NT] Start menu Startup directory for all users
    #       CSIDL_COMMON_TEMPLATES (0x002d)
    #           [NT] Document templates directory for all users
    #       CSIDL_COMMON_VIDEO (0x0037)
    #           [XP] Video directory for all users
    #       CSIDL_CONTROLS (0x0003)
    #           [All] Control Panel applets
    #       CSIDL_COOKIES (0x0021)
    #           [All] Cookies directory
    #       CSIDL_DESKTOP (0x0000)
    #           [All] Namespace root (shown as "Desktop", but is parent to my computer, control panel, my documents, etc.)
    #       CSIDL_DESKTOPDIRECTORY (0x0010)
    #           [All] Desktop directory (for desktop icons, folders, etc.) for the current user
    #       CSIDL_DRIVES (0x0011)
    #           [All] My Computer (drives and mapped network drives)
    #       CSIDL_FAVORITES (0x0006)
    #           [All] Favorites directory for the current user
    #       CSIDL_FONTS (0x0014)
    #           [All] Fonts directory
    #       CSIDL_HISTORY (0x0022)
    #           [All] Internet Explorer history items for the current user
    #       CSIDL_INTERNET (0x0001)
    #           [All] Internet root
    #       CSIDL_INTERNET_CACHE (0x0020)
    #           [IE4] Temporary Internet Files directory for the current user
    #       CSIDL_LOCAL_APPDATA (0x001c)
    #           [2000/ME] Local (non-roaming) application data directory for the current user
    #       CSIDL_MYMUSIC (0x000d)
    #           [All] My Music directory for the current user
    #       CSIDL_MYPICTURES (0x0027)
    #           [2000/ME] Image directory for the current user
    #       CSIDL_MYVIDEO (0x000e)
    #           [XP] Video directory for the current user
    #       CSIDL_NETHOOD (0x0013)
    #           [All] My Network Places directory for the current user
    #       CSIDL_NETWORK (0x0012)
    #           [All] Root of network namespace (Network Neighbourhood)
    #       CSIDL_PERSONAL (0x0005)
    #           [All] My Documents directory for the current user
    #       CSIDL_PRINTERS (0x0004)
    #           [All] List of installed printers
    #       CSIDL_PRINTHOOD (0x001b)
    #           [All] Network printers directory for the current user
    #       CSIDL_PROFILE (0x0028)
    #           [2000/ME] The current user's profile directory
    #       CSIDL_PROFILES (0x003e)
    #           [XP] The directory that holds user profiles (see CSDIL_PROFILE)
    #       CSIDL_PROGRAM_FILES (0x0026)
    #           [2000/ME] Program Files directory
    #       CSIDL_PROGRAM_FILES_COMMON (0x002b)
    #           [2000] Directory for files that are used by several applications. Usually Program Files\Common
    #       CSIDL_PROGRAMS (0x0002)
    #           [All] Start menu "Programs" directory for the current user
    #       CSIDL_RECENT (0x0008)
    #           [All] Recent Documents directory for the current user
    #       CSIDL_SENDTO (0x0009)
    #           [All] "Send To" directory for the current user
    #       CSIDL_STARTMENU (0x000b)
    #           [All] Start Menu root for the current user
    #       CSIDL_STARTUP (0x0007)
    #           [All] Start Menu "Startup" folder for the current user
    #       CSIDL_SYSTEM (0x0025)
    #           [2000/ME] System directory. Usually \Windows\System32
    #       CSIDL_TEMPLATES (0x0015)
    #           [All] Document templates directory for the current user
    #       CSIDL_WINDOWS (0x0024)
    #           [2000/ME] Windows root directory, can also be accessed via the environment variables %windir% or %SYSTEMROOT%.
    #
void
BrowseForFolder(...)
PPCODE:
    BROWSEINFO bi;
    LPITEMIDLIST retval;
    LPITEMIDLIST pidl;
    LPSHELLFOLDER pDesktopFolder;
    OLECHAR olePath[MAX_PATH];
    ULONG chEaten;
    HRESULT hr;
    int i, next_i;
    char folder[MAX_PATH];
    char *option;
    ZeroMemory(&bi, sizeof(BROWSEINFO));
    bi.pszDisplayName = folder;
    next_i = -1;
    for(i = 0; i < items; i++) {
        if(next_i == -1) {
            option = SvPV_nolen(ST(i));
            if(strcmp(option, "-owner") == 0) {
                next_i = i + 1;
                bi.hwndOwner = (HWND) handle_From(NOTXSCALL ST(next_i));
            } else if(strcmp(option, "-title") == 0) {
                next_i = i + 1;
                bi.lpszTitle = SvPV_nolen(ST(next_i));
            } else BitmaskOption("-computeronly", bi.ulFlags, BIF_BROWSEFORCOMPUTER)
            } else BitmaskOption("-domainonly", bi.ulFlags, BIF_DONTGOBELOWDOMAIN)
            } else BitmaskOption("-driveonly", bi.ulFlags, BIF_RETURNFSANCESTORS)
            } else BitmaskOption("-editbox", bi.ulFlags, BIF_EDITBOX)
            } else BitmaskOption("-folderonly", bi.ulFlags, BIF_RETURNONLYFSDIRS)
            } else BitmaskOption("-includefiles", bi.ulFlags, BIF_BROWSEINCLUDEFILES)
            } else BitmaskOption("-printeronly", bi.ulFlags, BIF_BROWSEFORPRINTER)
            } else if(strcmp(option, "-directory") == 0) {
                next_i = i + 1;
                bi.lParam = (LPARAM) SvPV_nolen(ST(next_i));
                bi.lpfn = BrowseForFolderProc;
            } else if(strcmp(option, "-root") == 0) {
                next_i = i + 1;
                if(SvIOK(ST(next_i))) {
                    bi.pidlRoot = INT2PTR(LPCITEMIDLIST,SvIV(ST(next_i)));
                } else {
                    SHGetDesktopFolder(&pDesktopFolder);
                    MultiByteToWideChar(
                        CP_ACP,
                        MB_PRECOMPOSED,
                        SvPV_nolen(ST(next_i)), -1,
                        olePath, MAX_PATH
                    );
                    hr = pDesktopFolder->ParseDisplayName(
                    // hr = IShellFolder::ParseDisplayName(
                        NULL,
                        NULL,
                        olePath,
                        &chEaten,
                        &pidl,
                        NULL
                    );
                    if(FAILED(hr)) {
                        W32G_WARN("Win32::GUI::BrowseForFolder: can't get ITEMIDLIST for -root!");
                        pDesktopFolder->Release();
                        XSRETURN_UNDEF;
                    } else {
                        bi.pidlRoot = pidl;
                        pDesktopFolder->Release();
                    }
                }
            }
        } else {
            next_i = -1;
        }
    }
    retval = SHBrowseForFolder(&bi);
    if(retval != NULL) {
        if(SHGetPathFromIDList(retval, folder)) {
            EXTEND(SP, 1);
            XST_mPV( 0, folder);
            XSRETURN(1);
        } else {
            XSRETURN_UNDEF;
        }
    } else {
        XSRETURN_UNDEF;
    }


    ###########################################################################
    # (@)METHOD:ChooseColor(%OPTIONS)
    # Allowed B<%OPTIONS> are:
    #  -owner => WINDOW
    #      Identifies the window that owns the dialog box.
    #  -color => COLOR
    #      Initial color selected.
void
ChooseColor(...)
PPCODE:
    CHOOSECOLOR cc;
    COLORREF lpCustColors[16];
    BOOL retval;
    int i, next_i;
    char * option;

    ZeroMemory(&cc, sizeof(CHOOSECOLOR));
    cc.lStructSize = sizeof(CHOOSECOLOR);
    cc.hwndOwner = NULL;
    cc.lpCustColors = lpCustColors;
    cc.lpTemplateName = NULL;
    cc.Flags = 0;
    cc.rgbResult = 0;

    next_i = -1;
    for(i = 0; i < items; i++) {
        if(next_i == -1) {
            option = SvPV_nolen(ST(i));
            if(strcmp(option, "-owner") == 0) {
                next_i = i + 1;
                cc.hwndOwner = (HWND) handle_From(NOTXSCALL ST(next_i));
            } else if(strcmp(option, "-color") == 0) {
                next_i = i + 1;
                cc.rgbResult = (COLORREF) SvCOLORREF(NOTXSCALL ST(next_i));
                cc.Flags = cc.Flags | CC_RGBINIT;
            }
        } else {
            next_i = -1;
        }
    }

    retval = ChooseColor(&cc);

    if(retval) {
        EXTEND(SP, 1);
        XST_mIV(0, cc.rgbResult);
        XSRETURN(1);
    } else {
        XSRETURN_UNDEF;
    }


    ###########################################################################
    # (@)METHOD:ChooseFont(%OPTIONS)
    # Allowed B<%OPTIONS> are:
    #  -owner => WINDOW
    #      Identifies the window that owns the dialog box.
    #  -pointsize
    #  -height
    #  -width
    #  -escapement
    #  -orientation
    #  -weight
    #  -bold
    #  -italic
    #  -underline
    #  -strikeout
    #  -charset
    #  -outputprecision
    #  -clipprecision
    #  -quality
    #  -family
    #  -name
    #  -face (== -name)
    #  -color
    #  -ttonly
    #  -fixedonly
    #  -effects
    #  -script
    #  -minsize
    #  -maxsize
void
ChooseFont(...)
PPCODE:
    CHOOSEFONT cf;
    static LOGFONT lf;
    BOOL retval;
    int i, next_i;
    char *option;

    ZeroMemory(&cf, sizeof(CHOOSEFONT));
    cf.lStructSize = sizeof(CHOOSEFONT);
    cf.hwndOwner = NULL;
    cf.lpLogFont = &lf;
    cf.lpTemplateName = NULL;
    cf.Flags = CF_SCREENFONTS;

    next_i = -1;
    for(i = 0; i < items; i++) {
        if(next_i == -1) {
            option = SvPV_nolen(ST(i));
            if(strcmp(option, "-owner") == 0) {
                next_i = i + 1;
                cf.hwndOwner = (HWND) handle_From(NOTXSCALL ST(next_i));
            }
            if(strcmp(option, "-pointsize") == 0) {
                next_i = i + 1;
                cf.iPointSize = SvIV(ST(next_i));
            }
            if(strcmp(option, "-height") == 0) {
                HDC hDisplay;
                hDisplay = CreateDC("DISPLAY", NULL, NULL, NULL);
                next_i = i + 1;
                lf.lfHeight = -MulDiv(SvIV(ST(next_i)), GetDeviceCaps(hDisplay, LOGPIXELSY), 72);
                DeleteDC(hDisplay);
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);

            }
            if(strcmp(option, "-width") == 0) {
                next_i = i + 1;
                lf.lfWidth = SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-escapement") == 0) {
                next_i = i + 1;
                lf.lfEscapement = SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-orientation") == 0) {
                next_i = i + 1;
                lf.lfOrientation = SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-weight") == 0) {
                next_i = i + 1;
                lf.lfWeight = (int) SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-bold") == 0) {
                next_i = i + 1;
                if(SvIV(ST(next_i)) != 0) lf.lfWeight = 700;
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-italic") == 0) {
                next_i = i + 1;
                lf.lfItalic = (BYTE) SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-underline") == 0) {
                next_i = i + 1;
                lf.lfUnderline = (BYTE) SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-strikeout") == 0) {
                next_i = i + 1;
                lf.lfStrikeOut = (BYTE) SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-charset") == 0) {
                next_i = i + 1;
                lf.lfCharSet = (BYTE) SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-outputprecision") == 0) {
                next_i = i + 1;
                lf.lfOutPrecision = (BYTE) SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-clipprecision") == 0) {
                next_i = i + 1;
                lf.lfClipPrecision = (BYTE) SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-quality") == 0) {
                next_i = i + 1;
                lf.lfQuality = (BYTE) SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-family") == 0) {
                next_i = i + 1;
                lf.lfPitchAndFamily = (BYTE) SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-name") == 0
            || strcmp(option, "-face") == 0) {
                next_i = i + 1;
                strncpy(lf.lfFaceName, SvPV_nolen(ST(next_i)), 32);
                SwitchBit(cf.Flags, CF_INITTOLOGFONTSTRUCT, 1);
            }
            if(strcmp(option, "-color") == 0) {
                next_i = i + 1;
                cf.rgbColors = (DWORD) SvCOLORREF(NOTXSCALL ST(next_i));
                SwitchBit(cf.Flags, CF_EFFECTS, 1);
            }
            if(strcmp(option, "-ttonly") == 0) {
                next_i = i + 1;
                SwitchBit(cf.Flags, CF_TTONLY, SvIV(ST(next_i)));
            }
            if(strcmp(option, "-fixedonly") == 0) {
                next_i = i + 1;
                SwitchBit(cf.Flags, CF_FIXEDPITCHONLY, SvIV(ST(next_i)));
            }
            if(strcmp(option, "-effects") == 0) {
                next_i = i + 1;
                SwitchBit(cf.Flags, CF_EFFECTS, SvIV(ST(next_i)));
            }
            if(strcmp(option, "-script") == 0) {
                next_i = i + 1;
                if(SvIV(ST(next_i)) == 0) {
                    SwitchBit(cf.Flags, CF_NOSCRIPTSEL, 1);
                } else {
                    SwitchBit(cf.Flags, CF_NOSCRIPTSEL, 0);
                }
            }
            if(strcmp(option, "-minsize") == 0) {
                next_i = i + 1;
                cf.nSizeMin = SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_LIMITSIZE, 1);
            }
            if(strcmp(option, "-maxsize") == 0) {
                next_i = i + 1;
                cf.nSizeMax = SvIV(ST(next_i));
                SwitchBit(cf.Flags, CF_LIMITSIZE, 1);
            }


        } else {
            next_i = -1;
        }
    }
    retval = ChooseFont(&cf);
    if(retval) {

        HDC hDisplay;
        hDisplay = CreateDC("DISPLAY", NULL, NULL, NULL);
        lf.lfHeight = -MulDiv(lf.lfHeight, 72, GetDeviceCaps(hDisplay, LOGPIXELSY));
        DeleteDC(hDisplay);
 
        EXTEND(SP, 18);
        XST_mPV( 0, "-name");
        XST_mPV( 1, lf.lfFaceName);
        XST_mPV( 2, "-height");
        XST_mIV( 3, lf.lfHeight);
        XST_mPV( 4, "-width");
        XST_mIV( 5, lf.lfWidth);
        XST_mPV( 6, "-weight");
        XST_mIV( 7, lf.lfWeight);
        XST_mPV( 8, "-pointsize");
        XST_mIV( 9, cf.iPointSize);
        XST_mPV(10, "-italic");
        XST_mIV(11, lf.lfItalic);
        XST_mPV(12, "-underline");
        XST_mIV(13, lf.lfUnderline);
        XST_mPV(14, "-strikeout");
        XST_mIV(15, lf.lfStrikeOut);
        XST_mPV(16, "-color");
        XST_mIV(17, cf.rgbColors);
        // XST_mPV(18, "-style");
        // XST_mPV(19, cf.lpszStyle);
        // XSRETURN(20);
        XSRETURN(18);
    } else
        XSRETURN_UNDEF;


    ###########################################################################
    # (@)METHOD:CommDlgExtendedError()
    # Returns the common dialog library error code.
DWORD
CommDlgExtendedError(...)
CODE:
    RETVAL = CommDlgExtendedError();
OUTPUT:
    RETVAL


HGDIOBJ
SelectObject(handle,hgdiobj)
    HDC handle
    HGDIOBJ hgdiobj
CODE:
    RETVAL = SelectObject(handle, hgdiobj);
OUTPUT:
    RETVAL

BOOL
DeleteObject(hgdiobj)
    HGDIOBJ hgdiobj
CODE:
    RETVAL = DeleteObject(hgdiobj);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:GetStockObject(OBJECT)
    # Returns the handle of the specified predefined system object (pen, brush
    # or font). 
    #
    # B<OBJECT> can have one of the following values:
    #   0 WHITE_BRUSH
    #   1 GRAY_BRUSH
    #   2 LTGRAY_BRUSH
    #   3 DKGRAY_BRUSH
    #   4 BLACK_BRUSH
    #   5 NULL_BRUSH (also HOLLOW_BRUSH)
    #   6 WHITE_PEN
    #   7 BLACK_PEN
    #   8 NULL_PEN
    #  10 OEM_FIXED_FONT
    #  11 ANSI_FIXED_FONT
    #  12 ANSI_VAR_FONT
    #  13 SYSTEM_FONT
    #  14 DEVICE_DEFAULT_FONT
    #  15 DEFAULT_PALETTE
    #  16 SYSTEM_FIXED_FONT
    #  17 DEFAULT_GUI_FONT
    #  18 DC_BRUSH (Windows 2000/XP only)
    #  19 DC_PEN (Windows 2000/XP only)
    #
    # The returned handle can be referenced as if it was a Win32::GUI object
    # (eg. a Win32::GUI::Brush or Win32::GUI::Font), but note that it is not
    # blessed, so you can't directly invoke methods on it:
    #
    #    $Font = Win32::GUI::GetStockObject(17);    # DEFAULT_GUI_FONT
    #    print $Font->GetMetrics();                 # !!!WRONG!!!
    #    print Win32::GUI::Font::GetMetrics($Font); # correct
    #    $Window->SetFont($Font);                   # correct
HGDIOBJ
GetStockObject(object)
    int object
CODE:
    RETVAL = GetStockObject(object);
OUTPUT:
    RETVAL

    ###########################################################################
    # Accelerator
    ###########################################################################

    ###########################################################################
    # (@)INTERNAL:CreateAcceleratorTable(ID, KEY, FLAG, ...)
HACCEL
CreateAcceleratorTable(...)
PREINIT:
    LPACCEL acc;
    int a, c, i;
CODE:
    a = items/3;
    acc = (LPACCEL) safemalloc(a * sizeof(ACCEL));
    c = 0;
    for(i=0; i<items; i+=3) {
        acc[c].cmd   = (WORD) SvIV(ST(i));
        acc[c].key   = (WORD) SvIV(ST(i+1));
        acc[c].fVirt = (BYTE) SvIV(ST(i+2));
        c++;
    }
    RETVAL = CreateAcceleratorTable(acc, a);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)INTERNAL:DestroyAcceleratorTable(HANDLE)
BOOL
DestroyAcceleratorTable(handle)
    HACCEL handle;
CODE:
    RETVAL = DestroyAcceleratorTable(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # Menu
    ###########################################################################


    ###########################################################################
    # (@)INTERNAL:CreateMenu()
HMENU
CreateMenu(...)
CODE:
    RETVAL = CreateMenu();
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)INTERNAL:CreatePopupMenu()
HMENU
CreatePopupMenu(...)
CODE:
    RETVAL = CreatePopupMenu();
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:SetMenu(MENU)
    # Associates the specified MENU to a window.
BOOL
SetMenu(handle,menu)
    HWND handle
    HMENU menu
CODE:
    RETVAL = SetMenu(handle, menu);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:GetMenu()
    # Returns the handle of the menu associated with the window.
HMENU
GetMenu(handle)
    HWND handle
CODE:
    RETVAL = GetMenu(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)METHOD:DrawMenuBar()
    # Forces redrawing of the menu bar.
BOOL
DrawMenuBar(handle)
    HWND handle
CODE:
    RETVAL = DrawMenuBar(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)INTERNAL:DestroyMenu(HANDLE)
BOOL
DestroyMenu(hmenu)
    HMENU hmenu
CODE:
    RETVAL = DestroyMenu(hmenu);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)INTERNAL:GetDllVersion(DLLNAME)
    # Replacement for Win32::GetFileVersion, which doesn't exist in perl 5.6 or
    # cygwin perl 5.8
    # In scalar contect returns dotted string of dll version
    # In list context returns major version, minor version, build
void
GetDllVersion(filename)
    LPTSTR filename
PREINIT:
    HINSTANCE hinstDll;
    DWORD major = 0xFFFFFFFF;
    DWORD minor = 0xFFFFFFFF;
    DWORD build = 0xFFFFFFFF;
PPCODE:
    hinstDll = LoadLibrary(filename);

    if(hinstDll) {
        DLLGETVERSIONPROC pDllGetVersion;
        pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinstDll,"DllGetVersion");

        if(pDllGetVersion) {
            DLLVERSIONINFO dvi;
            HRESULT hr;

            ZeroMemory(&dvi, sizeof(dvi));
            dvi.cbSize = sizeof(dvi);

            hr = (*pDllGetVersion)(&dvi);
    
            if(SUCCEEDED(hr)) {
                major = dvi.dwMajorVersion;
                minor = dvi.dwMinorVersion;
                build = dvi.dwBuildNumber;
            }
        }

        FreeLibrary(hinstDll);
    }

    if(major == 0xFFFFFFFF) {
        DWORD size;
        DWORD handle;
        char *data;

        size = GetFileVersionInfoSize(filename, &handle);
        if(size) {
            New(0, data, size, char);
            if(data) {
                if(GetFileVersionInfo(filename, handle, size, data)) {
                    VS_FIXEDFILEINFO *info;
                    UINT len;
                    if(VerQueryValue(data, "\\", (void**)&info, &len)) {
                        major = (info->dwFileVersionMS>>16);
                        minor = (info->dwFileVersionMS&0xffff);
                        build = (info->dwFileVersionLS>>16);
		    }
		}

                Safefree(data);
	    }
	}
    }

    if(major == 0xFFFFFFFF) {
	    XSRETURN_UNDEF;
    }

    if (GIMME_V == G_ARRAY) {
        EXTEND(SP, 3);
        XST_mIV(0, major);
        XST_mIV(1, minor);
        XST_mIV(2, build);
	items = 3;
    }
    else {
       char version[50];
       sprintf(version, "%d.%d.%d", major, minor, build);
       XST_mPV(0, version);
       items = 1;
    }
    XSRETURN(items);


    ###########################################################################
    # (@)PACKAGE:Win32::GUI::Menu
    ###########################################################################

MODULE = Win32::GUI     PACKAGE = Win32::GUI::Menu


#pragma message( "*** PACKAGE Win32::GUI::Menu..." )

    ###########################################################################
    # (@)METHOD:RemoveMenu(position, [flags=MF_BYPOSITION])
    # The RemoveMenu function deletes a menu item or detaches a submenu from the
    # specified menu. If the menu item opens a drop-down menu or submenu, RemoveMenu
    # does not destroy the menu or its handle, allowing the menu to be reused
    #
    # position specifies the menu item to be removed, as determined by the flags field.
    #
    # flags specifies how the position field is interpreted, and can take one of the
    # following values:
    #   MF_BYCOMMAND  (0x0000): Indicates that position gives the identifier of the menu item
    #   MF_BYPOSITION (0x0400): Indicates that position gives the zero-based relative position of the menu item.
    # If flags is not supplied, then MF_BYPOSITION is used as the default
BOOL
RemoveMenu(menu, position, flags=MF_BYPOSITION)
  HMENU menu
  UINT position
  UINT flags
CODE:
    RETVAL = RemoveMenu(menu, position, flags);
OUTPUT:
    RETVAL

    ###########################################################################
    # (@)INTERNAL:DESTROY(HANDLE)
BOOL
DESTROY(handle)
    HMENU handle
CODE:
    RETVAL = DestroyMenu(handle);
OUTPUT:
    RETVAL


    ###########################################################################
    # (@)PACKAGE:Win32::GUI::MenuButton
    ###########################################################################

MODULE = Win32::GUI     PACKAGE = Win32::GUI::MenuButton

#pragma message( "*** PACKAGE Win32::GUI::MenuButton..." )

    ###########################################################################
    # (@)INTERNAL:InsertMenuItem(HANDLE, %OPTIONS)
void
InsertMenuItem(...)
PREINIT:
    MENUITEMINFO mii;
    LPPERLWIN32GUI_MENUITEMDATA perlmid;
    UINT myItem;
    BOOL RETVAL;
PPCODE:
    ZeroMemory(&mii, sizeof(MENUITEMINFO));
    mii.cbSize = sizeof(MENUITEMINFO);
    myItem = 0;
    Newz(0, perlmid, 1, PERLWIN32GUI_MENUITEMDATA);
    perlmid->dwSize = sizeof(PERLWIN32GUI_MENUITEMDATA);
    perlmid->svCode = newSVsv(&PL_sv_undef);
    ParseMenuItemOptions(NOTXSCALL sp, mark, ax, items, 1, &mii, perlmid, &myItem);
    mii.hbmpChecked = NULL;
    mii.hbmpUnchecked = NULL;
#ifdef PERLWIN32GUI_STRONGDEBUG
    printf("XS(InsertMenuItem) doing InsertMenuItem (HMENU=0x%x)...\n", handle_From(NOTXSCALL ST(0)));
#endif
    RETVAL = InsertMenuItem(
        (HMENU) handle_From(NOTXSCALL ST(0)),
        myItem,
        FALSE,
        &mii
    );
#ifdef PERLWIN32GUI_STRONGDEBUG
    printf("XS(InsertMenuItem) done InsertMenuItem (RETVAL=%d)\n", RETVAL);
#endif
    XSRETURN_IV(RETVAL);


    ###########################################################################
    # (@)PACKAGE:Win32::GUI::MenuItem
    ###########################################################################

MODULE = Win32::GUI     PACKAGE = Win32::GUI::MenuItem

#pragma message( "*** PACKAGE Win32::GUI::MenuItem..." )

    ###########################################################################
    # (@)METHOD:Change(%OPTIONS)
    # Change most of the options used when the object was created.
void
Change(...)
PPCODE:
    MENUITEMINFO myMII;
    LPPERLWIN32GUI_MENUITEMDATA perlmid;
    UINT myItem;
    HMENU hMenu;
    SV** parentmenu;
    char tmpmenutext[1024];
    if(SvROK(ST(0))) {
        parentmenu = hv_fetch((HV*)SvRV((ST(0))), "-menu", 5, 0);
        if(parentmenu != NULL) {
            hMenu = INT2PTR(HMENU,SvIV(*parentmenu));
            myItem = SvIV(*(hv_fetch((HV*)SvRV(ST(0)), "-id", 3, 0)));
        } else {
            hMenu = (HMENU) handle_From(NOTXSCALL ST(0));
        }
    }
#ifdef PERLWIN32GUI_DEBUG
        printf("XS(MenuItem::Change): hMenu=0x%x, myItem=%d\n", hMenu, myItem);
#endif
    ZeroMemory(&myMII, sizeof(MENUITEMINFO));
    myMII.cbSize = sizeof(MENUITEMINFO);
    myMII.fMask = MIIM_STATE | MIIM_SUBMENU | MIIM_TYPE | MIIM_DATA;
    myMII.dwTypeData = tmpmenutext;
    myMII.cch = 1024;
    if(GetMenuItemInfo(hMenu, myItem, FALSE, &myMII)) {
        perlmid = (LPPERLWIN32GUI_MENUITEMDATA) myMII.dwItemData;
        myMII.fMask = 0;
        ParseMenuItemOptions(NOTXSCALL sp, mark, ax, items, 1, &myMII, perlmid, &myItem);
        myMII.hbmpChecked = NULL;
        myMII.hbmpUnchecked = NULL;
        XSRETURN_IV(
            SetMenuItemInfo(hMenu, myItem, FALSE, &myMII)
        );
    } else
        XSRETURN_UNDEF;


    ###########################################################################
    # (@)METHOD:Checked(...)
    # Set or Retrieve Checked state of a menu item.
void
Checked(...)
PPCODE:
    MENUITEMINFO myMII;
    int i;
    UINT myItem;
    HMENU hMenu;
    SV** parentmenu;

    if(SvROK(ST(0))) {
        parentmenu = hv_fetch((HV*)SvRV((ST(0))), "-menu", 5, 0);
        if(parentmenu != NULL) {
            hMenu = INT2PTR(HMENU,SvIV(*parentmenu));
            myItem = SvIV(*(hv_fetch((HV*)SvRV(ST(0)), "-id", 3, 0)));
            i = 1;
        } else {
            hMenu = (HMENU) handle_From(NOTXSCALL ST(0));
            myItem = SvIV(ST(1));
            i = 2;
        }
    }
    ZeroMemory(&myMII, sizeof(MENUITEMINFO));
    myMII.cbSize = sizeof(MENUITEMINFO);
    myMII.fMask = MIIM_STATE;
    if(GetMenuItemInfo(hMenu, myItem, FALSE, &myMII)) {
        if(items > i) {
            myMII.fMask = MIIM_STATE;
            SwitchBit(myMII.fState, MFS_CHECKED, SvIV(ST(i)));
            XSRETURN_IV(
                SetMenuItemInfo(hMenu, myItem, FALSE, &myMII)
            );
        } else {
            XSRETURN_IV((myMII.fState & MFS_CHECKED) ? 1 : 0);
        }
    } else {
        XSRETURN_UNDEF;
    }


    ###########################################################################
    # (@)METHOD:Enabled([FLAG])
    # Set or Retrieve Enabled state of a menu item.
    #
    # B<FLAG> is a boolean.  If supplied sets the state of the menu item
    # (0 = Disabled, 1 = Eabled).  If not supplied, retrieves the enabled
    # state if the menu item.
    #
void
Enabled(...)
PPCODE:
    MENUITEMINFO myMII;
    int i, x;
    UINT myItem;
    HMENU hMenu;
    SV** parentmenu;

    if(SvROK(ST(0))) {
        parentmenu = hv_fetch((HV*)SvRV((ST(0))), "-menu", 5, 0);
        if(parentmenu != NULL) {
            hMenu = INT2PTR(HMENU,SvIV(*parentmenu));
            myItem = SvIV(*(hv_fetch((HV*)SvRV(ST(0)), "-id", 3, 0)));
            i = 1;
        } else {
            hMenu = (HMENU) handle_From(NOTXSCALL ST(0));
            myItem = SvIV(ST(1));
            i = 2;
        }
    }
    ZeroMemory(&myMII, sizeof(MENUITEMINFO));
    myMII.cbSize = sizeof(MENUITEMINFO);
    myMII.fMask = MIIM_STATE;
    if(GetMenuItemInfo(hMenu, myItem, FALSE, &myMII)) {
        if(items > i) {
            myMII.fMask = MIIM_STATE;
            x = (SvIV(ST(i))) ? 0 : 1;
            SwitchBit(myMII.fState, MFS_DISABLED, x);
            XSRETURN_IV(
                SetMenuItemInfo(hMenu, myItem, FALSE, &myMII)
            );
        } else {
            XSRETURN_IV((myMII.fState & MFS_DISABLED) ? 0 : 1);
        }
    } else {
        XSRETURN_UNDEF;
    }



BOOT:
    {
        INITCOMMONCONTROLSEX icce;
        icce.dwSize = sizeof(INITCOMMONCONTROLSEX);
        icce.dwICC = ICC_ANIMATE_CLASS | ICC_BAR_CLASSES | ICC_COOL_CLASSES
                   | ICC_LISTVIEW_CLASSES | ICC_PROGRESS_CLASS
                   | ICC_TAB_CLASSES | ICC_TREEVIEW_CLASSES
                   | ICC_UPDOWN_CLASS | ICC_USEREX_CLASSES
                   | ICC_DATE_CLASSES;
        if(!InitCommonControlsEx(&icce)) {
            warn("Win32::GUI: Unable to init common controls!\n");
        }
    }