File: MS\Internal\AutomationProxies\Misc.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\UIAutomation\UIAutomationClientSideProviders\UIAutomationClientSideProviders.csproj (UIAutomationClientSideProviders)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
// Description: Miscellaneous helper routines
 
 
// PRESHARP: In order to avoid generating warnings about unkown message numbers and unknown pragmas.
#pragma warning disable 1634, 1691
 
using Microsoft.Win32.SafeHandles;
using MS.Win32;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Automation;
using System.Windows.Automation.Provider;
using System.Windows;
using System.Windows.Input;
using System.Diagnostics;
using NativeMethodsSetLastError = MS.Internal.UIAutomationClientSideProviders.NativeMethodsSetLastError;
 
namespace MS.Internal.AutomationProxies
{
    static class Misc
    {
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        //
        //  HrGetWindowShortcut()
        //
        internal static string AccessKey(string s)
        {
            // Get the index of the shortcut
            int iPosShortCut = s.IndexOf('&');
 
            // Did we found an & or is it at the end of the string
            if (iPosShortCut < 0 || iPosShortCut + 1 >= s.Length)
            {
                return null;
            }
 
            // Build the result string
            return SR.KeyAlt + "+" + s[iPosShortCut + 1];
        }
 
        // Extend an existing RunTimeID by one element
        internal static int[] AppendToRuntimeId(int[] baseID, int id)
        {
            // For the base case, where parent is a hwnd, baseID will be null,
            // so use AppendRuntimeId instead. UIA will then glue that to the ID
            // of the parent HWND.
            if(baseID == null)
                baseID = new int[] { AutomationInteropProvider.AppendRuntimeId };
 
            int len = baseID.Length;
            int[] newID = new int[len + 1];
 
            baseID.CopyTo(newID, 0);
            newID[len] = id;
            return newID;
        }
 
        internal static double[] RectArrayToDoubleArray(Rect[] rectArray)
        {
            if (rectArray == null)
                return null;
            double[] doubles = new double[rectArray.Length * 4];
            int scan = 0;
            for (int i = 0; i < rectArray.Length; i++)
            {
                doubles[scan++] = rectArray[i].X;
                doubles[scan++] = rectArray[i].Y;
                doubles[scan++] = rectArray[i].Width;
                doubles[scan++] = rectArray[i].Height;
            }
            return doubles;
        }
 
        // Ensure a window and all its parents are enabled.
        // If not, throw ElementNotEnabledException.
        internal static void CheckEnabled(IntPtr hwnd)
        {
            if (!IsEnabled(hwnd))
            {
                throw new ElementNotEnabledException();
            }
        }
 
        // Checks to see if the process owning the hwnd is currently in menu mode
        // and takes steps to exit menu mode if it is
        internal static void ClearMenuMode()
        {
            // Check if we're in menu mode with helper method.
            if (InMenuMode())
            {
                // If we are, send an alt keypress to escape
                Input.SendKeyboardInput(Key.LeftAlt, true);
                Input.SendKeyboardInput(Key.LeftAlt, false);
 
                // Wait for a few milliseconds for this operation to be completed
                long dwTicks = (long)Environment.TickCount;
 
                // Wait until the action has been completed
                while (InMenuMode() && ((long)Environment.TickCount - dwTicks) < MenuTimeOut)
                {
                    // Sleep the shortest amount of time possible while still guaranteeing that some sleep occurs
                    System.Threading.Thread.Sleep(1);
                }
            }
        }
 
        internal static bool CloseHandle(IntPtr processHandle)
        {
            bool result = UnsafeNativeMethods.CloseHandle(processHandle);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        // Compares 2 raw elements and returns true if equal, false otherwise
        internal static bool Compare(ProxySimple el1, ProxySimple el2)
        {
            int[] a1 = el1.GetRuntimeId();
            int[] a2 = el2.GetRuntimeId();
            int l = a1.Length;
 
            if (l != a2.Length)
                return false;
 
            for (int i = 0; i < l; i++)
            {
                if (a1[i] != a2[i])
                {
                    return false;
                }
            }
 
            return true;
        }
 
        internal static IntPtr DispatchMessage(ref NativeMethods.MSG msg)
        {
            // From the Windows SDK documentation:
            // The return value specifies the value returned by the window procedure.
            // Although its meaning depends on the message being dispatched, the return
            // value generally is ignored.
#pragma warning suppress 6031, 6523
            return UnsafeNativeMethods.DispatchMessage(ref msg);
        }
 
 
        internal unsafe static bool EnumChildWindows(IntPtr hwnd, NativeMethods.EnumChildrenCallbackVoid lpEnumFunc, void* lParam)
        {
            bool result = UnsafeNativeMethods.EnumChildWindows(hwnd, lpEnumFunc, lParam);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string className, string wndName)
        {
            IntPtr result = NativeMethodsSetLastError.FindWindowEx(hwndParent, hwndChildAfter, className, wndName);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == IntPtr.Zero)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static string GetClassName(IntPtr hwnd)
        {
            StringBuilder sb = new StringBuilder(NativeMethods.MAX_PATH + 1);
 
            int result = UnsafeNativeMethods.GetClassName(hwnd, sb, NativeMethods.MAX_PATH);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
                return "";
            }
 
            return sb.ToString();
        }
 
        // Get the name of a control and conditionally strip mnemonic.
        // label is the hwnd of the control that is funtioning as the label.  Use GetLabelhwnd to find this.
        // If stripMnemonic is true, amperstrands characters will be stripped out.
        internal static string GetControlName(IntPtr label, bool stripMnemonic)
        {
            if (label == IntPtr.Zero)
            {
                return null;
            }
 
            StringBuilder sb = new StringBuilder(MaxLengthNameProperty);
 
            int result = NativeMethodsSetLastError.GetWindowText(label, sb, MaxLengthNameProperty);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
                return null;
            }
 
            return stripMnemonic ? StripMnemonic(sb.ToString()) : sb.ToString();
 
        }
 
        internal static bool GetClientRectInScreenCoordinates(IntPtr hwnd, ref NativeMethods.Win32Rect rc)
        {
            rc = NativeMethods.Win32Rect.Empty;
 
            if (!GetClientRect(hwnd, ref rc))
            {
                return false;
            }
 
            NativeMethods.Win32Point leftTop = new NativeMethods.Win32Point(rc.left, rc.top);
            if (!MapWindowPoints(hwnd, IntPtr.Zero, ref leftTop, 1))
            {
                return false;
            }
 
            NativeMethods.Win32Point rightBottom = new NativeMethods.Win32Point(rc.right, rc.bottom);
            if (!MapWindowPoints(hwnd, IntPtr.Zero, ref rightBottom, 1))
            {
                return false;
            }
 
            rc = new NativeMethods.Win32Rect(leftTop.x, leftTop.y, rightBottom.x, rightBottom.y);
            return true;
        }
 
        internal static bool GetClientRect(IntPtr hwnd, ref NativeMethods.Win32Rect rc)
        {
            bool result = UnsafeNativeMethods.GetClientRect(hwnd, ref rc);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            // When the control is right to left GetClentRect() will return a rectangle with left > right.
            // Normalize thesee rectangle back to left to right.
            rc.Normalize(IsLayoutRTL(hwnd));
 
            return result;
        }
 
        internal static bool GetComboBoxInfo(IntPtr hwnd, ref NativeMethods.COMBOBOXINFO cbi)
        {
            bool result = UnsafeNativeMethods.GetComboBoxInfo(hwnd, ref cbi);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool GetCursorPos(ref NativeMethods.Win32Point pt)
        {
            // Vista and beyond use GetPhysicalCursorPos which handles DPI issues
            bool result = (System.Environment.OSVersion.Version.Major >= 6) ? UnsafeNativeMethods.GetPhysicalCursorPos(ref pt)
                                                                            : UnsafeNativeMethods.GetCursorPos(ref pt);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
 
        internal static IntPtr GetDC(IntPtr hwnd)
        {
            IntPtr hdc = UnsafeNativeMethods.GetDC(hwnd);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (hdc == IntPtr.Zero)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return hdc;
        }
 
        internal static IntPtr GetFocusedWindow()
        {
            NativeMethods.GUITHREADINFO gui;
 
            return ProxyGetGUIThreadInfo(0, out gui) ? gui.hwndFocus : IntPtr.Zero;
        }
 
        internal static string GetItemToolTipText(IntPtr hwnd, IntPtr hwndToolTip, int item)
        {
            if (hwndToolTip != IntPtr.Zero)
            {
                // We've found the tooltip window, so we won't need to scan for it.
 
                // Got a tooltip window - use it.
                NativeMethods.TOOLINFO tool = new NativeMethods.TOOLINFO();
                tool.Init(Marshal.SizeOf(typeof(NativeMethods.TOOLINFO)));
 
                tool.hwnd = hwnd;
                tool.uId = item;
 
                return XSendMessage.GetItemText(hwndToolTip, tool);
            }
            else
            {
                // Control doesn't know its tooltip window - instead scan for one...
 
                // Enum the top-level windows owned by this thread...
                uint processId;
                uint threadId = GetWindowThreadProcessId(hwnd, out processId);
 
                UnsafeNativeMethods.ENUMTOOLTIPWINDOWINFO info = new UnsafeNativeMethods.ENUMTOOLTIPWINDOWINFO();
                info.hwnd = hwnd;
                info.id = item;
                info.name = "";
 
                UnsafeNativeMethods.EnumThreadWndProc enumToolTipWindows = new UnsafeNativeMethods.EnumThreadWndProc(EnumToolTipWindows);
                GCHandle gch = GCHandle.Alloc(enumToolTipWindows);
                UnsafeNativeMethods.EnumThreadWindows(threadId, enumToolTipWindows, ref info);
                gch.Free();
 
                return info.name;
            }
        }
 
        // --------------------------------------------------------------------------
        //
        //  GetLabelhwnd()
        //
        //  This walks backwards among peer windows to find a static field.  It stops
        //  if it gets to the front or hits a group/tabstop, just like the dialog
        //  manager does.
        //
        // Ported from OleAcc\Client.CPP
        // --------------------------------------------------------------------------
 
        internal static IntPtr GetLabelhwnd(IntPtr hwnd)
        {
            // Sanity check
            if (!UnsafeNativeMethods.IsWindow(hwnd))
            {
                return IntPtr.Zero;
            }
 
            // Only get labels for child windows - not top-level windows or desktop
            IntPtr hwndParent = Misc.GetParent(hwnd);
            if (hwndParent == IntPtr.Zero || hwndParent == UnsafeNativeMethods.GetDesktopWindow())
            {
                return IntPtr.Zero;
            }
 
            IntPtr peer = hwnd;
 
            // If GetWindow fails we're going to exit, no need to call Marshal.GetLastWin32Error
#pragma warning suppress 56523
            while ((peer = NativeMethodsSetLastError.GetWindow(peer, NativeMethods.GW_HWNDPREV)) != IntPtr.Zero)
            {
                //
                // Is this a static dude?
                //
                int code = Misc.ProxySendMessageInt(peer, NativeMethods.WM_GETDLGCODE, IntPtr.Zero, IntPtr.Zero);
                if ((code & NativeMethods.DLGC_STATIC) == NativeMethods.DLGC_STATIC)
                {
                    //
                    // Great, we've found our label.
                    //
                    return peer;
                }
 
                //
                // Skip invisible controls.
                // Note that we do this after checking if its a static, so that we give invisible statics a chance.
                // Using invisible statics is an easy workaround to add names to controls without changing the visual UI.
                //
                // If GetWindowLong fails we're going to exit, no need to call Marshal.GetLastWin32Error
#pragma warning suppress 56523
                int error = 0;
                int style = UnsafeNativeMethods.GetWindowLong(peer, NativeMethods.GWL_STYLE, out error);
                if ((style & NativeMethods.WS_VISIBLE) != 0)
                    continue;
 
                //
                // Is this a tabstop or group?  If so, bail out now.
                //
                if ((style & (NativeMethods.WS_GROUP | NativeMethods.WS_TABSTOP)) != 0)
                    break;
            }
 
            // Failed to find a suitable peer
            return IntPtr.Zero;
        }
 
        internal static bool GetMenuBarInfo(IntPtr hwnd, int idObject, uint item, ref NativeMethods.MENUBARINFO mbi)
        {
            bool result = NativeMethodsSetLastError.GetMenuBarInfo(hwnd, idObject, item, ref mbi);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static int GetMenuItemCount(IntPtr hmenu)
        {
            int count = UnsafeNativeMethods.GetMenuItemCount(hmenu);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (count == -1)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return count;
        }
 
        internal static bool GetMenuItemInfo(IntPtr hmenu, int item, bool byPosition, ref NativeMethods.MENUITEMINFO menuItemInfo)
        {
            bool result = UnsafeNativeMethods.GetMenuItemInfo(hmenu, item, byPosition, ref menuItemInfo);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool GetMenuItemRect(IntPtr hwnd, IntPtr hmenu, int item, out NativeMethods.Win32Rect rc)
        {
            bool result = UnsafeNativeMethods.GetMenuItemRect(hwnd, hmenu, item, out rc);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool GetMessage(ref NativeMethods.MSG msg, IntPtr hwnd, int msgFilterMin, int msgFilterMax)
        {
            int result = UnsafeNativeMethods.GetMessage(ref msg, hwnd, msgFilterMin, msgFilterMax);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            bool success = (result != 0 && result != -1);
            if (!success)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return success;
        }
 
        internal static int GetObjectW(IntPtr hObject, int size, ref NativeMethods.LOGFONT lf)
        {
            int result = UnsafeNativeMethods.GetObjectW(hObject, size, ref lf);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static IntPtr GetParent(IntPtr hwnd)
        {
            IntPtr hwndParent = NativeMethodsSetLastError.GetAncestor(hwnd, NativeMethods.GA_PARENT);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (hwndParent == IntPtr.Zero)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return hwndParent;
        }
 
        internal static bool GetScrollBarInfo(IntPtr hwnd, int fnBar, ref NativeMethods.ScrollBarInfo sbi)
        {
            bool result = UnsafeNativeMethods.GetScrollBarInfo(hwnd, fnBar, ref sbi);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool GetScrollInfo(IntPtr hwnd, int fnBar, ref NativeMethods.ScrollInfo si)
        {
            bool result = UnsafeNativeMethods.GetScrollInfo(hwnd, fnBar, ref si);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                // 1447     ERROR_NO_SCROLLBARS     The window does not have scroll bars.
                // If GetScrollInfo() fails with ERROR_NO_SCROLLBARS then there is no scroll information
                // to get.  Just return false saying that GetScrollInfo() could not get the information
                if (lastWin32Error == 1447)
                {
                    return false;
                }
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static int GetTextExtentPoint32(IntPtr hdc, string text, int length, out NativeMethods.SIZE size)
        {
            int result = NativeMethodsSetLastError.GetTextExtentPoint32(hdc, text, length, out size);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        // Calls a message that retrieves a string, but doesn't take a length argument, in a relatively safe manner.
        // Attempts to rapidly resize in hopes of crashing UI Automation should just crash whatever is rapidly resizing.
        // Param "hwnd" the Window Handle
        // Param "uMsg" the Windows Message
        // Param "wParam" the Windows wParam
        // Param "maxLength" the size of the string
        internal static unsafe string GetUnsafeText(IntPtr hwnd, int uMsg, IntPtr wParam, int maxLength)
        {
            uint pageSize = GetPageSize();
            IntPtr memAddr = IntPtr.Zero;     // Ptr to remote mem
            // calculate the size needed for the string
            uint cbSize = (uint)((maxLength + 1) * sizeof(char));
            // resize it to include enough pages for the string, and an extra guarding page, and one extra page to account for shifts.
            cbSize = ((cbSize / pageSize) + 3) * pageSize;
 
            try
            {
                // Allocate the space
                memAddr = VirtualAlloc(IntPtr.Zero, new UIntPtr(cbSize), UnsafeNativeMethods.MEM_COMMIT, UnsafeNativeMethods.PAGE_READWRITE);
 
                // Allocate the Final page as No Access, so any attempt to write to it will GPF
                VirtualAlloc(new IntPtr((byte *)memAddr.ToPointer() + cbSize - pageSize), new UIntPtr(pageSize), UnsafeNativeMethods.MEM_COMMIT, UnsafeNativeMethods.PAGE_NOACCESS);
 
                // Send the message...
                if (ProxySendMessage(hwnd, uMsg, wParam, memAddr) == IntPtr.Zero)
                {
                    return "";
                }
 
                String str = new string((char*)memAddr.ToPointer(), 0, maxLength);
                // Note: lots of "old world" strings are null terminated
                // Leaving the null termination in the System.String may lead
                // to some issues when used with the StringBuilder
                int nullTermination = str.IndexOf('\0');
 
                if (-1 != nullTermination)
                {
                    // We need to strip null terminated char and everything behind it from the str
                    str = str.Remove(nullTermination, maxLength - nullTermination);
                }
                return str;
            }
            finally
            {
                // Free the memory
                if (memAddr != IntPtr.Zero)
                {
                    VirtualFree(memAddr, UIntPtr.Zero, UnsafeNativeMethods.MEM_RELEASE);
                }
            }
        }
 
        internal static IntPtr GetWindow(IntPtr hwnd, int cmd)
        {
            IntPtr resultHwnd = NativeMethodsSetLastError.GetWindow(hwnd, cmd);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (resultHwnd == IntPtr.Zero)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return resultHwnd;
        }
 
        // Gets the extended style of the window
        internal static int GetWindowExStyle(IntPtr hwnd)
        {
            int lastWin32Error = 0;
            int exstyle = UnsafeNativeMethods.GetWindowLong(hwnd, NativeMethods.GWL_EXSTYLE, out lastWin32Error);
 
            if (exstyle == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return exstyle;
        }
 
        // Gets the id of the window
        internal static int GetWindowId(IntPtr hwnd)
        {
            int lastWin32Error = 0;
            int id = UnsafeNativeMethods.GetWindowLong(hwnd, NativeMethods.GWL_ID, out lastWin32Error);
 
            if (id == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return id;
        }
 
        // Gets the parent of the window
        internal static IntPtr GetWindowParent(IntPtr hwnd)
        {
            // NOTE: This may have issues in 64-bit.
 
            int lastWin32Error = 0;
            int result = UnsafeNativeMethods.GetWindowLong(hwnd, NativeMethods.GWL_HWNDPARENT, out lastWin32Error);
 
            if (result == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return (IntPtr)result;
        }
 
        internal static bool GetWindowRect(IntPtr hwnd, ref NativeMethods.Win32Rect rc)
        {
            bool result = UnsafeNativeMethods.GetWindowRect(hwnd, ref rc);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
            return result;
        }
 
        // Gets the style of the window
        internal static int GetWindowStyle(IntPtr hwnd)
        {
            int lastWin32Error = 0;
            int style = UnsafeNativeMethods.GetWindowLong(hwnd, NativeMethods.GWL_STYLE, out lastWin32Error);
 
            if (style == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return style;
        }
 
        internal static uint GetWindowThreadProcessId(IntPtr hwnd, out uint processId)
        {
            // GetWindowThreadProcessId does use SetLastError().  So a call to GetLastError() would be meanless.
            // Disabling the PreSharp warning.
#pragma warning suppress 6523
            uint threadId = UnsafeNativeMethods.GetWindowThreadProcessId(hwnd, out processId);
 
            if (threadId == 0)
            {
                throw new ElementNotAvailableException();
            }
 
            return threadId;
        }
 
        internal static short GlobalAddAtom(string atomName)
        {
            short atom = UnsafeNativeMethods.GlobalAddAtom(atomName);
            int lastWin32Error = Marshal.GetLastWin32Error();
            if (atom == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
            return atom;
        }
 
        internal static short GlobalDeleteAtom(short atom)
        {
            short result = NativeMethodsSetLastError.GlobalDeleteAtom(atom);
            ThrowWin32ExceptionsIfError(Marshal.GetLastWin32Error());
            return result;
        }
 
        // detect if we're in the menu mode
        internal static bool InMenuMode()
        {
            NativeMethods.GUITHREADINFO gui;
            return (ProxyGetGUIThreadInfo(0, out gui) && (IsBitSet(gui.dwFlags, NativeMethods.GUI_INMENUMODE)));
        }
 
        internal static bool IsBitSet(int flags, int bit)
        {
            return (flags & bit) == bit;
        }
 
        // Check if window is really enabled, taking parent state into account.
        internal static bool IsEnabled(IntPtr hwnd)
        {
            // Navigate up parent chain. If any ancestor window is
            // not enabled, then that has the effect of disabling this window.
            // All ancestor windows must be enabled for this window to be enabled.
            for (; ; )
            {
                if (!SafeNativeMethods.IsWindowEnabled(hwnd))
                {
                    return false;
                }
 
                hwnd = NativeMethodsSetLastError.GetAncestor(hwnd, NativeMethods.GA_PARENT);
                if (hwnd == IntPtr.Zero)
                {
                    return true;
                }
            }
        }
 
        internal static bool IsControlRTL(IntPtr hwnd)
        {
            int exStyle = GetWindowExStyle(hwnd);
            return IsBitSet(exStyle, NativeMethods.WS_EX_LAYOUTRTL) || IsBitSet(exStyle, NativeMethods.WS_EX_RTLREADING);
        }
 
        internal static bool IsLayoutRTL(IntPtr hwnd)
        {
            return IsBitSet(GetWindowExStyle(hwnd), NativeMethods.WS_EX_LAYOUTRTL);
        }
 
        internal static bool IsReadingRTL(IntPtr hwnd)
        {
            return IsBitSet(GetWindowExStyle(hwnd), NativeMethods.WS_EX_RTLREADING);
        }
 
        internal static bool IntersectRect(ref NativeMethods.Win32Rect rcDest, ref NativeMethods.Win32Rect rc1, ref NativeMethods.Win32Rect rc2)
        {
            bool result = SafeNativeMethods.IntersectRect(ref rcDest, ref rc1, ref rc2);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        // Call IsCriticalException w/in a catch-all-exception handler to allow critical exceptions
        // to be thrown (this is copied from exception handling code in WinForms but feel free to
        // add new critical exceptions).  Usage:
        //      try
        //      {
        //          Somecode();
        //      }
        //      catch (Exception e)
        //      {
        //          if (Misc.IsCriticalException(e))
        //              throw;
        //          // ignore non-critical errors from external code
        //      }
        internal static bool IsCriticalException(Exception e)
        {
            return e is NullReferenceException || e is StackOverflowException || e is OutOfMemoryException || e is System.Threading.ThreadAbortException;
        }
 
        // this is to determine is an item is visible.  The assumption is that the items here are not hwnds
        // and that they are clipped by there parent.  For example this is called by the WindowsListBox.
        // In that case the hwnd is the list box and the itemRect would be a list item this code checks to see
        // if the item is scrolled out of view.
        static internal bool IsItemVisible(IntPtr hwnd, ref NativeMethods.Win32Rect itemRect)
        {
            NativeMethods.Win32Rect clientRect = new NativeMethods.Win32Rect(0, 0, 0, 0);
            if (!GetClientRectInScreenCoordinates(hwnd, ref clientRect))
                return false;
 
            NativeMethods.Win32Rect intersection = new NativeMethods.Win32Rect(0, 0, 0, 0);
 
            // Returns true if the passed in itemRect overlaps with the client area of the hwnd this API
            // does not modify clientRect or itemRect
            return IntersectRect(ref intersection, ref clientRect, ref itemRect);
        }
 
        static internal bool IsItemVisible(ref NativeMethods.Win32Rect parentRect, ref NativeMethods.Win32Rect itemRect)
        {
            NativeMethods.Win32Rect intersection = new NativeMethods.Win32Rect(0, 0, 0, 0);
 
            // Returns true if the passed in itemRect overlaps with the client area of the hwnd this API
            // does not modify clientRect or itemRect
            return IntersectRect(ref intersection, ref parentRect, ref itemRect);
        }
 
        static internal bool IsItemVisible(ref NativeMethods.Win32Rect parentRect, ref Rect itemRect)
        {
            NativeMethods.Win32Rect itemRc = new NativeMethods.Win32Rect(itemRect);
            NativeMethods.Win32Rect intersection = new NativeMethods.Win32Rect(0, 0, 0, 0);
 
            // Returns true if the passed in itemRect overlaps with the client area of the hwnd this API
            // does not modify clientRect or itemRect
            return IntersectRect(ref intersection, ref parentRect, ref itemRc);
        }
 
        static internal bool IsItemVisible(ref Rect parentRect, ref NativeMethods.Win32Rect itemRect)
        {
            NativeMethods.Win32Rect parentRc = new NativeMethods.Win32Rect(parentRect);
            NativeMethods.Win32Rect intersection = new NativeMethods.Win32Rect(0, 0, 0, 0);
 
            // Returns true if the passed in itemRect overlaps with the client area of the hwnd this API
            // does not modify clientRect or itemRect
            return IntersectRect(ref intersection, ref parentRc, ref itemRect);
        }
 
        static internal bool IsItemVisible(ref Rect parentRect, ref Rect itemRect)
        {
            NativeMethods.Win32Rect itemRc = new NativeMethods.Win32Rect(itemRect);
            NativeMethods.Win32Rect parentRc = new NativeMethods.Win32Rect(parentRect);
            NativeMethods.Win32Rect intersection = new NativeMethods.Win32Rect(0, 0, 0, 0);
 
            // Returns true if the passed in itemRect overlaps with the client area of the hwnd this API
            // does not modify clientRect or itemRect
            return IntersectRect(ref intersection, ref parentRc, ref itemRc);
        }
 
        internal static bool IsProgmanWindow(IntPtr hwnd)
        {
            while (hwnd != IntPtr.Zero)
            {
                if (GetClassName(hwnd).CompareTo("Progman") == 0)
                {
                    return true;
                }
                hwnd = NativeMethodsSetLastError.GetAncestor(hwnd, NativeMethods.GA_PARENT);
            }
            return false;
        }
 
        internal static bool IsWow64Process(MS.Internal.AutomationProxies.SafeProcessHandle hProcess, out bool Wow64Process)
        {
            bool result = UnsafeNativeMethods.IsWow64Process(hProcess, out Wow64Process);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        // wrapper for MapWindowPoints
        internal static bool MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, ref NativeMethods.Win32Rect rect, int cPoints)
        {
            int mappingOffset = NativeMethodsSetLastError.MapWindowPoints(hWndFrom, hWndTo, ref rect, cPoints);
            int lastWin32Error = Marshal.GetLastWin32Error();
            if (mappingOffset == 0)
            {
                // When mapping points to/from Progman and its children MapWindowPoints may fail with error code 1400
                // Invalid Window Handle.  Since Progman is the desktop no mapping is need.
                if ((IsProgmanWindow(hWndFrom) && hWndTo == IntPtr.Zero) ||
                    (hWndFrom == IntPtr.Zero && IsProgmanWindow(hWndTo)))
                {
                    lastWin32Error = 0;
                }
 
                ThrowWin32ExceptionsIfError(lastWin32Error);
 
                // If the coordinates is at the origin a zero return is valid.
                // Use GetLastError() to check that. Error code 0 is "Operation completed successfull".
                return lastWin32Error == 0;
            }
 
            return true;
        }
 
        // wrapper for MapWindowPoints
        internal static bool MapWindowPoints(IntPtr hWndFrom, IntPtr hWndTo, ref NativeMethods.Win32Point pt, int cPoints)
        {
            int mappingOffset = NativeMethodsSetLastError.MapWindowPoints(hWndFrom, hWndTo, ref pt, cPoints);
            int lastWin32Error = Marshal.GetLastWin32Error();
            if (mappingOffset == 0)
            {
                // When mapping points to/from Progman and its children MapWindowPoints may fail with error code 1400
                // Invalid Window Handle.  Since Progman is the desktop no mapping is need.
                if ((IsProgmanWindow(hWndFrom) && hWndTo == IntPtr.Zero) ||
                    (hWndFrom == IntPtr.Zero && IsProgmanWindow(hWndTo)))
                {
                    lastWin32Error = 0;
                }
 
                ThrowWin32ExceptionsIfError(lastWin32Error);
 
                // If the coordinates is at the origin a zero return is valid.
                // Use GetLastError() to check that. Error code 0 is "Operation completed successfull".
                return lastWin32Error == 0;
            }
 
            return true;
        }
 
        // Move the mouse to the x, y location and perfoms a mouse clik
        // The mouse is then brough back to the original location.
        internal static void MouseClick(int x, int y)
        {
            MouseClick(x, y, false);
        }
 
        // Move the mouse to the x, y location and perfoms either
        // a single of double clik depending on the fDoubleClick parameter
        // The mouse is then brough back to the original location.
        internal static void MouseClick(int x, int y, bool fDoubleClick)
        {
            NativeMethods.Win32Point ptPrevious = new NativeMethods.Win32Point();
            bool fSetOldCursorPos = GetCursorPos(ref ptPrevious);
            bool mouseSwapped = UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_SWAPBUTTON) != 0;
 
            Input.SendMouseInput(x, y, 0, SendMouseInputFlags.Move | SendMouseInputFlags.Absolute);
 
            Input.SendMouseInput(0, 0, 0, mouseSwapped ? SendMouseInputFlags.RightDown : SendMouseInputFlags.LeftDown);
            Input.SendMouseInput(0, 0, 0, mouseSwapped ? SendMouseInputFlags.RightUp : SendMouseInputFlags.LeftUp);
 
            if (fDoubleClick)
            {
                Input.SendMouseInput(0, 0, 0, mouseSwapped ? SendMouseInputFlags.RightDown : SendMouseInputFlags.LeftDown);
                Input.SendMouseInput(0, 0, 0, mouseSwapped ? SendMouseInputFlags.RightUp : SendMouseInputFlags.LeftUp);
            }
 
            // toolbar items don't have time to proccess the mouse click if we move it back too soon
            // so wait a small amount of time to give them a chance.  A value of 10 made this work
            // on a 2gig dual proc machine so 50 should cover a slower machine.
            System.Threading.Thread.Sleep(50);
 
            // Set back the mouse position where it was
            if (fSetOldCursorPos)
            {
                Input.SendMouseInput(ptPrevious.x, ptPrevious.y, 0, SendMouseInputFlags.Move | SendMouseInputFlags.Absolute);
            }
        }
 
        internal static int MsgWaitForMultipleObjects(SafeWaitHandle handle, bool waitAll, int milliseconds, int wakeMask)
        {
            int terminationEvent, lastWin32Error;
            if (handle == null)
            {
                terminationEvent = UnsafeNativeMethods.MsgWaitForMultipleObjects(0, null, waitAll, milliseconds, wakeMask);
                lastWin32Error = Marshal.GetLastWin32Error();
            }
            else
            {
                bool fRelease = false;
                try
                {
                    handle.DangerousAddRef(ref fRelease);
                    IntPtr[] handles = { handle.DangerousGetHandle() };
                    terminationEvent = UnsafeNativeMethods.MsgWaitForMultipleObjects(1, handles, waitAll, milliseconds, wakeMask);
                    lastWin32Error = Marshal.GetLastWin32Error();
                }
                finally
                {
                    if (fRelease)
                    {
                        handle.DangerousRelease();
                    }
                }
            }
            if (terminationEvent == NativeMethods.WAIT_FAILED)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
            return terminationEvent;
        }
 
        internal static IntPtr OpenProcess(int flags, bool inherit, uint processId, IntPtr hwnd)
        {
            IntPtr processHandle = UnsafeNativeMethods.OpenProcess(flags, inherit, processId);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            // If we fail due to permission issues, if we're on vista, try the hooking technique
            // to access the process instead.
            if (processHandle == IntPtr.Zero
             && lastWin32Error == 5/*ERROR_ACCESS_DENIED*/
             && System.Environment.OSVersion.Version.Major >= 6)
            {
                try
                {
                    processHandle = UnsafeNativeMethods.GetProcessHandleFromHwnd(hwnd);
                    lastWin32Error = Marshal.GetLastWin32Error();
                }
                catch(EntryPointNotFoundException)
                {
                    // Ignore; until OLEACC propogates into Vista builds, the entry point may not be present.
                }
 
            }
 
            if (processHandle == IntPtr.Zero)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return processHandle;
        }
 
        // wrapper for PostMessage
        internal static void PostMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam)
        {
            bool result = UnsafeNativeMethods.PostMessage(hwnd, msg, wParam, lParam);
            int lastWin32Error = Marshal.GetLastWin32Error();
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
        }
 
        // Returns the Win32 Class Name for an hwnd
        internal static string ProxyGetClassName(IntPtr hwnd)
        {
            const int OBJID_QUERYCLASSNAMEIDX = unchecked(unchecked((int)0xFFFFFFF4));
            const int QUERYCLASSNAME_BASE = 65536;
 
            // Call ProxySendMessage ignoring the timeout
            // are there known bad hwnd that do not work with WM_GETOBJECT? We should investigate more with hwnd for
            // which this call time outs
            int index = ProxySendMessageInt(hwnd, NativeMethods.WM_GETOBJECT, IntPtr.Zero, (IntPtr)OBJID_QUERYCLASSNAMEIDX, true);
 
            if (index >= QUERYCLASSNAME_BASE && index - QUERYCLASSNAME_BASE < _asClassNames.Length)
            {
                return _asClassNames[index - QUERYCLASSNAME_BASE];
            }
            else
            {
                return  RealGetWindowClass(hwnd);
            }
        }
 
        // wrapper for GetGuiThreadInfo
        internal static bool ProxyGetGUIThreadInfo(uint idThread, out NativeMethods.GUITHREADINFO gui)
        {
            gui = new NativeMethods.GUITHREADINFO();
            gui.cbSize = Marshal.SizeOf(gui.GetType());
 
            bool result = UnsafeNativeMethods.GetGUIThreadInfo(idThread, ref gui);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                // If the focused thread is on another [secure] desktop, GetGUIThreadInfo
                // will fail with ERROR_ACCESS_DENIED - don't throw an exception for that case,
                // instead treat as a failure. Callers will treat this as though no window has
                // focus.
                if (lastWin32Error == 5 /*ERROR_ACCESS_DENIED*/)
                    return false;
 
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
#if _NEED_DEBUG_OUTPUT
            bool fCaretBlink = (gui.dwFlags & NativeMethods.GUI_CARETBLINKING) != 0;
            bool fMoveSize = (gui.dwFlags & NativeMethods.GUI_INMOVESIZE) != 0;
            bool fMenuMode = (gui.dwFlags & NativeMethods.GUI_INMENUMODE) != 0;
            bool fSystemMenuMode = (gui.dwFlags & NativeMethods.GUI_SYSTEMMENUMODE) != 0;
            bool fPopupMenuMode = (gui.dwFlags & NativeMethods.GUI_POPUPMENUMODE) != 0;
 
            StringBuilder sbFlag = new StringBuilder(NativeMethods.MAX_PATH);
            if (fCaretBlink)
            {
                sbFlag.Append("GUI_CARETBLINKING");
            }
            if (fMoveSize)
            {
                sbFlag.Append(sbFlag.Length > 0 ? " | GUI_INMOVESIZE" : "GUI_INMOVESIZE");
            }
            if (fMenuMode)
            {
                sbFlag.Append(sbFlag.Length > 0 ? " | GUI_INMENUMODE" : "GUI_INMENUMODE");
            }
            if (fSystemMenuMode)
            {
                sbFlag.Append(sbFlag.Length > 0 ? " | GUI_SYSTEMMENUMODE" : "GUI_SYSTEMMENUMODE");
            }
            if (fPopupMenuMode)
            {
                sbFlag.Append(sbFlag.Length > 0 ? " | GUI_POPUPMENUMODE" : "GUI_POPUPMENUMODE");
            }
 
            StringBuilder sb = new StringBuilder(NativeMethods.MAX_PATH);
            sb.Append("GUITHREADINFO \n\r{");
            sb.AppendFormat("\n\r\tcbSize = {0}", gui.cbSize);
            sb.AppendFormat("\n\r\tdwFlags = {0}", gui.dwFlags);
            if (sbFlag.Length > 0)
            {
                sb.Append(" (");
                sb.Append(sbFlag);
                sb.Append(')');
            }
            sb.AppendFormat("\n\r\thwndActive = 0x{0:x8}", gui.hwndActive.ToInt32());
            sb.AppendFormat("\n\r\thwndFocus = 0x{0:x8}", gui.hwndFocus.ToInt32());
            sb.AppendFormat("\n\r\thwndCapture = 0x{0:x8}", gui.hwndCapture.ToInt32());
            sb.AppendFormat("\n\r\thwndMenuOwner = 0x{0:x8}", gui.hwndMenuOwner.ToInt32());
            sb.AppendFormat("\n\r\thwndMoveSize = 0x{0:x8}", gui.hwndMoveSize.ToInt32());
            sb.AppendFormat("\n\r\thwndCaret = 0x{0:x8}", gui.hwndCaret.ToInt32());
            sb.AppendFormat("\n\r\trc = ({0}, {1}, {2}, {3})", gui.rc.left, gui.rc.top, gui.rc.right, gui.rc.bottom);
            sb.Append("\n\r}");
 
            System.Diagnostics.Debug.WriteLine(sb.ToString());
#endif
 
            return result;
        }
 
        // The name text based on the WM_GETTEXT message. The text is truncated to a predefined character
        // length.
        internal static string ProxyGetText(IntPtr hwnd)
        {
            return ProxyGetText(hwnd, MaxLengthNameProperty);
        }
 
        internal static string ProxyGetText(IntPtr hwnd, int length)
        {
            // if the length is zero don't bother asking for the text.
            if (length == 0)
            {
                return "";
            }
 
            // Length passes to SendMessage includes terminating NUL
            StringBuilder str = new StringBuilder(length + 1);
 
            // Send the message...
            ProxySendMessage(hwnd, NativeMethods.WM_GETTEXT, (IntPtr)str.Capacity, str);
 
            // We don't try to decifer between a zero length string and an error
            return str.ToString();
        }
 
        // wrapper for GetTitleBarInfo
        internal static bool ProxyGetTitleBarInfo(IntPtr hwnd, out UnsafeNativeMethods.TITLEBARINFO ti)
        {
            ti = new UnsafeNativeMethods.TITLEBARINFO();
            ti.cbSize = Marshal.SizeOf(ti.GetType());
 
            bool result = UnsafeNativeMethods.GetTitleBarInfo(hwnd, ref ti);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
                return false;
            }
            return true;
        }
 
        internal static bool ProxyGetTitleBarInfoEx(IntPtr hwnd, out UnsafeNativeMethods.TITLEBARINFOEX ti)
        {
            ti = new UnsafeNativeMethods.TITLEBARINFOEX();
            ti.cbSize = Marshal.SizeOf(ti.GetType());
            IntPtr result;
            IntPtr resultSendMessage = UnsafeNativeMethods.SendMessageTimeout(hwnd, NativeMethods.WM_GETTITLEBARINFOEX, IntPtr.Zero, ref ti, _sendMessageFlags, _sendMessageTimeoutValue, out result);
            int lastWin32Error = Marshal.GetLastWin32Error();
            if (resultSendMessage == IntPtr.Zero)
            {
                //Window owner failed to process the message WM_GETTITLEBARINFOEX
                EvaluateSendMessageTimeoutError(lastWin32Error);
            }
            return true;
        }
 
        // Return the bounding rects for titlebar items or null if they are invisible or offscreen
        internal static Rect [] GetTitlebarRects(IntPtr hwnd)
        {
            // Vista and beyond
            if (System.Environment.OSVersion.Version.Major >= 6)
            {
                return GetTitlebarRectsEx(hwnd);
            }
 
            // Up through XP
            return GetTitlebarRectsXP(hwnd);
        }
 
        internal static Rect GetTitleBarRect(IntPtr hwnd)
        {
            UnsafeNativeMethods.TITLEBARINFO ti;
            if (!Misc.ProxyGetTitleBarInfo(hwnd, out ti) || ti.rcTitleBar.IsEmpty)
            {
                return Rect.Empty;
            }
 
            NativeMethods.MENUBARINFO mbi;
            bool retValue = WindowsMenu.GetMenuBarInfo(hwnd, NativeMethods.OBJID_SYSMENU, 0, out mbi);
 
            int left = 0;
            int right = 0;
            if(Misc.IsControlRTL(hwnd))
            {
                // Possible that there is no menu
                left  = ti.rcTitleBar.left;
                right = (!retValue || mbi.rcBar.IsEmpty) ? ti.rcTitleBar.right : mbi.rcBar.right;
            }
            else
            {
                // Possible that there is no menu
                left  = (!retValue || mbi.rcBar.IsEmpty) ? ti.rcTitleBar.left : mbi.rcBar.left;
                right = ti.rcTitleBar.right;
            }
            return new Rect(left, ti.rcTitleBar.top, right - left, ti.rcTitleBar.bottom - ti.rcTitleBar.top);
        }
 
        internal static IntPtr ProxySendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam)
        {
            IntPtr result;
 
            IntPtr resultSendMessage = UnsafeNativeMethods.SendMessageTimeout(hwnd, msg, wParam, lParam, _sendMessageFlags, _sendMessageTimeoutValue, out result);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (resultSendMessage == IntPtr.Zero)
            {
                EvaluateSendMessageTimeoutError(lastWin32Error);
            }
 
            return result;
        }
 
        // On a 64-bit platform, the value of the IntPtr is too large to represent as a 32-bit signed integer.
        // An int is a System.Int32.  When an explicit cast of IntPtr to int is done on a 64-bit platform an
        // OverflowException will occur when the IntPtr value exceeds the range of int. In cases where using
        // SendMessage to get back int (e.g. an item index or an enum value), this version safely truncates
        // from IntPtr to int.
        internal static int ProxySendMessageInt(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam)
        {
            IntPtr result = ProxySendMessage(hwnd, msg, wParam, lParam);
            return unchecked((int)(long)result);
        }
 
        // Same as above but does not throw on timeout
        // This maybe a temp solution for quick unblock, be careful when using this method
        internal static IntPtr ProxySendMessage(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, bool ignoreTimeout)
        {
            IntPtr result;
 
            IntPtr resultSendMessage = UnsafeNativeMethods.SendMessageTimeout(hwnd, msg, wParam, lParam, _sendMessageFlags, _sendMessageTimeoutValue, out result);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (resultSendMessage == IntPtr.Zero)
            {
                EvaluateSendMessageTimeoutError(lastWin32Error, ignoreTimeout);
            }
 
            return result;
        }
 
        // On a 64-bit platform, the value of the IntPtr is too large to represent as a 32-bit signed integer.
        // An int is a System.Int32.  When an explicit cast of IntPtr to int is done on a 64-bit platform an
        // OverflowException will occur when the IntPtr value exceeds the range of int. In cases where using
        // SendMessage to get back int (e.g. an item index or an enum value), this version safely truncates
        // from IntPtr to int.
        internal static int ProxySendMessageInt(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, bool ignoreTimeout)
        {
            IntPtr result = ProxySendMessage(hwnd, msg, wParam, lParam, ignoreTimeout);
            return unchecked((int)(long)result);
        }
 
        internal static IntPtr ProxySendMessage(IntPtr hwnd, int msg, IntPtr wParam, StringBuilder sb)
        {
            IntPtr result;
 
            IntPtr resultSendMessage = UnsafeNativeMethods.SendMessageTimeout(hwnd, msg, wParam, sb, _sendMessageFlags, _sendMessageTimeoutValue, out result);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (resultSendMessage == IntPtr.Zero)
            {
                EvaluateSendMessageTimeoutError(lastWin32Error);
            }
 
            return result;
        }
 
        // On a 64-bit platform, the value of the IntPtr is too large to represent as a 32-bit signed integer.
        // An int is a System.Int32.  When an explicit cast of IntPtr to int is done on a 64-bit platform an
        // OverflowException will occur when the IntPtr value exceeds the range of int. In cases where using
        // SendMessage to get back int (e.g. an item index or an enum value), this version safely truncates
        // from IntPtr to int.
        internal static int ProxySendMessageInt(IntPtr hwnd, int msg, IntPtr wParam, StringBuilder sb)
        {
            IntPtr result = ProxySendMessage(hwnd, msg, wParam, sb);
            return unchecked((int)(long)result);
        }
 
        internal static IntPtr ProxySendMessage(IntPtr hwnd, int msg, IntPtr wParam, ref NativeMethods.Win32Rect lParam)
        {
            IntPtr result;
 
            IntPtr resultSendMessage = UnsafeNativeMethods.SendMessageTimeout(hwnd, msg, wParam, ref lParam, _sendMessageFlags, _sendMessageTimeoutValue, out result);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (resultSendMessage == IntPtr.Zero)
            {
                EvaluateSendMessageTimeoutError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static IntPtr ProxySendMessage(IntPtr hwnd, int msg, out int wParam, out int lParam)
        {
            IntPtr result;
 
            IntPtr resultSendMessage = UnsafeNativeMethods.SendMessageTimeout(hwnd, msg, out wParam, out lParam, _sendMessageFlags, _sendMessageTimeoutValue, out result);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (resultSendMessage == IntPtr.Zero)
            {
                EvaluateSendMessageTimeoutError(lastWin32Error);
            }
 
            return result;
        }
 
        // Check if a point is within the bounding Rect of a window
        internal static bool PtInRect(ref NativeMethods.Win32Rect rc, int x, int y)
        {
            return x >= rc.left && x < rc.right && y >= rc.top && y < rc.bottom;
        }
 
        internal static bool PtInRect(ref Rect rc, int x, int y)
        {
            return x >= rc.Left && x < rc.Right && y >= rc.Top && y < rc.Bottom;
        }
 
        // Check if a point is within the client Rect of a window
        internal static bool PtInWindowRect(IntPtr hwnd, int x, int y)
        {
            NativeMethods.Win32Rect rc = new NativeMethods.Win32Rect();
 
            if (!GetWindowRect(hwnd, ref rc))
            {
                return false;
            }
            return x >= rc.left && x < rc.right && y >= rc.top && y < rc.bottom;
        }
 
        internal static bool ReadProcessMemory(MS.Internal.AutomationProxies.SafeProcessHandle hProcess, IntPtr source, IntPtr dest, IntPtr size, out IntPtr bytesRead)
        {
            bool result = UnsafeNativeMethods.ReadProcessMemory(hProcess, source, dest, size, out bytesRead);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool ReadProcessMemory(MS.Internal.AutomationProxies.SafeProcessHandle hProcess, IntPtr source, MS.Internal.AutomationProxies.SafeCoTaskMem destAddress, IntPtr size, out IntPtr bytesRead)
        {
            bool result = UnsafeNativeMethods.ReadProcessMemory(hProcess, source, destAddress, size, out bytesRead);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        // Get the class name
        internal static string RealGetWindowClass(IntPtr hwnd)
        {
            System.Text.StringBuilder className = new System.Text.StringBuilder(NativeMethods.MAX_PATH + 1);
 
            uint result = UnsafeNativeMethods.RealGetWindowClass(hwnd, className, NativeMethods.MAX_PATH);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
                return "";
            }
 
            return className.ToString();
        }
 
        internal static bool RegisterHotKey(IntPtr hwnd, short atom, int modifiers, int vk)
        {
            bool result = UnsafeNativeMethods.RegisterHotKey(hwnd, atom, modifiers, vk);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static int ReleaseDC(IntPtr hwnd, IntPtr hdc)
        {
            // If ReleaseDC fails we will not do anything with that information so just ignore the
            // PRESHARP warnings.
#pragma warning suppress 6031, 6523
            return UnsafeNativeMethods.ReleaseDC(hwnd, hdc);
        }
 
        internal static int RegisterWindowMessage(string msg)
        {
            int result = SafeNativeMethods.RegisterWindowMessage(msg);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static IntPtr SelectObject(IntPtr hdc, IntPtr hObject)
        {
            // There is no indication in the Windows SDK documentation that SelectObject()
            // will set an error to be retrieved with GetLastError, so set the pragma to ignore
            // the PRESHARP warning.  Anyway if ReleaseDC() fails here, nothing more can be done
            // since the code is restoring the orginal object and discarding the temp object.
#pragma warning suppress 6031, 6523
            return UnsafeNativeMethods.SelectObject(hdc, hObject);
        }
 
        internal static int SendInput(int inputs, ref NativeMethods.INPUT ki, int size)
        {
            int eventCount = UnsafeNativeMethods.SendInput(inputs, ref ki, size);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (eventCount <= 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return eventCount;
        }
 
        // The Win32 call to SetFocus does not do it for hwnd that are
        // not in the same process as the caller.
        // This is implemented here to work around this behavior.
        // Fake keystroke are sent to a specific hwnd to force
        // Windows to give the focus to that hwnd.
        static internal bool SetFocus(IntPtr hwnd)
        {
            // First Check for ComboLBox
            // Because it uses Keystrokes it dismisses the ComboLBox
            string className = RealGetWindowClass(hwnd);
 
            if (className == "ComboLBox")
                return true;
 
 
            // If window is currently Disabled or Invisible no need
            // to continue
            if (!SafeNativeMethods.IsWindowVisible(hwnd) || !SafeNativeMethods.IsWindowEnabled(hwnd))
            {
                return false;
            }
 
            // If already focused, leave as-is. Calling SetForegroundWindow
            // on an already focused HWND will remove focus!
            if (GetFocusedWindow().Equals(hwnd))
            {
                return true;
            }
 
            // Try calling SetForegroundWindow directly first; it should succeed if we
            // already have the focus or have UIAccess
            if (UnsafeNativeMethods.SetForegroundWindow(hwnd))
            {
                return true;
            }
 
            // Use the hotkey technique:
            // Register a hotkey and send it to ourselves - this gives us the
            // input, and allows us to call SetForegroundWindow.
            short atom = GlobalAddAtom("FocusHotKey");
            if (atom == 0)
            {
                return false;
            }
            short vk = 0xB9;
            bool gotHotkey = false;
 
            for (int tries = 0; tries < 10; tries++)
            {
                if (RegisterHotKey(IntPtr.Zero, atom, 0, vk))
                {
                    gotHotkey = true;
                    break;
                }
 
                vk++; // try another key
            }
 
            if (gotHotkey)
            {
                // Get state of modifiers - and temporarilly release them...
                bool fShiftDown = (UnsafeNativeMethods.GetAsyncKeyState(UnsafeNativeMethods.VK_SHIFT) & unchecked((int)0x80000000)) != 0;
                bool fAltDown = (UnsafeNativeMethods.GetAsyncKeyState(UnsafeNativeMethods.VK_MENU) & unchecked((int)0x80000000)) != 0;
                bool fCtrlDown = (UnsafeNativeMethods.GetAsyncKeyState(UnsafeNativeMethods.VK_CONTROL) & unchecked((int)0x80000000)) != 0;
 
                if (fShiftDown)
                    Input.SendKeyboardInputVK(UnsafeNativeMethods.VK_SHIFT, false);
 
                if (fAltDown)
                    Input.SendKeyboardInputVK(UnsafeNativeMethods.VK_MENU, false);
 
                if (fCtrlDown)
                    Input.SendKeyboardInputVK(UnsafeNativeMethods.VK_CONTROL, false);
 
                Input.SendKeyboardInputVK(vk, true);
                Input.SendKeyboardInputVK(vk, false);
 
                // Restore release modifier keys...
                if (fShiftDown)
                    Input.SendKeyboardInputVK(UnsafeNativeMethods.VK_SHIFT, true);
 
                if (fAltDown)
                    Input.SendKeyboardInputVK(UnsafeNativeMethods.VK_MENU, true);
 
                if (fCtrlDown)
                    Input.SendKeyboardInputVK(UnsafeNativeMethods.VK_CONTROL, true);
 
                // Spin in this message loop until we get the hot key
                while (true)
                {
                    // If the hotkey input gets lost (eg due to desktop switch), GetMessage may not return -
                    // so use MsgWait first so we can timeout if there's no message present instead of blocking.
                    int result = MsgWaitForMultipleObjects(null, false, 2000, NativeMethods.QS_ALLINPUT);
                    if (result == NativeMethods.WAIT_FAILED || result == NativeMethods.WAIT_TIMEOUT)
                        break;
 
                    NativeMethods.MSG msg = new NativeMethods.MSG();
                    if (!GetMessage(ref msg, IntPtr.Zero, 0, 0))
                        break;
 
                    // TranslateMessage() will not set an error to be retrieved with GetLastError,
                    // so set the pragma to ignore the PERSHARP warning.
                    // the PERSHARP warning.
#pragma warning suppress 6031, 6523
                    UnsafeNativeMethods.TranslateMessage(ref msg);
 
                    // From the Windows SDK documentation:
                    // The return value specifies the value returned by the window procedure.
                    // Although its meaning depends on the message being dispatched, the return
                    // value generally is ignored.
#pragma warning suppress 6031, 6523
                    UnsafeNativeMethods.DispatchMessage(ref msg);
 
                    if (msg.message == NativeMethods.WM_HOTKEY && (short)msg.wParam == atom)
                    {
                        break;
                    }
                }
 
                UnregisterHotKey(IntPtr.Zero, atom);
            }
 
            GlobalDeleteAtom(atom);
 
            return UnsafeNativeMethods.SetForegroundWindow(hwnd);
        }
 
        internal static int SetScrollPos(IntPtr hwnd, int bar, int pos, bool redraw)
        {
            // NOTE: From Windows SDK Documentaion:
            // If the function succeeds, the return value is the previous position of the scroll
            // box.  If the desktop is themed and the parent window is a message-only window,
            // the function returns an incorrect value.
 
            int prevPos = NativeMethodsSetLastError.SetScrollPos(hwnd, bar, pos, redraw);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (prevPos == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return prevPos;
        }
 
        internal static IntPtr SetWinEventHook(int eventMin, int eventMax, IntPtr hmodWinEventProc, NativeMethods.WinEventProcDef WinEventReentrancyFilter, uint idProcess, uint idThread, int dwFlags)
        {
            // There is no indication in the Windows SDK documentation that SetWinEventHook()
            // will set an error to be retrieved with GetLastError, so set the pragma to ignore
            // the PERSHARP warning.
#pragma warning suppress 6523
            return UnsafeNativeMethods.SetWinEventHook(eventMin, eventMax, hmodWinEventProc, WinEventReentrancyFilter, idProcess, idThread, dwFlags);
        }
 
        // this strips the mnemonic prefix for short cuts as well as leading spaces
        // If we find && leave one & there.
        internal static string StripMnemonic(string s)
        {
            // If there are no spaces or & then it's ok just return it
            if (string.IsNullOrEmpty(s) || s.IndexOfAny(new char[2] { ' ', '&' }) < 0)
            {
                return s;
            }
 
            char[] ach = s.ToCharArray();
            bool amper = false;
            bool leadingSpace = false;
            int dest = 0;
 
            for (int source = 0; source < ach.Length; source++)
            {
                // get rid of leading spaces
                if (ach[source] == ' ' && leadingSpace == false)
                {
                    continue;
                }
                else
                {
                    leadingSpace = true;
                }
 
                // get rid of &
                if (ach[source] == '&' && amper == false)
                {
                    amper = true;
                }
                else
                {
                    ach[dest++] = ach[source];
                }
            }
 
            return new string(ach, 0, dest);
        }
 
        internal static void ThrowWin32ExceptionsIfError(int errorCode)
        {
            switch (errorCode)
            {
                case 0:     //    0 ERROR_SUCCESS                   The operation completed successfully.
                    // The error code indicates that there is no error, so do not throw an exception.
                    break;
 
                case 6:     //    6 ERROR_INVALID_HANDLE            The handle is invalid.
                case 1400:  // 1400 ERROR_INVALID_WINDOW_HANDLE     Invalid window handle.
                case 1401:  // 1401 ERROR_INVALID_MENU_HANDLE       Invalid menu handle.
                case 1402:  // 1402 ERROR_INVALID_CURSOR_HANDLE     Invalid cursor handle.
                case 1403:  // 1403 ERROR_INVALID_ACCEL_HANDLE      Invalid accelerator table handle.
                case 1404:  // 1404 ERROR_INVALID_HOOK_HANDLE       Invalid hook handle.
                case 1405:  // 1405 ERROR_INVALID_DWP_HANDLE        Invalid handle to a multiple-window position structure.
                case 1406:  // 1406 ERROR_TLW_WITH_WSCHILD          Cannot create a top-level child window.
                case 1407:  // 1407 ERROR_CANNOT_FIND_WND_CLASS     Cannot find window class.
                case 1408:  // 1408 ERROR_WINDOW_OF_OTHER_THREAD    Invalid window; it belongs to other thread.
                    throw new ElementNotAvailableException();
 
                // We're getting this in AMD64 when calling RealGetWindowClass; adding this code
                // to allow the DRTs to pass while we continue investigation.
                case 87:    //   87 ERROR_INVALID_PARAMETER
                    throw new ElementNotAvailableException();
 
                case 8:     //    8 ERROR_NOT_ENOUGH_MEMORY         Not enough storage is available to process this command.
                case 14:    //   14 ERROR_OUTOFMEMORY               Not enough storage is available to complete this operation.
                    throw new OutOfMemoryException();
 
                case 998:   //  998 ERROR_NOACCESS                  Invalid access to memory location.
                case 5:     //    5 ERROR_ACCESS_DENIED
                    throw new InvalidOperationException();
 
                case 121:   //  121 ERROR_SEM_TIMEOUT               The semaphore timeout period has expired.
                case 258:   //  258 WAIT_TIMEOUT                    The wait operation timed out.
                case 1053:  // 1053 ERROR_SERVICE_REQUEST_TIMEOUT   The service did not respond to the start or control request in a timely fashion.
                case 1460:  // 1460 ERROR_TIMEOUT                   This operation returned because the timeout period expired.
                    throw new TimeoutException();
 
                default:
                    // Not sure how to map the reset of the error codes so throw generic Win32Exception.
                    throw new Win32Exception(errorCode);
            }
        }
 
        internal static bool UnhookWinEvent(IntPtr winEventHook)
        {
            // There is no indication in the Windows SDK documentation that UnhookWinEvent()
            // will set an error to be retrieved with GetLastError, so set the pragma to ignore
            // the PERSHARP warning.
#pragma warning suppress 6523
            return UnsafeNativeMethods.UnhookWinEvent(winEventHook);
        }
 
        internal static bool UnionRect(out NativeMethods.Win32Rect rcDst, ref NativeMethods.Win32Rect rc1, ref NativeMethods.Win32Rect rc2)
        {
            bool result = SafeNativeMethods.UnionRect(out rcDst, ref rc1, ref rc2);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool UnregisterHotKey(IntPtr hwnd, short atom)
        {
            bool result = UnsafeNativeMethods.UnregisterHotKey(hwnd, atom);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static IntPtr VirtualAlloc(IntPtr address, UIntPtr size, int allocationType, int protect)
        {
            IntPtr result = UnsafeNativeMethods.VirtualAlloc(address, size, allocationType, protect);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == IntPtr.Zero)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static IntPtr VirtualAllocEx(MS.Internal.AutomationProxies.SafeProcessHandle hProcess, IntPtr address, UIntPtr size, int allocationType, int protect)
        {
            IntPtr result = UnsafeNativeMethods.VirtualAllocEx(hProcess, address, size, allocationType, protect);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == IntPtr.Zero)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool VirtualFree(IntPtr address, UIntPtr size, int freeType)
        {
            bool result = UnsafeNativeMethods.VirtualFree(address, size, freeType);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool VirtualFreeEx(MS.Internal.AutomationProxies.SafeProcessHandle hProcess, IntPtr address, UIntPtr size, int freeType)
        {
            bool result = UnsafeNativeMethods.VirtualFreeEx(hProcess, address, size, freeType);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool WriteProcessMemory(MS.Internal.AutomationProxies.SafeProcessHandle hProcess, IntPtr dest, IntPtr sourceAddress, IntPtr size, out IntPtr bytesWritten)
        {
            bool result = UnsafeNativeMethods.WriteProcessMemory(hProcess, dest, sourceAddress, size, out bytesWritten);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool IsWindowInGivenProcess(IntPtr hwnd, string targetprocess)
        {
            uint processId;
            //GetWindowThreadProcessId throws ElementNotAvailableException if the hwnd is no longer valid.
            //But, this exception should be handled by the client accessing this proxy.
            uint threadId = GetWindowThreadProcessId(hwnd, out processId);
            try
            {
                string processName = System.Diagnostics.Process.GetProcessById((int)processId).ProcessName;
                if (processName == targetprocess)
                {
                    return true;
                }
            }
            catch (Exception ex)
            {
                if (IsCriticalException(ex))
                    throw;
 
                if (ex is ArgumentException || ex is SystemException)
                {
                    //The process is no longer running which implies AutomationElement is no longer available.
                    //Let the client handle it.
                    throw new ElementNotAvailableException();
                }
 
                //All other exceptions are ignored as the purpose of this method is to find whether the hwnd belongs to
                //a particular process or not.
            }
            return false;
        }
 
        internal static bool InTheShellProcess(IntPtr hwnd)
        {
            IntPtr hwndShell = SafeNativeMethods.GetShellWindow();
            if (hwndShell == IntPtr.Zero)
                return false;
            uint idProcessUs;
            GetWindowThreadProcessId(hwnd, out idProcessUs);
            uint idProcessShell;
            GetWindowThreadProcessId(hwndShell, out idProcessShell);
            return idProcessUs == idProcessShell;
        }
 
        // the windows listview has feature (like group by) in v6 that only exist on
        // vista and beyond.  This lets us test for that.
        internal static bool IsComctrlV6OnOsVerV6orHigher(IntPtr hwnd)
        {
            int commonControlVersion  = Misc.ProxySendMessageInt(hwnd, NativeMethods.CCM_GETVERSION, IntPtr.Zero, IntPtr.Zero);
            if (Environment.OSVersion.Version.Major >= 6 && commonControlVersion >= 6)
            {
                return true;
            }
 
            return false;
        }
 
        #endregion
 
        // ------------------------------------------------------
        //
        // Internal Fields
        //
        // ------------------------------------------------------
 
        #region Internal Fields
 
        // Max length for the name
        internal const int MaxLengthNameProperty = 2000;
 
        // Timeout for clearing menus
        internal const long MenuTimeOut = 100;
 
        #endregion
 
        // ------------------------------------------------------
        //
        // Private Methods
        //
        // ------------------------------------------------------
 
        #region Private Methods
 
        private static bool EnumToolTipWindows(IntPtr hwnd, ref UnsafeNativeMethods.ENUMTOOLTIPWINDOWINFO lParam)
        {
            // Use ProxyGetClassName here instead of GetClassName(),
            // since for a winforms tooltip the latter will return
            // "WindowsForms10.tooltips_class32.app.0.b7ab7b".
            // Instead, ProxyGetClassName uses WM_GETOBJECT with
            // OBJID_QUERYCLASSNAMEIDX, which will return the correct answer.
            if (!ProxyGetClassName(hwnd).Equals("tooltips_class32"))
            {
                return true;
            }
 
            NativeMethods.TOOLINFO tool = new NativeMethods.TOOLINFO();
            tool.Init(Marshal.SizeOf(typeof(NativeMethods.TOOLINFO)));
            // For tooltips with ids of 0, MFC will create the tooltip with the flag of TTF_IDISHWND.
            // TTF_IDISHWND indicates that the uId member is the window handle to the tool.
            if (lParam.id == 0)
            {
                tool.hwnd = Misc.GetParent(lParam.hwnd);
                tool.uId = unchecked((int)lParam.hwnd);
                tool.uFlags = NativeMethods.TTF_IDISHWND;
            }
            else
            {
                tool.hwnd = lParam.hwnd;
                tool.uId = lParam.id;
            }
 
            string name = XSendMessage.GetItemText(hwnd, tool);
 
            // Didn't get anything - continue looking...
            if (string.IsNullOrEmpty(name))
            {
                return true;
            }
 
            lParam.name = name;
 
            // Got it - can stop iterating now.
            return false;
        }
 
        // Get the size of a page in memory for VirtualAlloc
        private static uint GetPageSize()
        {
            NativeMethods.SYSTEM_INFO sysInfo;
            UnsafeNativeMethods.GetSystemInfo(out sysInfo);
            return sysInfo.dwPageSize;
        }
 
        //This function throws corresponding exception depending on the last error.
        private static void EvaluateSendMessageTimeoutError(int error)
        {
            EvaluateSendMessageTimeoutError(error, false);
        }
 
        private static void EvaluateSendMessageTimeoutError(int error, bool ignoreTimeout)
        {
            // SendMessageTimeout Function
            // If the function fails or times out, the return value is zero. To get extended error information,
            // call GetLastError. If GetLastError returns zero, then the function timed out.
            // NOTE: The GetLastError after a SendMessageTimeout my also be an ERROR_TIMEOUT depending on the
            // message.
 
            // 1460 This operation returned because the timeout period expired. ERROR_TIMEOUT
            if (error == 0 || error == 1460)
            {
                if (!ignoreTimeout)
                {
                    throw new TimeoutException();
                }
            }
            else
            {
                ThrowWin32ExceptionsIfError(error);
            }
        }
 
        private static Rect[] GetTitlebarRectsXP(IntPtr hwnd)
        {
            Debug.Assert(System.Environment.OSVersion.Version.Major < 6);
 
            UnsafeNativeMethods.TITLEBARINFO tiDL;
            if (!Misc.ProxyGetTitleBarInfo(hwnd, out tiDL))
            {
                return null;
            }
 
            // Titlebars that are invisible or may not exist should not have a rect
            if ((tiDL.rgstate[NativeMethods.INDEX_TITLEBAR_SELF] & (NativeMethods.STATE_SYSTEM_INVISIBLE | NativeMethods.STATE_SYSTEM_OFFSCREEN)) != 0)
            {
                return null;
            }
 
            // We really should be using the the theme APIs but they give incorrect results with the titlebar buttons so we are reverting back to system metrics
            // This is not perfect, its a looks like it may be a few pixels of here or there but clicking on the center of the button will be sucessfull.  The only place this
            // does not work is on console windows when the theme is Windows Clasic Style.
 
            // The system metric seems to be just bit off, subtracting 1 seems to get a better result on all themes
            int buttonWidth = UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CXSIZE) - 1;
            int buttonHeight = UnsafeNativeMethods.GetSystemMetrics(NativeMethods.SM_CYSIZE) - 1;
 
            Rect[] rects = new Rect[NativeMethods.CCHILDREN_TITLEBAR + 1];
 
            // Start at the end and work backwards to the system menu.  Subtract buttonSize when a child is present.
            int leftEdge;
 
            if (Misc.IsLayoutRTL(hwnd))
            {
                // Right to left mirroring style
 
                // This is to take in count for a bug in GetTitleBarInfo().  It does not calculate the
                // rcTitleBar correctly when the WS_EX_LAYOUTRTL extended style is set.  It assumes
                // SYSMENU is always on the left and removes its space from the wrong side of rcTitleBar.
                // Use the bounding rectangle of the whole title bar to get the true left boundary.
                leftEdge = (int)(Misc.GetTitleBarRect(hwnd).Left);
                for (int i = NativeMethods.INDEX_TITLEBAR_MAC; i > NativeMethods.INDEX_TITLEBAR_SELF; i--)
                {
                    if ((tiDL.rgstate[i] & NativeMethods.STATE_SYSTEM_INVISIBLE) == 0)
                    {
                        rects[i] = new Rect(leftEdge, tiDL.rcTitleBar.top, buttonWidth, buttonHeight);
                        leftEdge += buttonWidth;
                    }
                    else
                    {
                        rects[i] = Rect.Empty;
                    }
                }
            }
            else
            {
                leftEdge = tiDL.rcTitleBar.right - buttonWidth;
                for (int i = NativeMethods.INDEX_TITLEBAR_MAC; i > NativeMethods.INDEX_TITLEBAR_SELF; i--)
                {
                    if ((tiDL.rgstate[i] & NativeMethods.STATE_SYSTEM_INVISIBLE) == 0)
                    {
                        rects[i] = new Rect(leftEdge, tiDL.rcTitleBar.top, buttonWidth, buttonHeight);
                        leftEdge -= buttonWidth;
                    }
                    else
                    {
                        rects[i] = Rect.Empty;
                    }
                }
            }
 
            return rects;
        }
 
        private static Rect[] GetTitlebarRectsEx(IntPtr hwnd)
        {
            Debug.Assert(System.Environment.OSVersion.Version.Major >= 6);
 
            UnsafeNativeMethods.TITLEBARINFOEX ti;
            if (!Misc.ProxyGetTitleBarInfoEx(hwnd, out ti))
            {
                return null;
            }
 
            // Titlebars that are invisible or may not exist should not have a rect
            if ((ti.rgstate[NativeMethods.INDEX_TITLEBAR_SELF] & (NativeMethods.STATE_SYSTEM_INVISIBLE | NativeMethods.STATE_SYSTEM_OFFSCREEN)) != 0)
            {
                return null;
            }
 
            Rect[] rects = new Rect[NativeMethods.CCHILDREN_TITLEBAR + 1];
            for (int i = 0; i <= NativeMethods.CCHILDREN_TITLEBAR; i++)
            {
                // Buttons that are invisible or may not exist should not have a rect
                if ((ti.rgstate[i] & NativeMethods.STATE_SYSTEM_INVISIBLE) != 0)
                {
                    rects[i] = Rect.Empty;
                }
                else
                {
                    rects[i] = new Rect(ti.rgrect[i].left, ti.rgrect[i].top, ti.rgrect[i].right - ti.rgrect[i].left, ti.rgrect[i].bottom - ti.rgrect[i].top);
                }
            }
 
            return rects;
        }
 
        #endregion
 
        // ------------------------------------------------------
        //
        // Private Fields
        //
        // ------------------------------------------------------
 
        #region Private Fields
 
        // Generic flags for SendMessages
        private const int _sendMessageFlags = NativeMethods.SMTO_BLOCK;
 
        // Generic time out for SendMessages
        // Most messages won't need anything near this - but there are a few - eg. WM_COMMAND/CBN_DROPDOWN
        // when sent to IE's address combo for the first time causes it to populate iself, and that can
        // take a couple of seconds on a slow machine.
        private const int _sendMessageTimeoutValue = 10000;
 
        // Array of known class names
        private static string[] _asClassNames = {
            "ListBox",
            "#32768",
            "Button",
            "Static",
            "Edit",
            "ComboBox",
            "#32770",
            "#32771",
            "MDIClient",
            "#32769",
            "ScrollBar",
            "msctls_statusbar32",
            "ToolbarWindow32",
            "msctls_progress32",
            "SysAnimate32",
            "SysTabControl32",
            "msctls_hotkey32",
            "SysHeader32",
            "msctls_trackbar32",
            "SysListView32",
            "OpenListView",
            "msctls_updown",
            "msctls_updown32",
            "tooltips_class",
            "tooltips_class32",
            "SysTreeView32",
            "SysMonthCal32",
            "SysDateTimePick32",
            "RICHEDIT",
            "RichEdit20A",
            "RichEdit20W",
            "SysIPAddress32"
        };
 
        #endregion
 
    }
}