File: MS\Internal\Automation\Misc.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\UIAutomation\UIAutomationClient\UIAutomationClient.csproj (UIAutomationClient)
// 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.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Automation;
using NativeMethodsSetLastError = MS.Internal.UIAutomationClient.NativeMethodsSetLastError;
 
namespace MS.Internal.Automation
{
    // Helper class that contains methods/utilities that don't really belong anywhere else,
    // or don't justify a class of their own.
    internal static class Misc
    {
        // Static class, so no ctor
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
 
        #region Internal Methods
 
        #region Element Comparisons
 
        // compare two arrays
        internal static bool Compare(int[] a1, int[] a2)
        {
            ArgumentNullException.ThrowIfNull(a1);
            ArgumentNullException.ThrowIfNull(a2);
 
            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;
        }
 
        // compare two AutomationElements
        internal static bool Compare(AutomationElement el1, AutomationElement el2)
        {
            ArgumentNullException.ThrowIfNull(el1);
            ArgumentNullException.ThrowIfNull(el2);
            return Compare(el1.GetRuntimeId(), el2.GetRuntimeId());
        }
        #endregion Element Comparisons
 
        #region Array combination
 
        // Concantenate arrays from a collection of arrays
        internal static Array CombineArrays(IEnumerable arrays, Type t)
        {
            int totalLength = 0;
 
            foreach (Array a in arrays)
            {
                totalLength += a.Length;
            }
 
            Array combined = Array.CreateInstance(t, totalLength);
            int pos = 0;
 
            foreach (Array a in arrays)
            {
                int l = a.Length;
 
                Array.Copy(a, 0, combined, pos, l);
                pos += l;
            }
 
            return combined;
        }
 
        // return an array with duplicate elements removed. Has side-effect of sorting array;
        // therefore elements must be sortable (IComparable)
        internal static Array RemoveDuplicates(Array a, Type t)
        {
            if (a.Length == 0)
                return a;
 
            Array.Sort(a);
 
            // Remove duplicate elements by selective copy-to-self...
            int dest = 0;
 
            for (int src = 1; src < a.Length; src++)
            {
                if (!a.GetValue(src).Equals(a.GetValue(dest)))
                {
                    dest++;
                    a.SetValue(a.GetValue(src), dest);
                }
            }
 
            // dest was the index of the last item, now use it as the length by adding one.
            int newLength = dest + 1;
 
            if (newLength == a.Length)
            {
                // No duplicates found - just return as-is
                return a;
            }
            else
            {
                // Return shorter array
                Array a2 = Array.CreateInstance(t, newLength);
 
                Array.Copy(a, 0, a2, 0, newLength);
                return a2;
            }
        }
        #endregion Array combination
 
        #region Interface & Property Wrapping
 
        // Wrap pattern interface on client side, before handing to client - ie. create a client-side pattern wrapper
        internal static object WrapInterfaceOnClientSide(AutomationElement el, SafePatternHandle hPattern, AutomationPattern pattern)
        {
            if (hPattern.IsInvalid)
                return null;
 
            AutomationPatternInfo pi;
 
            if (!Schema.GetPatternInfo(pattern, out pi))
            {
                throw new ArgumentException(SR.UnsupportedPattern);
            }
 
            if (pi.ClientSideWrapper == null)
            {
                Debug.Assert(false, "missing client-side pattern wrapper");
                return null;
            }
            else
            {
                // false -> not cached. (Cached goes via AutomationElement.GetCachedPattern, not here)
                return pi.ClientSideWrapper(el, hPattern, false);
            }
        }
 
        #endregion Interface & Property Wrapping
 
        #region Param validation & Error related
 
        // Throw an argument Exception with a generic error
        internal static void ThrowInvalidArgument(string argName)
        {
            throw new ArgumentException(SR.Format(SR.GenericInvalidArgument, argName));
        }
 
        // Check that specified condition is true; if not, throw exception
        internal static void ValidateArgument(bool cond, string reason)
        {
            if (!cond)
            {
                throw new ArgumentException(SR.GetResourceString(reason, null));
            }
        }
 
        // Called by the patterns before accessing .Cache
        internal static void ValidateCached(bool cached)
        {
            if (!cached)
            {
                throw new InvalidOperationException(SR.CacheRequestNeedCache);
            }
        }
 
        // Called by the patterns before accessing .Current
        internal static void ValidateCurrent(SafePatternHandle hPattern)
        {
            if (hPattern.IsInvalid)
            {
                throw new InvalidOperationException(SR.CacheRequestNeedLiveForProperties);
            }
        }
 
        // 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;
        }
 
        internal static bool IsWindowsFormsControl(string className)
        {
            return className.IndexOf("windowsforms", StringComparison.OrdinalIgnoreCase) > -1;
        }
 
        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.
                    throw new InvalidOperationException();
 
                default:
                    // Not sure how to map the reset of the error codes so throw generic Win32Exception.
                    throw new Win32Exception(errorCode);
            }
        }
 
        // We have this wrapper because casting IntPtr to int may
        // generate OverflowException when one of high 32 bits is set.
        private static int IntPtrToInt32(IntPtr intPtr)
        {
            return unchecked((int)intPtr.ToInt64());
        }
 
        #endregion Param validation & Error related
 
        #region Misc
 
        internal static IntPtr CreateRectRgn(int left, int top, int right, int bottom)
        {
            IntPtr result = SafeNativeMethods.CreateRectRgn(left, top, right, bottom);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == IntPtr.Zero)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool CloseHandle(IntPtr handle)
        {
            bool result = UnsafeNativeMethods.CloseHandle(handle);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool DeleteObject(IntPtr hrgn)
        {
            bool result = UnsafeNativeMethods.DeleteObject(hrgn);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool GetClientRect(NativeMethods.HWND hwnd, out NativeMethods.RECT rc)
        {
            bool result = SafeNativeMethods.GetClientRect(hwnd, out rc);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool GetGUIThreadInfo(int idThread, ref SafeNativeMethods.GUITHREADINFO guiThreadInfo)
        {
            guiThreadInfo.cbSize = Marshal.SizeOf(guiThreadInfo);
 
            bool result = SafeNativeMethods.GetGUIThreadInfo(0, ref guiThreadInfo);
            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);
            }
 
            return result;
        }
 
        internal static bool GetMenuBarInfo(NativeMethods.HWND hwnd, int idObject, uint item, ref UnsafeNativeMethods.MENUBARINFO mbi)
        {
            bool result = NativeMethodsSetLastError.GetMenuBarInfo(hwnd, idObject, item, ref mbi);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static int GetModuleFileNameEx(MS.Internal.Automation.SafeProcessHandle hProcess, IntPtr hModule, StringBuilder buffer, int length)
        {
            int result = SafeNativeMethods.GetModuleFileNameEx(hProcess, hModule, buffer, length);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static int GetMessage(ref UnsafeNativeMethods.MSG msg, NativeMethods.HWND hwnd, int nMsgFilterMin, int nMsgFilterMax)
        {
            int result = UnsafeNativeMethods.GetMessage(ref msg, hwnd, nMsgFilterMin, nMsgFilterMax);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == -1)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static NativeMethods.HWND GetWindow(NativeMethods.HWND hwnd, int uCmd)
        {
            NativeMethods.HWND result = NativeMethodsSetLastError.GetWindow(hwnd, uCmd);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == NativeMethods.HWND.NULL)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static int GetWindowLong(NativeMethods.HWND hWnd, int nIndex)
        {
            int iResult = 0;
            IntPtr result = IntPtr.Zero;
            int error = 0;
 
            if (IntPtr.Size == 4)
            {
                // use GetWindowLong
                iResult = NativeMethodsSetLastError.GetWindowLong(hWnd, nIndex);
                error = Marshal.GetLastWin32Error();
                result = new IntPtr(iResult);
            }
            else
            {
                // use GetWindowLongPtr
                result = NativeMethodsSetLastError.GetWindowLongPtr(hWnd, nIndex);
                error = Marshal.GetLastWin32Error();
                iResult = IntPtrToInt32(result);
            }
 
            if ((result == IntPtr.Zero) && (error != 0))
            {
                ThrowWin32ExceptionsIfError(error);
            }
 
            return iResult;
        }
 
        internal static bool GetWindowPlacement(NativeMethods.HWND hwnd, ref UnsafeNativeMethods.WINDOWPLACEMENT wp)
        {
            bool result = UnsafeNativeMethods.GetWindowPlacement(hwnd, ref wp);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool GetWindowRect(NativeMethods.HWND hwnd, out NativeMethods.RECT rc)
        {
            bool result = SafeNativeMethods.GetWindowRect(hwnd, out rc);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static short GlobalAddAtom(string lpString)
        {
            short result = SafeNativeMethods.GlobalAddAtom(lpString);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static short GlobalDeleteAtom(short atom)
        {
            short result = NativeMethodsSetLastError.GlobalDeleteAtom(atom);
            ThrowWin32ExceptionsIfError(Marshal.GetLastWin32Error());
            return result;
        }
 
        internal static int TryMsgWaitForMultipleObjects(SafeWaitHandle handle, bool waitAll, int milliseconds, int wakeMask, ref int lastWin32Error)
        {
            int terminationEvent;
            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();
                    }
                }
            }
            return terminationEvent;
        }
 
        internal static int MsgWaitForMultipleObjects(SafeWaitHandle handle, bool waitAll, int milliseconds, int wakeMask)
        {
            int lastWin32Error = 0;
            int terminationEvent = TryMsgWaitForMultipleObjects(handle, waitAll, milliseconds, wakeMask, ref lastWin32Error);
            if (terminationEvent == UnsafeNativeMethods.WAIT_FAILED)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
            return terminationEvent;
        }
 
        internal static IntPtr OpenProcess(int dwDesiredAccess, bool fInherit, int dwProcessId, NativeMethods.HWND hwnd)
        {
            IntPtr processHandle = UnsafeNativeMethods.OpenProcess(dwDesiredAccess, fInherit, dwProcessId);
            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.h);
                    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;
        }
 
        internal static bool PostMessage(NativeMethods.HWND hWnd, int nMsg, IntPtr wParam, IntPtr lParam)
        {
            bool result = UnsafeNativeMethods.PostMessage(hWnd, nMsg, wParam, lParam);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool RegisterHotKey(NativeMethods.HWND hWnd, int id, int fsModifiers, int vk)
        {
            bool result = UnsafeNativeMethods.RegisterHotKey(hWnd, id, fsModifiers, vk);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static int SendInput(int nInputs, ref UnsafeNativeMethods.INPUT mi, int cbSize)
        {
            int result = UnsafeNativeMethods.SendInput(nInputs, ref mi, cbSize);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (result == 0)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static IntPtr SendMessageTimeout(NativeMethods.HWND hwnd, int Msg, IntPtr wParam, IntPtr lParam)
        {
            // Use a timeout of 10 seconds.
            // Don't use the SMTO_ABORTIFHUNG flag - windows if often too quick to think that an app is hung - eg only a couple of
            // seconds or less - many apps recover from this.
            IntPtr lresult;
            IntPtr smtoRetVal = UnsafeNativeMethods.SendMessageTimeout(hwnd, Msg, wParam, lParam, 0, 10000, out lresult);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (smtoRetVal == IntPtr.Zero)
            {
                EvaluateSendMessageTimeoutError(lastWin32Error);
            }
 
            return lresult;
        }
 
        internal static IntPtr SendMessageTimeout(NativeMethods.HWND hwnd, int Msg, IntPtr wParam, ref UnsafeNativeMethods.MINMAXINFO lParam)
        {
            // Use a timeout of 10 seconds.
            // Don't use the SMTO_ABORTIFHUNG flag - windows if often too quick to think that an app is hung - eg only a couple of
            // seconds or less - many apps recover from this.
            IntPtr lresult;
            IntPtr smtoRetVal = UnsafeNativeMethods.SendMessageTimeout(hwnd, Msg, wParam, ref lParam, 0, 10000, out lresult);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (smtoRetVal == IntPtr.Zero)
            {
                EvaluateSendMessageTimeoutError(lastWin32Error);
            }
 
            return lresult;
        }
 
        internal static IntPtr SendMessageTimeout(NativeMethods.HWND hwnd, int Msg, IntPtr wParam, StringBuilder lParam)
        {
            // Use a timeout of 10 seconds.
            // Don't use the SMTO_ABORTIFHUNG flag - windows if often too quick to think that an app is hung - eg only a couple of
            // seconds or less - many apps recover from this.
            IntPtr lresult;
            IntPtr smtoRetVal = UnsafeNativeMethods.SendMessageTimeout(hwnd, Msg, wParam, lParam, 0, 10000, out lresult);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (smtoRetVal == IntPtr.Zero)
            {
                EvaluateSendMessageTimeoutError(lastWin32Error);
            }
 
            return lresult;
        }
 
 
        internal static bool SetWindowPlacement(NativeMethods.HWND hwnd, ref UnsafeNativeMethods.WINDOWPLACEMENT wp)
        {
            bool result = UnsafeNativeMethods.SetWindowPlacement(hwnd, ref wp);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool SetWindowPos(NativeMethods.HWND hWnd, NativeMethods.HWND hWndInsertAfter, int x, int y, int cx, int cy, int flags)
        {
            bool result = UnsafeNativeMethods.SetWindowPos(hWnd, hWndInsertAfter, x, y, cx, cy, flags);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        internal static bool UnregisterHotKey(NativeMethods.HWND hWnd, int id)
        {
            bool result = UnsafeNativeMethods.UnregisterHotKey(hWnd, id);
            int lastWin32Error = Marshal.GetLastWin32Error();
 
            if (!result)
            {
                ThrowWin32ExceptionsIfError(lastWin32Error);
            }
 
            return result;
        }
 
        // 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);
        }
 
 
        #endregion Misc
 
        #endregion Internal Methods
 
 
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
 
        #region Private Methods
 
        //This function throws corresponding exception depending on the last error.
        private static void EvaluateSendMessageTimeoutError(int error)
        {
            // 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)
            {
                throw new TimeoutException();
            }
            else
            {
                ThrowWin32ExceptionsIfError(error);
            }
        }
 
        #endregion Private Methods
    }
}