using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
using System.Drawing;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

// TODO Move most of this to the Platform project
namespace Foo.Platform.Win32
{
    public class MyWin32Wnd : IWin32Window
    {
        private IntPtr m_Handle = IntPtr.Zero;
        public IntPtr Handle
        {
            get{return m_Handle;}
            set{m_Handle = value;}
        }
    }

    ////
    // Win32 Enums
    ////
    #region Win32 Enums

    public enum WindowAction : int
    {
        SW_HIDE = 0,
        SW_SHOWNORMAL = 1,
        SW_NORMAL = 1,
        SW_SHOWMINIMIZED = 2,
        SW_SHOWMAXIMIZED = 3,
        SW_MAXIMIZE = 3,
        SW_SHOWNOACTIVATE = 4,
        SW_SHOW = 5,
        SW_MINIMIZE = 6,
        SW_SHOWMINNOACTIVE = 7,
        SW_SHOWNA = 8,
        SW_RESTORE = 9,
        SW_SHOWDEFAULT = 10,
        SW_FORCEMINIMIZE = 11,
        SW_MAX = 11
    }

    public enum WindowLong : int
    {
        GWL_EXSTYLE   = -20,
        GWL_STYLE     = -16,
        GWL_WNDPROC   = -4,
        GWL_HINSTANCE = -6,
        GWL_ID        = -12,
        GWL_USERDATA  = -21,
        DWL_DLGPROC   = 4,
        DWL_MSGRESULT = 0,
        DWL_USER      = 8
    }

    public enum WindowStyles : uint
    {
        WS_MAXIMIZE = 0x01000000,
        WS_VISIBLE = 0x10000000,
        WS_POPUP = 0x80000000,
        WS_BORDER = 0x00800000,
        WS_CAPTION = 0x00C00000,
        WS_CHILD = 0x40000000,
        WS_CHILDWINDOW = 0x40000000,
        WS_CLIPCHILDREN = 0x02000000,
        WS_CLIPSIBLINGS = 0x04000000,
        WS_DISABLED = 0x08000000,
        WS_DLGFRAME = 0x00400000,
        WS_GROUP = 0x00020000
    }

    public enum WindowMessage : int
    {
        WM_NULL = 0x0000,
        WM_CREATE = 0x0001,
        WM_DESTROY = 0x0002,
        WM_MOVE = 0x0003,
        WM_SIZE = 0x0005,
        WM_ACTIVATE = 0x0006,
        WM_SETFOCUS = 0x0007,
        WM_KILLFOCUS = 0x0008,
        WM_ENABLE = 0x000A,
        WM_SETREDRAW = 0x000B,
        WM_SETTEXT = 0x000C,
        WM_GETTEXT = 0x000D,
        WM_GETTEXTLENGTH = 0x000E,
        WM_PAINT = 0x000F,
        WM_CLOSE = 0x0010,
        WM_QUERYENDSESSION = 0x0011,
        WM_QUIT = 0x0012,
        WM_QUERYOPEN = 0x0013,
        WM_ERASEBKGND = 0x0014,
        WM_SYSCOLORCHANGE = 0x0015,
        WM_ENDSESSION = 0x0016,
        WM_SHOWWINDOW = 0x0018,
        WM_CTLCOLOR = 0x0019,
        WM_WININICHANGE = 0x001A,
        WM_SETTINGCHANGE = 0x001A,
        WM_DEVMODECHANGE = 0x001B,
        WM_ACTIVATEAPP = 0x001C,
        WM_FONTCHANGE = 0x001D,
        WM_TIMECHANGE = 0x001E,
        WM_CANCELMODE = 0x001F,
        WM_SETCURSOR = 0x0020,
        WM_MOUSEACTIVATE = 0x0021,
        WM_CHILDACTIVATE = 0x0022,
        WM_QUEUESYNC = 0x0023,
        WM_GETMINMAXINFO = 0x0024,
        WM_PAINTICON = 0x0026,
        WM_ICONERASEBKGND = 0x0027,
        WM_NEXTDLGCTL = 0x0028,
        WM_SPOOLERSTATUS = 0x002A,
        WM_DRAWITEM = 0x002B,
        WM_MEASUREITEM = 0x002C,
        WM_DELETEITEM = 0x002D,
        WM_VKEYTOITEM = 0x002E,
        WM_CHARTOITEM = 0x002F,
        WM_SETFONT = 0x0030,
        WM_GETFONT = 0x0031,
        WM_SETHOTKEY = 0x0032,
        WM_GETHOTKEY = 0x0033,
        WM_QUERYDRAGICON = 0x0037,
        WM_COMPAREITEM = 0x0039,
        WM_GETOBJECT = 0x003D,
        WM_COMPACTING = 0x0041,
        WM_COMMNOTIFY = 0x0044,
        WM_WINDOWPOSCHANGING = 0x0046,
        WM_WINDOWPOSCHANGED = 0x0047,
        WM_POWER = 0x0048,
        WM_COPYDATA = 0x004A,
        WM_CANCELJOURNAL = 0x004B,
        WM_NOTIFY = 0x004E,
        WM_INPUTLANGCHANGEREQUEST = 0x0050,
        WM_INPUTLANGCHANGE = 0x0051,
        WM_TCARD = 0x0052,
        WM_HELP = 0x0053,
        WM_USERCHANGED = 0x0054,
        WM_NOTIFYFORMAT = 0x0055,
        WM_CONTEXTMENU = 0x007B,
        WM_STYLECHANGING = 0x007C,
        WM_STYLECHANGED = 0x007D,
        WM_DISPLAYCHANGE = 0x007E,
        WM_GETICON = 0x007F,
        WM_SETICON = 0x0080,
        WM_NCCREATE = 0x0081,
        WM_NCDESTROY = 0x0082,
        WM_NCCALCSIZE = 0x0083,
        WM_NCHITTEST = 0x0084,
        WM_NCPAINT = 0x0085,
        WM_NCACTIVATE = 0x0086,
        WM_GETDLGCODE = 0x0087,
        WM_SYNCPAINT = 0x0088,
        WM_NCMOUSEMOVE = 0x00A0,
        WM_NCLBUTTONDOWN = 0x00A1,
        WM_NCLBUTTONUP = 0x00A2,
        WM_NCLBUTTONDBLCLK = 0x00A3,
        WM_NCRBUTTONDOWN = 0x00A4,
        WM_NCRBUTTONUP = 0x00A5,
        WM_NCRBUTTONDBLCLK = 0x00A6,
        WM_NCMBUTTONDOWN = 0x00A7,
        WM_NCMBUTTONUP = 0x00A8,
        WM_NCMBUTTONDBLCLK = 0x00A9,
        WM_KEYDOWN = 0x0100,
        WM_KEYUP = 0x0101,
        WM_CHAR = 0x0102,
        WM_DEADCHAR = 0x0103,
        WM_SYSKEYDOWN = 0x0104,
        WM_SYSKEYUP = 0x0105,
        WM_SYSCHAR = 0x0106,
        WM_SYSDEADCHAR = 0x0107,
        WM_KEYLAST = 0x0108,
        WM_IME_STARTCOMPOSITION = 0x010D,
        WM_IME_ENDCOMPOSITION = 0x010E,
        WM_IME_COMPOSITION = 0x010F,
        WM_IME_KEYLAST = 0x010F,
        WM_INITDIALOG = 0x0110,
        WM_COMMAND = 0x0111,
        WM_SYSCOMMAND = 0x0112,
        WM_TIMER = 0x0113,
        WM_HSCROLL = 0x0114,
        WM_VSCROLL = 0x0115,
        WM_INITMENU = 0x0116,
        WM_INITMENUPOPUP = 0x0117,
        WM_MENUSELECT = 0x011F,
        WM_MENUCHAR = 0x0120,
        WM_ENTERIDLE = 0x0121,
        WM_MENURBUTTONUP = 0x0122,
        WM_MENUDRAG = 0x0123,
        WM_MENUGETOBJECT = 0x0124,
        WM_UNINITMENUPOPUP = 0x0125,
        WM_MENUCOMMAND = 0x0126,
        WM_CTLCOLORMSGBOX = 0x0132,
        WM_CTLCOLOREDIT = 0x0133,
        WM_CTLCOLORLISTBOX = 0x0134,
        WM_CTLCOLORBTN = 0x0135,
        WM_CTLCOLORDLG = 0x0136,
        WM_CTLCOLORSCROLLBAR = 0x0137,
        WM_CTLCOLORSTATIC = 0x0138,
        WM_MOUSEMOVE = 0x0200,
        WM_LBUTTONDOWN = 0x0201,
        WM_LBUTTONUP = 0x0202,
        WM_LBUTTONDBLCLK = 0x0203,
        WM_RBUTTONDOWN = 0x0204,
        WM_RBUTTONUP = 0x0205,
        WM_RBUTTONDBLCLK = 0x0206,
        WM_MBUTTONDOWN = 0x0207,
        WM_MBUTTONUP = 0x0208,
        WM_MBUTTONDBLCLK = 0x0209,
        WM_MOUSEWHEEL = 0x020A,
        WM_PARENTNOTIFY = 0x0210,
        WM_ENTERMENULOOP = 0x0211,
        WM_EXITMENULOOP = 0x0212,
        WM_NEXTMENU = 0x0213,
        WM_SIZING = 0x0214,
        WM_CAPTURECHANGED = 0x0215,
        WM_MOVING = 0x0216,
        WM_DEVICECHANGE = 0x0219,
        WM_MDICREATE = 0x0220,
        WM_MDIDESTROY = 0x0221,
        WM_MDIACTIVATE = 0x0222,
        WM_MDIRESTORE = 0x0223,
        WM_MDINEXT = 0x0224,
        WM_MDIMAXIMIZE = 0x0225,
        WM_MDITILE = 0x0226,
        WM_MDICASCADE = 0x0227,
        WM_MDIICONARRANGE = 0x0228,
        WM_MDIGETACTIVE = 0x0229,
        WM_MDISETMENU = 0x0230,
        WM_ENTERSIZEMOVE = 0x0231,
        WM_EXITSIZEMOVE = 0x0232,
        WM_DROPFILES = 0x0233,
        WM_MDIREFRESHMENU = 0x0234,
        WM_IME_SETCONTEXT = 0x0281,
        WM_IME_NOTIFY = 0x0282,
        WM_IME_CONTROL = 0x0283,
        WM_IME_COMPOSITIONFULL = 0x0284,
        WM_IME_SELECT = 0x0285,
        WM_IME_CHAR = 0x0286,
        WM_IME_REQUEST = 0x0288,
        WM_IME_KEYDOWN = 0x0290,
        WM_IME_KEYUP = 0x0291,
        WM_MOUSEHOVER = 0x02A1,
        WM_MOUSELEAVE = 0x02A3,
        WM_CUT = 0x0300,
        WM_COPY = 0x0301,
        WM_PASTE = 0x0302,
        WM_CLEAR = 0x0303,
        WM_UNDO = 0x0304,
        WM_RENDERFORMAT = 0x0305,
        WM_RENDERALLFORMATS = 0x0306,
        WM_DESTROYCLIPBOARD = 0x0307,
        WM_DRAWCLIPBOARD = 0x0308,
        WM_PAINTCLIPBOARD = 0x0309,
        WM_VSCROLLCLIPBOARD = 0x030A,
        WM_SIZECLIPBOARD = 0x030B,
        WM_ASKCBFORMATNAME = 0x030C,
        WM_CHANGECBCHAIN = 0x030D,
        WM_HSCROLLCLIPBOARD = 0x030E,
        WM_QUERYNEWPALETTE = 0x030F,
        WM_PALETTEISCHANGING = 0x0310,
        WM_PALETTECHANGED = 0x0311,
        WM_HOTKEY = 0x0312,
        WM_PRINT = 0x0317,
        WM_PRINTCLIENT = 0x0318,
        WM_HANDHELDFIRST = 0x0358,
        WM_HANDHELDLAST = 0x035F,
        WM_AFXFIRST = 0x0360,
        WM_AFXLAST = 0x037F,
        WM_PENWINFIRST = 0x0380,
        WM_PENWINLAST = 0x038F,
        WM_APP = 0x8000,
        WM_USER = 0x0400,
        WM_REFLECT = WM_USER + 0x1c00,
        WM_CHANGEUISTATE = 0x0127,
        WM_UPDATEUISTATE = 0x0128,
        WM_QUERYUISTATE  = 0x0129
    }

    public enum WindowUIState : int
    {
        UIS_SET = 1,
        UIS_CLEAR = 2,
        UIS_INITIALIZE = 3
    }    

    public enum CSIDL : int
    {
        CSIDL_DESKTOP = 0x0000,    // <desktop>
        CSIDL_INTERNET = 0x0001,    // Internet Explorer (icon on desktop)
        CSIDL_PROGRAMS = 0x0002,    // Start Menu\Programs
        CSIDL_CONTROLS = 0x0003,    // My Computer\Control Panel
        CSIDL_PRINTERS = 0x0004,    // My Computer\Printers
        CSIDL_PERSONAL = 0x0005,    // My Documents
        CSIDL_FAVORITES = 0x0006,    // <user name>\Favorites
        CSIDL_STARTUP = 0x0007,    // Start Menu\Programs\Startup
        CSIDL_RECENT = 0x0008,    // <user name>\Recent
        CSIDL_SENDTO = 0x0009,    // <user name>\SendTo
        CSIDL_BITBUCKET = 0x000a,    // <desktop>\Recycle Bin
        CSIDL_STARTMENU = 0x000b,    // <user name>\Start Menu
        CSIDL_MYDOCUMENTS = 0x000c,    // logical "My Documents" desktop icon
        CSIDL_MYMUSIC = 0x000d,    // "My Music" folder
        CSIDL_MYVIDEO = 0x000e,    // "My Videos" folder
        CSIDL_DESKTOPDIRECTORY = 0x0010,    // <user name>\Desktop
        CSIDL_DRIVES = 0x0011,    // My Computer
        CSIDL_NETWORK = 0x0012,    // Network Neighborhood (My Network Places)
        CSIDL_NETHOOD = 0x0013,    // <user name>\nethood
        CSIDL_FONTS = 0x0014,    // windows\fonts
        CSIDL_TEMPLATES = 0x0015,
        CSIDL_COMMON_STARTMENU = 0x0016,    // All Users\Start Menu
        CSIDL_COMMON_PROGRAMS = 0X0017,    // All Users\Start Menu\Programs
        CSIDL_COMMON_STARTUP = 0x0018,    // All Users\Startup
        CSIDL_COMMON_DESKTOPDIRECTORY = 0x0019,    // All Users\Desktop
        CSIDL_APPDATA = 0x001a,    // <user name>\Application Data
        CSIDL_PRINTHOOD = 0x001b,    // <user name>\PrintHood
        CSIDL_LOCAL_APPDATA = 0x001c,    // <user name>\Local Settings\Applicaiton Data (non roaming)
        CSIDL_ALTSTARTUP = 0x001d,    // non localized startup
        CSIDL_COMMON_ALTSTARTUP = 0x001e,    // non localized common startup
        CSIDL_COMMON_FAVORITES = 0x001f,
        CSIDL_INTERNET_CACHE = 0x0020,
        CSIDL_COOKIES = 0x0021,
        CSIDL_HISTORY = 0x0022,
        CSIDL_COMMON_APPDATA = 0x0023,    // All Users\Application Data
        CSIDL_WINDOWS = 0x0024,    // GetWindowsDirectory()
        CSIDL_SYSTEM = 0x0025,    // GetSystemDirectory()
        CSIDL_PROGRAM_FILES = 0x0026,    // C:\Program Files
        CSIDL_MYPICTURES = 0x0027,    // C:\Program Files\My Pictures
        CSIDL_PROFILE = 0x0028,    // USERPROFILE
        CSIDL_SYSTEMX86 = 0x0029,    // x86 system directory on RISC
        CSIDL_PROGRAM_FILESX86 = 0x002a,    // x86 C:\Program Files on RISC
        CSIDL_PROGRAM_FILES_COMMON = 0x002b,    // C:\Program Files\Common
        CSIDL_PROGRAM_FILES_COMMONX86 = 0x002c,    // x86 Program Files\Common on RISC
        CSIDL_COMMON_TEMPLATES = 0x002d,    // All Users\Templates
        CSIDL_COMMON_DOCUMENTS = 0x002e,    // All Users\Documents
        CSIDL_COMMON_ADMINTOOLS = 0x002f,    // All Users\Start Menu\Programs\Administrative Tools
        CSIDL_ADMINTOOLS = 0x0030,    // <user name>\Start Menu\Programs\Administrative Tools
        CSIDL_CONNECTIONS = 0x0031,    // Network and Dial-up Connections
        CSIDL_COMMON_MUSIC = 0x0035,    // All Users\My Music
        CSIDL_COMMON_PICTURES = 0x0036,    // All Users\My Pictures
        CSIDL_COMMON_VIDEO = 0x0037,    // All Users\My Video
        CSIDL_CDBURN_AREA = 0x003b    // USERPROFILE\Local 
    }

    public enum SHGFP_TYPE : int
    {
        SHGFP_TYPE_CURRENT = 0,
        SHGFP_TYPE_DEFAULT = 1
    }

    public enum DesiredAccess : int
    {
        DELETE = 0x00010000,       // Required to delete the object. 
        READ_CONTROL = 0x00020000, // Required to read information in the security descriptor for the object, not including the information in the SACL. To read or write the SACL, you must request the ACCESS_SYSTEM_SECURITY access right. For more information, see SACL Access Right. 
        SYNCHRONIZE = 0x00100000,  // The right to use the object for synchronization. This enables a thread to wait until the object is in the signaled state. 
        WRITE_DAC = 0x00040000,    // Required to modify the DACL in the security descriptor for the object. 
        WRITE_OWNER = 0x00080000   // Required to change the owner in the security descriptor for the object.
    }

    public enum WaitTime : uint
    {
        IGNORE = 0,            // Ignore signal
        INFINITE = 0xFFFFFFFF  // Infinite timeout
    }

    public enum SystemMetric : int
    {
        SM_CXSCREEN = 0,
        SM_CYSCREEN = 1,
        SM_CXVSCROLL = 2,
        SM_CYHSCROLL = 3,
        SM_CYCAPTION = 4,
        SM_CXBORDER = 5,
        SM_CYBORDER = 6,
        SM_CXDLGFRAME = 7,
        SM_CYDLGFRAME = 8,
        SM_CYVTHUMB = 9,
        SM_CXHTHUMB = 10,
        SM_CXICON = 11,
        SM_CYICON = 12,
        SM_CXCURSOR = 13,
        SM_CYCURSOR = 14,
        SM_CYMENU = 15,
        SM_CXFULLSCREEN = 16,
        SM_CYFULLSCREEN = 17,
        SM_CYKANJIWINDOW = 18,
        SM_MOUSEPRESENT = 19,
        SM_CYVSCROLL = 20,
        SM_CXHSCROLL = 21,
        SM_DEBUG = 22,
        SM_SWAPBUTTON = 23,
        SM_RESERVED1 = 24,
        SM_RESERVED2 = 25,
        SM_RESERVED3 = 26,
        SM_RESERVED4 = 27,
        SM_CXMIN = 28,
        SM_CYMIN = 29,
        SM_CXSIZE = 30,
        SM_CYSIZE = 31,
        SM_CXFRAME = 32,
        SM_CYFRAME = 33,
        SM_CXMINTRACK = 34,
        SM_CYMINTRACK = 35,
        SM_CXDOUBLECLK = 36,
        SM_CYDOUBLECLK = 37,
        SM_CXICONSPACING = 38,
        SM_CYICONSPACING = 39,
        SM_MENUDROPALIGNMENT = 40,
        SM_PENWINDOWS = 41,
        SM_DBCSENABLED = 42,
        SM_CMOUSEBUTTONS = 43
    }

    public enum SetWindowPosFlags : uint
    {
        SWP_NOSIZE = 0x0001,
        SWP_NOMOVE = 0x0002,
        SWP_NOZORDER = 0x0004,
        SWP_NOREDRAW = 0x0008,
        SWP_NOACTIVATE = 0x0010,
        SWP_FRAMECHANGED = 0x0020,
        SWP_SHOWWINDOW = 0x0040,
        SWP_HIDEWINDOW = 0x0080,
        SWP_NOCOPYBITS = 0x0100,
        SWP_NOOWNERZORDER = 0x0200,
        SWP_NOSENDCHANGING = 0x0400,
        SWP_DRAWFRAME = SWP_FRAMECHANGED,
        SWP_NOREPOSITION = SWP_NOOWNERZORDER
    }

    public enum WindowLayerAttributes
    {
        LWA_COLORKEY = 0x00000001,
        LWA_ALPHA = 0x00000002
    }

    public enum WindowPos : int
    {
        HWND_TOP = 0,
        HWND_BOTTOM = 1,
        HWND_TOPMOST = -1,
        HWND_NOTOPMOST = -2
    }

    public enum VK : ushort
    {
        SHIFT = 0x10,
        CONTROL = 0x11,
        MENU = 0x12,
        ESCAPE = 0x1B,
        BACK = 0x08,
        TAB = 0x09,
        RETURN = 0x0D,
        PRIOR = 0x21,
        NEXT = 0x22,
        END = 0x23,
        HOME = 0x24,
        LEFT = 0x25,
        UP = 0x26,
        RIGHT = 0x27,
        DOWN = 0x28,
        SELECT = 0x29,
        PRINT = 0x2A,
        EXECUTE = 0x2B,
        SNAPSHOT = 0x2C,
        INSERT = 0x2D,
        DELETE = 0x2E,
        HELP = 0x2F,
        NUMPAD0 = 0x60,
        NUMPAD1 = 0x61,
        NUMPAD2 = 0x62,
        NUMPAD3 = 0x63,
        NUMPAD4 = 0x64,
        NUMPAD5 = 0x65,
        NUMPAD6 = 0x66,
        NUMPAD7 = 0x67,
        NUMPAD8 = 0x68,
        NUMPAD9 = 0x69,
        MULTIPLY = 0x6A,
        ADD = 0x6B,
        SEPARATOR = 0x6C,
        SUBTRACT = 0x6D,
        DECIMAL = 0x6E,
        DIVIDE = 0x6F,
        F1 = 0x70,
        F2 = 0x71,
        F3 = 0x72,
        F4 = 0x73,
        F5 = 0x74,
        F6 = 0x75,
        F7 = 0x76,
        F8 = 0x77,
        F9 = 0x78,
        F10 = 0x79,
        F11 = 0x7A,
        F12 = 0x7B,
        OEM_1 = 0xBA,   // ',:' for US
        OEM_PLUS = 0xBB,   // '+' any country
        OEM_COMMA = 0xBC,   // ',' any country
        OEM_MINUS = 0xBD,   // '-' any country
        OEM_PERIOD = 0xBE,   // '.' any country
        OEM_2 = 0xBF,   // '/?' for US
        OEM_3 = 0xC0,   // '`~' for US
        MEDIA_NEXT_TRACK = 0xB0,
        MEDIA_PREV_TRACK = 0xB1,
        MEDIA_STOP = 0xB2,
        MEDIA_PLAY_PAUSE = 0xB3,
        LWIN = 0x5B,
        RWIN = 0x5C
    }

    public enum AssocF : ushort
    {
        Init_NoRemapCLSID = 0x1,
        Init_ByExeName = 0x2,
        Open_ByExeName = 0x2,
        Init_DefaultToStar = 0x4,
        Init_DefaultToFolder = 0x8,
        NoUserSettings = 0x10,
        NoTruncate = 0x20,
        Verify = 0x40,
        RemapRunDll = 0x80,
        NoFixUps = 0x100,
        IgnoreBaseClass = 0x200
    }

    public enum AssocStr : ushort
    {
        Command = 1,
        Executable,
        FriendlyDocName,
        FriendlyAppName,
        NoOpen,
        ShellNewValue,
        DDECommand,
        DDEIfExec,
        DDEApplication,
        DDETopic
    }

    #endregion

    ////
    // Win32 Structures
    ////
    #region Win32 Structures
    
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;

        public Rectangle AsRectangle
        {
            get
            {
                return new Rectangle(this.left, this.top, this.right - this.left, this.bottom - this.top);
            }
        }

        public static RECT FromXYWH(int x, int y, int width, int height)
        {
            RECT rect = new RECT();
            rect.left = x;
            rect.top = y;
            rect.right = x + width;
            rect.bottom = y + height;
            return rect;
        }

        public static RECT FromRectangle(Rectangle rectangle)
        {
            RECT rect = new RECT();
            rect.left = rectangle.Left;
            rect.top = rectangle.Top;
            rect.right = rectangle.Right;
            rect.bottom = rectangle.Bottom;
            return rect;
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SIZE
    {
        public int cx;
        public int cy;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct ShellExecuteInfo
    {
        public int cbSize;
        public uint fMask;
        public IntPtr hwnd;
        public string lpVerb;
        public string lpFile;
        public string lpParameters;
        public string lpDirectory;
        public uint nShow;
        public IntPtr hInstApp;
        public IntPtr lpIDList;
        public string lpClass;
        public IntPtr hkeyClass;
        public uint dwHotKey;
        public IntPtr hIcon_Monitor; // union DUMMYUNIONNAME
        public IntPtr hProcess;
    }

    [StructLayout(LayoutKind.Explicit, Size = 28)]
    public struct INPUT
    {
        [FieldOffset(0)]
        public uint type;
        [FieldOffset(4)]
        public KEYBDINPUT ki;
    };

    [StructLayout(LayoutKind.Sequential)]
    public struct KEYBDINPUT
    {
        public ushort wVk;
        public ushort wScan;
        public uint dwFlags;
        public long time;
        public uint dwExtraInfo;
    };

    [StructLayout(LayoutKind.Sequential)]
    public struct MONITORINFOEX
    {
        public int cbSize;
        public RECT rcMonitor;
        public RECT rcWork;
        public int dwFlags;
        [MarshalAs(UnmanagedType.ByValTStr,SizeConst=32)]
        public string szDeviceName;
    }

    #endregion

    ////
    // .Net Win32 Wrapper Functions
    ////
    public class Win32_WrapperFunc
    {
        /// <summary>   
        /// Get a windows client rectangle in a .NET structure
        /// </summary>   
        /// <param name="hwnd">The window handle to look up</param>   
        /// <returns>The rectangle</returns>   
        public static Rectangle GetClientRect(IntPtr hwnd)
        {
            RECT rect = new RECT();
            Win32Functions.GetClientRect(hwnd, out rect);
            return rect.AsRectangle;
        }

        /// <summary>   
        /// Get a windows rectangle in a .NET structure   
        /// </summary>   
        /// <param name="hwnd">The window handle to look up</param>   
        /// <returns>The rectangle</returns>   
        public static Rectangle GetWindowRect(IntPtr hwnd)
        {
            RECT rect = new RECT();
            Win32Functions.GetWindowRect(hwnd, out rect);
            return rect.AsRectangle;
        }

        /// <summary>
        /// Get's the System's registered file for the corresponding File
        /// </summary>
        /// <param name="fileNameAndPath">full file name and path</param>
        /// <returns>Returns Registered exe for file in Uppercase</returns>
        public static string GetRegisteredExeForFile(string fileNameAndPath)
        {
            if (!String.IsNullOrEmpty(fileNameAndPath))
            {
                bool bIsValidExe = false;
                string strExeOrDllPath = string.Empty;
                bool bSuccess = Win32_WrapperFunc.FindExeOrDll(fileNameAndPath, out strExeOrDllPath, out bIsValidExe);

                if (bSuccess && bIsValidExe)
                    return Path.GetFileName(strExeOrDllPath).ToUpper();
            }
            return string.Empty;
        }

        /// <summary>
        /// Useful for finding who is responsible by Shell32 to handle this file
        /// </summary>
        /// <param name="fileNameAndPath">full qualifying path to File</param>
        /// <param name="strExeOrDllPath">will contain that path that is found in the registry</param>
        /// <param name="IsValidExe">true if the return value is a valid exe, false if it is a Dll</param>
        /// <returns>true if a match was found, false otherwise</returns>
        public static bool FindExeOrDll(string fileNameAndPath, out string strExeOrDllPath, out bool IsValidExe)
        {
            if (!String.IsNullOrEmpty(fileNameAndPath))
            {
                // Try FindExecutable first
                StringBuilder retStr = new StringBuilder();
                int ret = (int)Win32Functions.FindExecutable(fileNameAndPath, null, retStr);
                strExeOrDllPath = retStr.ToString();

                // If FindExe Failed, try AssocQueryString instead
                if (String.IsNullOrEmpty(strExeOrDllPath))
                {
                    string Ext = Path.GetExtension(fileNameAndPath);
                    StringBuilder retStr2 = new StringBuilder();
                    uint pcchOut = Win32_Constants.MAX_PATH;
                    Win32Functions.AssocQueryString(0, AssocStr.Executable, Ext, "open", retStr2, ref pcchOut);

                    if (retStr2.Length > 0)
                        strExeOrDllPath = retStr2.ToString();
                }

                IsValidExe = false;
                if (!String.IsNullOrEmpty(strExeOrDllPath))
                {
                    string Ext = Path.GetExtension(strExeOrDllPath);
                    IsValidExe = (Ext.ToUpper() == ".EXE");
                    return true;
                }
            }
            strExeOrDllPath = string.Empty;
            IsValidExe = false;
            return false;
        }

        /// <summary>
        /// Uses Shell32 to get the Windows location on this computer
        /// </summary>
        /// <returns>path to windows dir on this machine</returns>
        public static string GetWindowsPath()
        {
            StringBuilder windowsPath = new StringBuilder();
            Win32Functions.SHGetFolderPath(IntPtr.Zero, (int)CSIDL.CSIDL_WINDOWS, IntPtr.Zero, (int)SHGFP_TYPE.SHGFP_TYPE_CURRENT, windowsPath);
            return windowsPath.ToString();
        }

        /// <summary>
        /// Use this function to set transparency of a layered top level window. 
        /// This will only work on Windows that are part of your own Process Space. calling this on external windows,
        /// outside of your process SetLayeredWindowAttributes() will fail.
        /// </summary>
        /// <param name="hWnd">the window to make or unmake transparent</param>
        /// <param name="alpha">0 for transparent 255 for totally visible</param>
        public static void SetAlphaChannel(IntPtr hWnd, byte alpha)
        {            
            long dwStyle = (long) Win32Functions.GetWindowLong(hWnd, (int) ((int) WindowLong.GWL_EXSTYLE | (int) Win32_Constants.WS_EX_LAYERED));
            long retVal = Win32Functions.SetWindowLong(hWnd, (int) WindowLong.GWL_EXSTYLE, (int) dwStyle);

            if (retVal == 0)
                Trace.Write("SetAlphaChannel's SetWindowLong Failed");

            bool bSuccess = Win32Functions.SetLayeredWindowAttributes(hWnd, (int)Win32Functions.RGB(0, 0, 0), alpha, (uint)(WindowLayerAttributes.LWA_ALPHA));
            if (!bSuccess)
                Trace.Write("SetAlphaChannel's SetLayeredWindowAttributes Failed");
        }

        /// <summary>
        /// Could be useful later - maybe
        /// </summary>
        /// <param name="hWnd"></param>
        //public static Rectangle GetAbsoluteClientRect(IntPtr hWnd)
        //{
        //    //Rectangle windowRect = Win32Functions.GetWindowRect(hWnd);
        //    //Rectangle clientRect = Win32Functions.GetClientRect(hWnd);

        //    //// This gives us the width of the left, right and bottom chrome - we can then determine the top height   
        //    //int chromeWidth = (int)((windowRect.Width - clientRect.Width) / 2);
        //    //return new Rectangle(new Point(windowRect.X + chromeWidth, windowRect.Y + (windowRect.Height - clientRect.Height - chromeWidth)), clientRect.Size);
        //}

    }

    ////
    // Win32 Constants
    ////    
    public class Win32_Constants
    {
        public const ushort KEYEVENTF_KEYUP = 0x0002;
        public const int SRCCOPY = 0x00CC0020;
        public const int CAPTUREBLT = 0x40000000;
        public const int MONITOR_DEFAULTTONEAREST = 0x00000002;
        public const int WM_USER = 0x0400;
        public const int WS_EX_LAYERED = 0x00080000;
        public const int MAX_PATH = 260;
    }

    ////
    // Win32 Functions
    ////
    public class Win32Functions
    {
        // Declare the Log4net Variable
        private static log4net.ILog Log = Logger.GetLog4NetInterface(MethodBase.GetCurrentMethod().DeclaringType);
        
        public static int LOWORD(int n)
        {
            return (n & 0xffff);
        }
        public static int HIWORD(int n)
        {
            return ((n >> 16) & 0xffff);
        }
        public static int RGB(int r, int g, int b)
        {
            int rs = r & 0xffff;
            int gs = (g << 8) & 0xffff;
            int bs = (b << 16) & 0xffff;
            return (rs | gs | bs);
        }

        ////
        // Shell32
        ////        
        #region Shell32

        [DllImport("shell32.dll")]
        public static extern IntPtr ShellExecute(IntPtr hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd);

        [DllImport("shell32.dll", SetLastError = true)]
        extern public static bool ShellExecuteEx(ref ShellExecuteInfo lpExecInfo);

        [DllImport("shell32.dll")]
        extern public static IntPtr FindExecutable(string lfFile, string lpDirectory, [Out] StringBuilder lpResult);

        [DllImport("shell32.dll")]
        extern public static long SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, int dwFlags, [Out] StringBuilder pszPath);

        #endregion

        ////
        // User32
        ////     
        #region User32

        [DllImport("user32.dll")]
        public static extern IntPtr SetFocus(IntPtr hwnd);

        [DllImport("user32.dll")]
        public static extern IntPtr GetFocus();

        [DllImport("user32.dll")]
        public static extern IntPtr SetActiveWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        extern public static IntPtr SetParent(IntPtr child, IntPtr newParent);

        [DllImport("user32.dll")]
        extern public static long SetWindowLong(IntPtr hWnd, int nIndex, Int32 dwNewLong);

        [DllImport("user32.dll")]
        extern public static long GetWindowLong(IntPtr hWnd, int nIndex);        

        [DllImport("User32.dll")]
        extern public static int SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        extern public static bool SetLayeredWindowAttributes(IntPtr hwnd, Int32 crKey, byte bAlpha, uint dwFlags);

        [DllImport("User32.dll")]
        extern public static IntPtr GetForegroundWindow();

        [DllImport("User32.dll")]
        extern public static bool IsWindow(IntPtr hwnd);

        [DllImport("user32.dll")]
        extern public static int ShowWindow(IntPtr hwnd, int nCmdShow);

        [DllImport("user32.dll")]
        extern public static int GetWindowText(IntPtr hWnd, StringBuilder text, int count);

        [DllImport("user32.dll")]
        extern public static bool GetWindowRect(IntPtr hWnd, out RECT rect);

        [DllImport("user32.dll")]
        extern public static bool GetClientRect(IntPtr hWnd, out RECT rect);

        [DllImport("User32.dll")]
        extern public static bool EnumWindows(Delegate lpEnumFunc, IntPtr lParam);

        [DllImport("User32.dll")]
        extern public static int GetWindowThreadProcessId(IntPtr hWnd, ref int lpdwProcessId);

        [DllImport("User32.dll")]
        extern public static bool PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        public static extern uint SendInput(uint nInputs, ref INPUT pInputs, int cbSize);

        [DllImport("User32.dll")]
        extern public static int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll")]
        extern public static int GetSystemMetrics(int index);

        [DllImport("user32.dll")]
        extern public static bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        [DllImport("user32.dll")]
        public static extern IntPtr GetDesktopWindow();

        [DllImport("user32.dll")]
        public static extern IntPtr GetDC(IntPtr ptr);

        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowDC(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern IntPtr GetDCEx(IntPtr hWnd, IntPtr hrgnClip, int flags);

        [DllImport("user32.dll")]
        public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);

        [DllImport("user32.dll")]
        public static extern bool EmptyClipboard();

        [DllImport("user32.dll")]
        public static extern int GetClipboardSequenceNumber();

        [DllImport("user32.dll")]
        public static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFOEX monitorinfo);

        [DllImport("user32.dll")]
        public static extern IntPtr MonitorFromRect(ref RECT rect, int dwFlags);

        #endregion

        ////
        // Kernel32
        ////
        #region Kernel32

        [DllImport("kernel32.dll")]
        internal static extern IntPtr LoadLibrary(String dllname);

        [DllImport("kernel32.dll")]
        internal static extern IntPtr GetProcAddress(IntPtr hModule, String procname);

        [DllImport("kernel32.dll")]
        extern public static int GetProcessId(IntPtr hProcess);

        [DllImport("kernel32.dll")]
        extern public static IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);

        [DllImport("kernel32.dll")]
        extern public static int WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);

        [DllImport("kernel32.dll")]
        extern public static int GetLastError();

        [DllImport("kernel32.dll")]
        // lpEventAttributes should be a ref to a SECURITY_ATTRIBUTES struct, but we only pass in NULL so we can just make it an IntPtr
        extern public static IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, [In] StringBuilder lpName);

        [DllImport("kernel32.dll")]
        extern public static IntPtr SetEvent(IntPtr hEvent);

        [DllImport("kernel32.dll")]
        extern public static bool CloseHandle(IntPtr hHandle);

        [DllImport("kernel32.dll")]
        extern public static int GlobalAddAtom([In] StringBuilder lpString);

        [DllImport("kernel32.dll")]
        extern public static int GlobalDeleteAtom(int nAtom);

        #endregion

        ////
        // Shlwapi
        ////
        [DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern uint AssocQueryString(AssocF flags, AssocStr str, string pszAssoc, string pszExtra, [Out] StringBuilder pszOut, [In][Out] ref uint pcchOut);

        ////
        // Ole32
        ////
        [DllImport("ole32.dll")]
        public static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);

        ////
        // Uxtheme
        ////
        [DllImport("uxTheme.dll")]
        extern public static bool IsAppThemed();

        ////
        // Dwmapi
        ////
        [DllImport("dwmapi.dll")]
        extern public static long DwmIsCompositionEnabled(ref bool pfEnabled);

        ////
        // GDI32
        ////
        #region GDI32

        [DllImport("gdi32.dll")]
        public static extern IntPtr DeleteDC(IntPtr hDc);

        [DllImport("gdi32.dll")]
        public static extern IntPtr DeleteObject(IntPtr hDc);

        [DllImport("gdi32.dll")]
        public static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, int RasterOp);

        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);

        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

        [DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);

        [DllImport("gdi32.dll")]
        public static extern IntPtr CreateDC(string lpszDriver, IntPtr passNULL, IntPtr passNULL2, IntPtr passNULL3);

        #endregion        

        ////
        // Helper .Net Functions
        ////        
        // .net implementation of EnumWindowsProc Callback
        public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

        public interface IWindowHandleAccess
        {
            IntPtr hWnd
            {
                get;
                set;
            }
        }

        public static IntPtr GetWindowHandle<Context>(EnumWindowsProc proc, Context context) where Context : IWindowHandleAccess
        {
            // Initialize unmanged memory to hold the struct.
            IntPtr pContext = Marshal.AllocHGlobal(Marshal.SizeOf(context));

            try
            {
                // Copy the struct to unmanaged memory.
                Marshal.StructureToPtr(context, pContext, false);

                bool bFound = false;
                int numTries = 5;

                while (!bFound && numTries != 0)
                {
                    Win32Functions.EnumWindows(proc, pContext);

                    // Marshall context back to managed
                    context = (Context)Marshal.PtrToStructure(pContext, typeof(Context));

                    if (context.hWnd != IntPtr.Zero)
                    {
                        bFound = true;
                    }
                    else
                    {
                        // Sleep a little and try again
                        Thread.Sleep(500);
                        --numTries;
                    }
                }
            }
            finally
            {
                Log.ErrorFormat("{0}() - Unknown exception occured!", MethodBase.GetCurrentMethod().Name);

                // Free the unmanaged memory.
                Marshal.FreeHGlobal(pContext);
            }

            return context.hWnd;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct EnumWindowsContext_pid_filename : IWindowHandleAccess
        {
            public int targetPid;
            public string filePath;
            private IntPtr _hWnd;

            public IntPtr hWnd
            {
                get
                {
                    return _hWnd;
                }

                set
                {
                    _hWnd = value;
                }
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct EnumWindowsContext_filePath : IWindowHandleAccess
        {
            public string filePath;
            private IntPtr _hWnd;

            public IntPtr hWnd
            {
                get
                {
                    return _hWnd;
                }

                set
                {
                    _hWnd = value;
                }
            }
        }

        public static bool EnumWindowProcImpl_filePath(IntPtr hWnd, IntPtr pContext)
        {
            bool bContinueEnumerating = true;

            // Marshall context back to managed
            EnumWindowsContext_filePath context = new EnumWindowsContext_filePath();
            context = (EnumWindowsContext_filePath)Marshal.PtrToStructure(pContext, typeof(EnumWindowsContext_filePath));

            // Check if filePath is the text in the window title bar
            if (IsTitleBar(hWnd, context.filePath))
            {
                Log.InfoFormat("{0}() - found!", MethodBase.GetCurrentMethod().Name);
                context.hWnd = hWnd;

                // Marshall changed data back to unmanaged
                Marshal.StructureToPtr(context, pContext, false);
                bContinueEnumerating = false;
            }

            return bContinueEnumerating;
        }

        public static bool EnumWindowProcImpl_pid_filename(IntPtr hWnd, IntPtr pContext)
        {
            bool bContinueEnumerating = true;

            int currentPid = 0;
            GetWindowThreadProcessId(hWnd, ref currentPid);

            // Marshall context back to managed
            EnumWindowsContext_pid_filename context = new EnumWindowsContext_pid_filename();
            context = (EnumWindowsContext_pid_filename)Marshal.PtrToStructure(pContext, typeof(EnumWindowsContext_pid_filename));

            if (currentPid == context.targetPid)
            {
                Log.InfoFormat("{0}() - currentpid = {1} vs. targetPid = {2} hwnd = {3}", MethodBase.GetCurrentMethod().Name, currentPid, context.targetPid, hWnd.ToString("X"));
                
                // Check if fileName is in window title bar
                if (IsFilenameInTitleBar(hWnd, context.filePath))
                {
                    Log.InfoFormat("{0}() - found!", MethodBase.GetCurrentMethod().Name);
                    context.hWnd = hWnd;

                    // Marshall changed data back to unmanaged
                    Marshal.StructureToPtr(context, pContext, false);
                    bContinueEnumerating = false;
                }
            }

            return bContinueEnumerating;
        }

        public static bool IsFilenameInTitleBar(IntPtr hWnd, string filePath)
        {
            bool bFound = false;
            int maxTextSize = 256;

            StringBuilder titleBarText = new StringBuilder(maxTextSize);

            if (GetWindowText(hWnd, titleBarText, maxTextSize) > 0)
            {
                string fileName = Path.GetFileName(filePath);

                if (fileName.Length > 0)
                {
                    if (titleBarText.ToString().ToLower().IndexOf(fileName.ToLower()) != -1)
                    {
                        bFound = true;
                    }
                }
            }

            return bFound;
        }

        public static bool IsTitleBar(IntPtr hWnd, string filePath)
        {
            bool bFound = false;
            int maxTextSize = 256;

            StringBuilder titleBarText = new StringBuilder(maxTextSize);

            if (GetWindowText(hWnd, titleBarText, maxTextSize) > 0)
            {
                if (filePath.Length > 0)
                {
                    if (titleBarText.ToString().ToLower().CompareTo(filePath.ToLower()) == 0)
                    {
                        bFound = true;
                    }
                }
            }

            return bFound;
        }

        public static string GetProcessName(IntPtr hWnd)
        {
            int currentPid = 0;
            Win32Functions.GetWindowThreadProcessId(hWnd, ref currentPid);

            Process process;
            try
            {
                process = Process.GetProcessById(currentPid);
            }
            catch (ArgumentException e)
            {
                // This is thrown when Process.GetProcessById cannot find the process
                throw e;
            }

            return process.ProcessName;
        }

        /// <summary>
        /// Returns the .Net Process Object that owns the passed in hWnd
        /// </summary>
        /// <param name="hWnd">handle to a Window</param>
        /// <returns>a .Net Process or throws an error</returns>
        public static Process GetProcessFromHandle(IntPtr hWnd)
        {
            try
            {
                int currentPid = 0;
                Win32Functions.GetWindowThreadProcessId(hWnd, ref currentPid);

                Process process;            
                process = Process.GetProcessById(currentPid);
                return process;
            }
            catch (ArgumentException e)
            {
                // This is thrown when Process.GetProcessById cannot find the process
                throw e;
            }            
        }

        /// <summary>
        /// Attaches to instance of COM object matching progid
        /// and returns an object.  Client must cast and know 
        /// expected type.
        /// </summary>
        public static Object GetCOMObject(string progId)
        {
            Object app = null;
            try
            {
                app = Marshal.GetActiveObject(progId);
            }
            catch (SystemException e)
            {
                string d = e.ToString();
            }

            return app;
        }

        public struct RunningObject
        {
            public string name;
            public object o;
        }

        /// <summary>
        /// Use this to Get All Running Objects in the ROT
        /// </summary>
        public static List<RunningObject> GetRunningObjects()
        {
            // Get the table.
            var res = new List<RunningObject>();
            IBindCtx bc;

            CreateBindCtx(0, out bc);
            IRunningObjectTable runningObjectTable;

            bc.GetRunningObjectTable(out runningObjectTable);
            IEnumMoniker monikerEnumerator;
            runningObjectTable.EnumRunning(out monikerEnumerator);
            monikerEnumerator.Reset();

            // Enumerate and fill our nice dictionary.
            IMoniker[] monikers = new IMoniker[1];
            IntPtr numFetched = IntPtr.Zero;

            while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
            {
                RunningObject running;
                monikers[0].GetDisplayName(bc, null, out running.name);
                runningObjectTable.GetObject(monikers[0], out running.o);
                res.Add(running);                
            }
            return res;            
        }

        /// <summary>
        /// Use this to Get A specific type of Object from the ROT
        /// </summary>
        public static List<T> GetRunningObjectsOfType<T>()
        {
            // Get the table.
            var res = new List<T>();
            IBindCtx bc;

            CreateBindCtx(0, out bc);
            IRunningObjectTable runningObjectTable;

            bc.GetRunningObjectTable(out runningObjectTable);
            IEnumMoniker monikerEnumerator;
            runningObjectTable.EnumRunning(out monikerEnumerator);
            monikerEnumerator.Reset();

            // Enumerate and fill our nice dictionary.
            IMoniker[] monikers = new IMoniker[1];
            IntPtr numFetched = IntPtr.Zero;

            while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
            {
                object o;
                runningObjectTable.GetObject(monikers[0], out o);

                if (o is T)
                {
                    res.Add((T) o);
                }
                o = null;
            }
            return res; 
        }

    }
}
