示例#1
0
 private static void SetCtrlHandler(ConsoleCtrlHandlerDelegate HandlerRoutine, bool bAdd)
 {
     if (!WinCon.SetConsoleCtrlHandler(HandlerRoutine, bAdd))
     {
         throw new IOException("Unable to set handler routine.", Marshal.GetLastWin32Error());
     }
 }
示例#2
0
 public WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX GetScreenBufferInfo(IntPtr hConsole)
 {
     WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
     sbiex.cbSize = (uint)Marshal.SizeOf(sbiex);
     NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Get screen buffer info for cursor position.");
     return(sbiex);
 }
示例#3
0
 public static void AddAlias(string Source, string Target, string ExeName)
 {
     if (!WinCon.AddConsoleAlias(Source, Target, ExeName))
     {
         throw new IOException("Unable to add alias", Marshal.GetLastWin32Error());
     }
 }
示例#4
0
        // Adapted from .NET source code...
        // See: http://referencesource.microsoft.com/#mscorlib/system/console.cs,fcb364a853d81c57
        private static void SetWindowPosition(int left, int top)
        {
            AutoHelpers.LogInvariant("Attempt to set console viewport buffer to Left: {0} and Top: {1}", left, top);

            IntPtr hConsole = WinCon.GetStdHandle(WinCon.CONSOLE_STD_HANDLE.STD_OUTPUT_HANDLE);

            // Get the size of the current console window
            WinCon.CONSOLE_SCREEN_BUFFER_INFO csbi = new WinCon.CONSOLE_SCREEN_BUFFER_INFO();
            NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfo(hConsole, out csbi), "Get console screen buffer for viewport size information.");

            WinCon.SMALL_RECT srWindow = csbi.srWindow;
            AutoHelpers.LogInvariant("Initial viewport position: {0}", srWindow);

            // Check for arithmetic underflows & overflows.
            int newRight = left + srWindow.Right - srWindow.Left + 1;

            if (left < 0 || newRight > csbi.dwSize.X || newRight < 0)
            {
                throw new ArgumentOutOfRangeException("left");
            }
            int newBottom = top + srWindow.Bottom - srWindow.Top + 1;

            if (top < 0 || newBottom > csbi.dwSize.Y || newBottom < 0)
            {
                throw new ArgumentOutOfRangeException("top");
            }

            // Preserve the size, but move the position.
            srWindow.Bottom -= (short)(srWindow.Top - top);
            srWindow.Right  -= (short)(srWindow.Left - left);
            srWindow.Left    = (short)left;
            srWindow.Top     = (short)top;

            NativeMethods.Win32BoolHelper(WinCon.SetConsoleWindowInfo(hConsole, true, ref srWindow), string.Format("Attempt to update viewport position to {0}.", srWindow));
        }
示例#5
0
/*
 #region Standard streams
 *              // Important note:  I manage the streams, but I don't write to or
 *              // read from them.
 *              // To write to the console:
 *              //  JConsole.Out.Write()
 *              //  JConsole.Error.Write()
 *              // To read from the console:
 *              //  JConsole.In.Read()
 *
 *              // TODO: Other stream types
 *              // I'd really like to support other than FileStream.  NetworkStream, for example,
 *              // could be really cool.  The problem is that the Stream class doesn't have a
 *              // Handle property, so there's no way that I could reliably redirect the I/O.
 *              // I guess I could write it so that it could identify FileStream and NetworkStream.
 *              // If it sees either of those, then it calls SetStdHandle and does all that.
 *              // Otherwise it just goes about its merry way, ignoring SetStdHandle.
 *              //
 *              // This isn't a problem for the .NET 1.1 or 2.0 Console class because it probably
 *              // doesn't worry about sharing.
 *
 *              // TODO: Sharing
 *              // The Windows Console API allows sharing the console among processes.
 *              // Many different processes can be talking to a single console,
 *              // and apparently any of those processes can change the input, output, or error handles
 *              // at any time.
 *              //
 *              // As currently written, this does not allow for that.  Another process changing the
 *              // handle will not affect the program that's using this class.
 *              //
 *              // I could check the STDIN handle every time it's accessed, to ensure that
 *              // it hasn't been changed by another process.  If it's been changed, I abandon
 *              // the current stream and open a new one.
 *              //
 *              // I can't Close() the current stream because I don't know if other parts of the
 *              // program are using it.
 *              //
 *              // One problem:  programs that assign a variable to JConsole.In will then be
 *              // writing to the wrong file.  Do I just document that and go on?
 *              // I think so.  People who will use this class will have to understand that
 *              // the rug can be pulled out from underneath them at any time.
 *              //
 *              // Another solution would be to create ConsoleInputStream and ConsoleOutputStream
 *              // classes that allow the underlying file to change.  I resist that because
 *              // then I wouldn't be able to use FileStream descendants.
 *              //
 *              private static FileStream stdIn = null;
 *              private static FileStream stdOut = null;
 *              private static FileStream stdErr = null;
 *
 *              private static StreamReader inputReader = null;
 *              private static StreamWriter outputWriter = null;
 *              private static StreamWriter errorWriter = null;
 *
 *              private static bool ownsInput = false;
 *              private static bool ownsOutput = false;
 *              private static bool ownsError = false;
 *
 *              public static StreamReader In
 *              {
 *                      get
 *                      {
 *                              if (stdIn == null)
 *                              {
 *                                      IntPtr handle = WinCon.GetStdHandle(WinCon.STD_INPUT_HANDLE);
 *                                      if (handle.ToInt32() == WinApi.INVALID_HANDLE_VALUE)
 *                                      {
 *                                              throw new IOException("Unable to get standard input handle", Marshal.GetLastWin32Error());
 *                                      }
 *                                      stdIn = new FileStream(handle, FileAccess.ReadWrite, false);
 *                                      inputReader = new StreamReader(stdIn);
 *                                      ownsInput = true;
 *                              }
 *                              return inputReader;
 *                      }
 *              }
 *
 *              public static void SetIn(FileStream aStream)
 *              {
 *                      if (!WinCon.SetStdHandle(WinCon.STD_INPUT_HANDLE, aStream.Handle))
 *                      {
 *                              throw new IOException("Unable to set standard input handle", Marshal.GetLastWin32Error());
 *                      }
 *                      if (inputReader != null)
 *                      {
 *                              inputReader.Close();
 *                      }
 *                      if (ownsInput && stdIn != null)
 *                      {
 *                              stdIn.Close();
 *                      }
 *                      stdIn = aStream;
 *                      inputReader = new StreamReader(stdIn);
 *                      ownsInput = false;
 *              }
 *
 *              public static void SetIn(string Filename)
 *              {
 *                      SetIn(new FileStream(Filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
 *                      ownsInput = true;
 *              }
 *
 *              private static StreamWriter CreateWriter(FileStream fs)
 *              {
 *                      StreamWriter sw = new StreamWriter(fs);
 *                      sw.AutoFlush = true;
 *                      return sw;
 *              }
 *
 *              public static StreamWriter Out
 *              {
 *                      get
 *                      {
 *                              if (stdOut == null)
 *                              {
 *                                      Console.Out.Close();
 *                                      IntPtr handle = WinCon.GetStdHandle(WinCon.STD_OUTPUT_HANDLE);
 *                                      if (handle.ToInt32() == WinApi.INVALID_HANDLE_VALUE)
 *                                      {
 *                                              throw new IOException("Unable to get standard output handle", Marshal.GetLastWin32Error());
 *                                      }
 *                                      stdOut = new FileStream(handle, FileAccess.Write, false);
 *                                      outputWriter = CreateWriter(stdOut);
 *                                      ownsOutput = true;
 *                              }
 *                              return outputWriter;
 *                      }
 *              }
 *
 *              public static void SetOut(FileStream aStream)
 *              {
 *                      if (!WinCon.SetStdHandle(WinCon.STD_OUTPUT_HANDLE, aStream.Handle))
 *                      {
 *                              throw new IOException("Unable to set standard output handle", Marshal.GetLastWin32Error());
 *                      }
 *                      if (outputWriter != null)
 *                      {
 *                              outputWriter.Close();
 *                      }
 *                      if (ownsOutput && stdOut != null)
 *                      {
 *                              stdOut.Close();
 *                      }
 *                      stdOut = aStream;
 *                      outputWriter = CreateWriter(stdOut);
 *                      ownsOutput = false;
 *              }
 *
 *              public static void SetOut(string Filename)
 *              {
 *                      SetOut(new FileStream(Filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite));
 *                      ownsOutput = true;
 *              }
 *
 *              public static StreamWriter Error
 *              {
 *                      get
 *                      {
 *                              if (stdErr == null)
 *                              {
 *                                      IntPtr handle = WinCon.GetStdHandle(WinCon.STD_ERROR_HANDLE);
 *                                      if (handle.ToInt32() == WinApi.INVALID_HANDLE_VALUE)
 *                                      {
 *                                              throw new IOException("Unable to get standard error handle", Marshal.GetLastWin32Error());
 *                                      }
 *                                      stdErr = new FileStream(handle, FileAccess.Write, false);
 *                                      errorWriter = CreateWriter(stdErr);
 *                                      ownsError = true;
 *                              }
 *                              return errorWriter;
 *                      }
 *              }
 *
 *              public static void SetError(FileStream aStream)
 *              {
 *                      if (!WinCon.SetStdHandle(WinCon.STD_ERROR_HANDLE, aStream.Handle))
 *                      {
 *                              throw new IOException("Unable to set standard error handle", Marshal.GetLastWin32Error());
 *                      }
 *                      if (errorWriter != null)
 *                      {
 *                              errorWriter.Close();
 *                      }
 *                      if (ownsError && stdErr != null)
 *                      {
 *                              stdErr.Close();
 *                      }
 *                      stdErr = aStream;
 *                      errorWriter = CreateWriter(stdErr);
 *                      ownsError = false;
 *              }
 *
 *              public static void SetError(string Filename)
 *              {
 *                      SetError(new FileStream(Filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite));
 *                      ownsError = true;
 *              }
 *
 #endregion
 */
        public static void GenerateCtrlEventType(ConsoleControlEventType eventId, int processGroupId)
        {
            if (!WinCon.GenerateConsoleCtrlEvent((int)eventId, processGroupId))
            {
                throw new IOException("Error generating event.", Marshal.GetLastWin32Error());
            }
        }
        void RunTest(AccessibilityTest test)
        {
            using (RegistryHelper reg = new RegistryHelper())
            {
                reg.BackupRegistry();
                using (CmdApp app = new CmdApp(CreateType.ProcessOnly, TestContext))
                {
                    using (WinEventSystem sys = app.AttachWinEventSystem(this))
                    {
                        using (ViewportArea area = new ViewportArea(app))
                        {
                            Globals.WaitForTimeout(); // wait for everything to settle with winevents
                            IntPtr hConsole = app.GetStdOutHandle();

                            // Prep structures to hold cursor and size of buffer.
                            WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
                            sbiex.cbSize = (uint)Marshal.SizeOf(sbiex);

                            // this is where we will hold our expected messages
                            Queue <EventData> expected = new Queue <EventData>();

                            NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Get initial console data.");
                            WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal = sbiex; // keep a copy of the original data for later.

                            // Run the test
                            test(app, area, hConsole, sbiex, expected, sbiexOriginal);
                        }
                    }
                }
            }
        }
示例#7
0
 public static void AttachConsole(int processId)
 {
     if (!WinCon.AttachConsole(processId))
     {
         throw new IOException(String.Format("Unable to attach console 0x{0,8:X}", processId), Marshal.GetLastWin32Error());
     }
 }
示例#8
0
 public static void FreeConsole()
 {
     if (!WinCon.FreeConsole())
     {
         throw new IOException("Unable to free console", Marshal.GetLastWin32Error());
     }
 }
示例#9
0
 public static void AllocConsole()
 {
     if (!WinCon.AllocConsole())
     {
         throw new IOException("Unable to allocate console", Marshal.GetLastWin32Error());
     }
 }
示例#10
0
 public void VerifyCtrlHCmd()
 {
     using (RegistryHelper reg = new RegistryHelper())
     {
         reg.BackupRegistry();
         using (CmdApp app = new CmdApp(CreateType.ProcessOnly, TestContext))
         {
             using (ViewportArea area = new ViewportArea(app))
             {
                 string testText = "1234blah5678";
                 IntPtr hConsole = app.GetStdOutHandle();
                 Verify.IsNotNull(hConsole, "Ensure the handle is valid.");
                 // get cursor location
                 WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX screenBufferInfo = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
                 WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref screenBufferInfo);
                 // send some text and a ^H to remove the last character
                 app.UIRoot.SendKeys(testText + Keys.Control + "h" + Keys.Control);
                 Globals.WaitForTimeout();
                 // test that we're missing the last character of testText on the line
                 Rectangle            rect = new Rectangle(0, 0, 200, 20);
                 IEnumerable <string> text = area.GetLinesInRectangle(hConsole, rect);
                 bool foundCtrlH           = false;
                 foreach (string line in text)
                 {
                     if (line.Contains(testText.Substring(0, testText.Length - 1)) && !line.Contains(testText))
                     {
                         foundCtrlH = true;
                         break;
                     }
                 }
                 Verify.IsTrue(foundCtrlH);
             }
         }
     }
 }
示例#11
0
 public void VerifyCtrlZCmd()
 {
     using (RegistryHelper reg = new RegistryHelper())
     {
         reg.BackupRegistry();
         using (CmdApp app = new CmdApp(CreateType.ProcessOnly, TestContext))
         {
             using (ViewportArea area = new ViewportArea(app))
             {
                 IntPtr hConsole = app.GetStdOutHandle();
                 Verify.IsNotNull(hConsole, "Ensure the handle is valid.");
                 // get cursor location
                 WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX screenBufferInfo = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
                 WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref screenBufferInfo);
                 // send ^Z
                 app.UIRoot.SendKeys(Keys.Control + "z" + Keys.Control);
                 Globals.WaitForTimeout();
                 // test that "^Z" exists on the screen
                 Rectangle            rect = new Rectangle(0, 0, 200, 20);
                 IEnumerable <string> text = area.GetLinesInRectangle(hConsole, rect);
                 bool foundCtrlZ           = false;
                 foreach (string line in text)
                 {
                     if (line.Contains("^Z"))
                     {
                         foundCtrlZ = true;
                         break;
                     }
                 }
                 Verify.IsTrue(foundCtrlZ);
             }
         }
     }
 }
示例#12
0
        public static int[] GetProcessList(IntPtr handle)
        {
            int[] processes = new int[10];
            do
            {
                int rslt = WinCon.GetConsoleProcessList(processes, processes.Length);
                if (rslt == 0)
                {
                    throw new IOException("Unable to get process list", Marshal.GetLastWin32Error());
                }
                if (rslt <= processes.Length)
                {
                    // if the array is exactly the right size, return it
                    if (rslt == processes.Length)
                    {
                        return(processes);
                    }

                    // otherwise create a new array of the required length
                    int[] newProcesses = new int[rslt];
                    Array.Copy(processes, newProcesses, rslt);
                    return(newProcesses);
                }
                else
                {
                    // The initial array was too small.
                    // Allocate more space and try again.
                    processes = new int[rslt];
                }
            } while (true);
        }
示例#13
0
        public bool IsVirtualTerminalEnabled(IntPtr hConsoleOutput)
        {
            WinCon.CONSOLE_OUTPUT_MODES modes;

            NativeMethods.Win32BoolHelper(WinCon.GetConsoleMode(hConsoleOutput, out modes), "Retrieve console output mode flags.");

            return((modes & WinCon.CONSOLE_OUTPUT_MODES.ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0);
        }
示例#14
0
        public string GetWindowTitle()
        {
            int           size    = 100;
            StringBuilder builder = new StringBuilder(size);

            WinCon.GetConsoleTitle(builder, size);

            return(builder.ToString());
        }
        private void TestLaunchAndExitChildImpl(CmdApp app, ViewportArea area, IntPtr hConsole, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex, Queue <EventData> expected, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal)
        {
            // A. We're going to type "cmd" into the prompt to start a command prompt.
            {
                Log.Comment("Type 'cmd' to get ready to start nested prompt.");
                NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Update console data.");
                TestTypeStringHelper("cmd", app, sbiex);
            }

            // B. Now we're going to press enter to launch the CMD application
            {
                Log.Comment("Press enter to launch and observe launch events.");
                NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Update console data.");
                expected.Enqueue(new EventData(EventType.StartApplication));
                expected.Enqueue(new EventData(EventType.UpdateRegion, 0, 0, sbiex.dwSize.X - 1, sbiex.dwSize.Y - 1));
                sbiex.dwCursorPosition.Y++;
                expected.Enqueue(new EventData(EventType.UpdateRegion, 0, sbiex.dwCursorPosition.Y, "Microsoft Windows [Version 10.0.14974.1001]".Length - 1, sbiex.dwCursorPosition.Y));
                sbiex.dwCursorPosition.Y++;
                expected.Enqueue(new EventData(EventType.UpdateRegion, 0, sbiex.dwCursorPosition.Y, "(c) 2016 Microsoft Corporation. All rights reserved.".Length - 1, sbiex.dwCursorPosition.Y));
                sbiex.dwCursorPosition.Y++;
                sbiex.dwCursorPosition.Y++;
                expected.Enqueue(new EventData(EventType.UpdateRegion, 0, sbiex.dwCursorPosition.Y, sbiexOriginal.dwCursorPosition.X - 1, sbiex.dwCursorPosition.Y));
                expected.Enqueue(new EventData(EventType.CaretVisible, sbiexOriginal.dwCursorPosition.X, sbiex.dwCursorPosition.Y));

                app.UIRoot.SendKeys(Keys.Enter);
                Globals.WaitForTimeout();
                Globals.WaitForTimeout();
                VerifyQueue(expected);
            }

            // C. Now we're going to type exit to leave the nested CMD application
            {
                Log.Comment("Type 'exit' to get ready to exit nested prompt.");
                NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Update console data.");
                TestTypeStringHelper("exit", app, sbiex);
            }

            // D. Now we're going to press enter to exit the CMD application
            {
                Log.Comment("Press enter to launch and observe exit events.");
                NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Update console data.");
                expected.Enqueue(new EventData(EventType.EndApplication));
                sbiex.dwCursorPosition.Y++;
                sbiex.dwCursorPosition.Y++;
                expected.Enqueue(new EventData(EventType.UpdateRegion, 0, sbiex.dwCursorPosition.Y, sbiexOriginal.dwCursorPosition.X - 1, sbiex.dwCursorPosition.Y));
                expected.Enqueue(new EventData(EventType.CaretVisible, sbiexOriginal.dwCursorPosition.X, sbiex.dwCursorPosition.Y));

                app.UIRoot.SendKeys(Keys.Enter);
                Globals.WaitForTimeout();
                Globals.WaitForTimeout();
                VerifyQueue(expected);
            }
        }
示例#16
0
        private void ExitCmdProcess()
        {
            // Release attachment to the child process console.
            WinCon.FreeConsole();

            this.UIRoot = null;

            if (this.job != IntPtr.Zero)
            {
                WinBase.TerminateJobObject(this.job, 0);
            }
            this.job = IntPtr.Zero;
        }
示例#17
0
        private void ExitProcess()
        {
            Globals.SweepAllModules(this.context);

            // Release attachment to the child process console.
            WinCon.FreeConsole();

            this.UIRoot = null;

            if (this.job != IntPtr.Zero)
            {
                WinBase.TerminateJobObject(this.job, 0);
            }
            this.job = IntPtr.Zero;
        }
示例#18
0
        public static string[] GetAliasExes()
        {
            int length = GetAliasExesLength();

            if (length > 0)
            {
                char[] buff = new char[length];
                if (WinCon.GetConsoleAliasExes(buff, length) == 0)
                {
                    throw new IOException("Unable to get alias exes", Marshal.GetLastWin32Error());
                }
                return(CharBufferToStringArray(buff, length));
            }
            return(new string[0]);
        }
        private void InitializeFont()
        {
            AutoHelpers.LogInvariant("Initializing font data for viewport area...");
            this.sizeFont = new Size();

            IntPtr hCon = app.GetStdOutHandle();

            Verify.IsNotNull(hCon, "Check that we obtained the console output handle.");

            WinCon.CONSOLE_FONT_INFO cfi = new WinCon.CONSOLE_FONT_INFO();
            NativeMethods.Win32BoolHelper(WinCon.GetCurrentConsoleFont(hCon, false, out cfi), "Attempt to get current console font.");

            this.sizeFont.Width  = cfi.dwFontSize.X;
            this.sizeFont.Height = cfi.dwFontSize.Y;
            AutoHelpers.LogInvariant("Font size is X:{0} Y:{1}", this.sizeFont.Width, this.sizeFont.Height);
        }
示例#20
0
        public static string GetAlias(string Source, string ExeName)
        {
            // The only reliable way to allocate enough space is to the
            // combined length for all aliases.
            int length = GetAliasesLength(ExeName);

            if (length == 0)
            {
                return(string.Empty);
            }

            char[] buff = new char[length];
            int    rslt = WinCon.GetConsoleAlias(Source, buff, buff.Length, ExeName);

            if (rslt == 0)
            {
                throw new IOException("Unable to get alias", Marshal.GetLastWin32Error());
            }
            return(new string(buff));
        }
示例#21
0
        public void CheckClose()
        {
            string closeTestCmdLine = $"{closeTestBinaryLocation} -n {processCount} --log {testPipeName} --delay 1000 --no-realloc";

            using (var tokenSource = new CancellationTokenSource())
            {
                var token = tokenSource.Token;

                Task.Run(() => MakePipeServer(token), token);

                Log.Comment("Connect a test console window to the close test binary and wait for a few seconds.");
                CmdApp app = new CmdApp(CreateType.ProcessOnly, TestContext, closeTestCmdLine);
                Thread.Sleep(TimeSpan.FromSeconds(2));
                NativeMethods.Win32BoolHelper(WinCon.FreeConsole(), "Free console bindings so we aren't shut down when we kill the window.");
                Log.Comment("Click the close button on the window then wait a few seconds for it to cleanup.");
                app.GetCloseButton().Click();

                Thread.Sleep(TimeSpan.FromSeconds(5));
                tokenSource.Cancel();

                Log.Comment("Compare the output we received on our pipes to what we expected to get in terms of ordering and process count.");

                for (uint i = 1; i <= processCount; i++)
                {
                    string expected = string.Format(attachPattern, i);
                    Verify.AreEqual(expected, messages.Dequeue());
                }

                Verify.IsTrue(messages.Dequeue().StartsWith(spacerStartsWith));

                for (uint i = processCount; i >= 1; i--)
                {
                    string expected;
                    expected = string.Format(pausingPattern, i);
                    Verify.AreEqual(expected, messages.Dequeue());
                    expected = string.Format(exitingPattern, i);
                    Verify.AreEqual(expected, messages.Dequeue());
                }
            }
        }
        private void _ClearScreenBuffer(CmdApp app)
        {
            IntPtr outHandle = app.GetStdOutHandle();

            WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX screenInfo = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
            screenInfo.cbSize = (uint)Marshal.SizeOf(screenInfo);
            WinCon.GetConsoleScreenBufferInfoEx(outHandle, ref screenInfo);
            int    charCount   = screenInfo.dwSize.X * screenInfo.dwSize.Y;
            string writeString = new string(' ', charCount);

            WinCon.COORD coord = new WinCon.COORD();
            coord.X = 0;
            coord.Y = 0;
            UInt32 charsWritten = 0;

            WinCon.WriteConsoleOutputCharacter(outHandle,
                                               writeString,
                                               (uint)charCount,
                                               coord,
                                               ref charsWritten);
            Verify.AreEqual((UInt32)charCount, charsWritten);
        }
        public WinCon.CHAR_INFO[,] GetCharInfoInRectangle(IntPtr handle, Rectangle rect)
        {
            WinCon.SMALL_RECT readRectangle = new WinCon.SMALL_RECT();
            readRectangle.Top    = (short)rect.Top;
            readRectangle.Bottom = (short)(rect.Bottom - 1);
            readRectangle.Left   = (short)rect.Left;
            readRectangle.Right  = (short)(rect.Right - 1);

            WinCon.COORD dataBufferSize = new WinCon.COORD();
            dataBufferSize.X = (short)rect.Width;
            dataBufferSize.Y = (short)rect.Height;

            WinCon.COORD dataBufferPos = new WinCon.COORD();
            dataBufferPos.X = 0;
            dataBufferPos.Y = 0;

            WinCon.CHAR_INFO[,] data = new WinCon.CHAR_INFO[dataBufferSize.Y, dataBufferSize.X];

            NativeMethods.Win32BoolHelper(WinCon.ReadConsoleOutput(handle, data, dataBufferSize, dataBufferPos, ref readRectangle), string.Format("Attempting to read rectangle (L: {0}, T: {1}, R: {2}, B: {3}) from output buffer.", readRectangle.Left, readRectangle.Top, readRectangle.Right, readRectangle.Bottom));

            return(data);
        }
        private void _WriteCharTestText(CmdApp app)
        {
            IntPtr outHandle = app.GetStdOutHandle();

            WinCon.COORD coord = new WinCon.COORD();
            coord.X = 0;
            coord.Y = 0;
            string row1         = "1234567890";
            string row2         = "   abcdefghijk";
            UInt32 charsWritten = 0;

            WinCon.WriteConsoleOutputCharacter(outHandle,
                                               row1,
                                               (uint)row1.Length,
                                               coord,
                                               ref charsWritten);

            coord.Y = 1;
            WinCon.WriteConsoleOutputCharacter(outHandle,
                                               row2,
                                               (uint)row2.Length,
                                               coord,
                                               ref charsWritten);
        }
示例#25
0
 public void SetScreenBufferInfo(IntPtr hConsole, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex)
 {
     NativeMethods.Win32BoolHelper(WinCon.SetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Set screen buffer info with given data.");
 }
示例#26
0
        private void CreateCmdProcess(string path, string link = "")
        {
            //string AdminPrefix = "Administrator: ";
            string WindowTitleToFind = "Host.Tests.UIA window under test";

            job = WinBase.CreateJobObject(IntPtr.Zero, IntPtr.Zero);
            NativeMethods.Win32NullHelper(job, "Creating job object to hold binaries under test.");

            Log.Comment("Attempting to launch command-line application at '{0}'", path);

            string binaryToRunPath = path;

#if __INSIDE_WINDOWS
            string launchArgs = binaryToRunPath;
#else
            string openConsolePath = Path.Combine(this.context.TestDeploymentDir, "OpenConsole.exe");

            string launchArgs = $"{openConsolePath}  {binaryToRunPath}";
#endif

            WinBase.STARTUPINFO si = new WinBase.STARTUPINFO();
            si.cb = Marshal.SizeOf(si);

            // If we were given a LNK file to startup with, set the STARTUPINFO structure to pass that information in to the console host.
            if (!string.IsNullOrEmpty(link))
            {
                si.dwFlags |= WinBase.STARTF.STARTF_TITLEISLINKNAME;
                si.lpTitle  = link;
            }

            WinBase.PROCESS_INFORMATION pi = new WinBase.PROCESS_INFORMATION();

            NativeMethods.Win32BoolHelper(WinBase.CreateProcess(null,
                                                                launchArgs,
                                                                IntPtr.Zero,
                                                                IntPtr.Zero,
                                                                false,
                                                                WinBase.CP_CreationFlags.CREATE_NEW_CONSOLE | WinBase.CP_CreationFlags.CREATE_SUSPENDED,
                                                                IntPtr.Zero,
                                                                null,
                                                                ref si,
                                                                out pi),
                                          "Attempting to create child host window process.");

            Log.Comment($"Host window PID: {pi.dwProcessId}");

            NativeMethods.Win32BoolHelper(WinBase.AssignProcessToJobObject(job, pi.hProcess), "Assigning new host window (suspended) to job object.");
            NativeMethods.Win32BoolHelper(-1 != WinBase.ResumeThread(pi.hThread), "Resume host window process now that it is attached and its launch of the child application will be caught in the job object.");

            Globals.WaitForTimeout();

            WinBase.JOBOBJECT_BASIC_PROCESS_ID_LIST list = new WinBase.JOBOBJECT_BASIC_PROCESS_ID_LIST();
            list.NumberOfAssignedProcesses = 2;

            int    listptrsize = Marshal.SizeOf(list);
            IntPtr listptr     = Marshal.AllocHGlobal(listptrsize);
            Marshal.StructureToPtr(list, listptr, false);

            TimeSpan totalWait    = TimeSpan.Zero;
            TimeSpan waitLimit    = TimeSpan.FromSeconds(30);
            TimeSpan pollInterval = TimeSpan.FromMilliseconds(500);
            while (totalWait < waitLimit)
            {
                WinBase.QueryInformationJobObject(job, WinBase.JOBOBJECTINFOCLASS.JobObjectBasicProcessIdList, listptr, listptrsize, IntPtr.Zero);
                list = (WinBase.JOBOBJECT_BASIC_PROCESS_ID_LIST)Marshal.PtrToStructure(listptr, typeof(WinBase.JOBOBJECT_BASIC_PROCESS_ID_LIST));

                if (list.NumberOfAssignedProcesses > 1)
                {
                    break;
                }
                else if (list.NumberOfAssignedProcesses < 1)
                {
                    Verify.Fail("Somehow we lost the one console host process in the job already.");
                }

                Thread.Sleep(pollInterval);
                totalWait += pollInterval;
            }
            Verify.IsLessThan(totalWait, waitLimit);

            WinBase.QueryInformationJobObject(job, WinBase.JOBOBJECTINFOCLASS.JobObjectBasicProcessIdList, listptr, listptrsize, IntPtr.Zero);
            list = (WinBase.JOBOBJECT_BASIC_PROCESS_ID_LIST)Marshal.PtrToStructure(listptr, typeof(WinBase.JOBOBJECT_BASIC_PROCESS_ID_LIST));

            Verify.AreEqual(list.NumberOfAssignedProcesses, list.NumberOfProcessIdsInList);

#if __INSIDE_WINDOWS
            pid = pi.dwProcessId;
#else
            // Take whichever PID isn't the host window's PID as the child.
            pid = pi.dwProcessId == (int)list.ProcessId ? (int)list.ProcessId2 : (int)list.ProcessId;
            Log.Comment($"Child command app PID: {pid}");
#endif

            // Free any attached consoles and attach to the console we just created.
            // The driver will bind our calls to the Console APIs into the child process.
            // This will allow us to use the APIs to get/set the console state of the test window.
            NativeMethods.Win32BoolHelper(WinCon.FreeConsole(), "Free existing console bindings.");
            // need to wait a bit or we might not be able to reliably attach
            System.Threading.Thread.Sleep(Globals.Timeout);
            NativeMethods.Win32BoolHelper(WinCon.AttachConsole((uint)pid), "Bind to the new PID for console APIs.");

            // we need to wait here for a bit or else
            // setting the console window title will fail.
            System.Threading.Thread.Sleep(Globals.Timeout * 5);
            NativeMethods.Win32BoolHelper(WinCon.SetConsoleTitle(WindowTitleToFind), "Set the window title so AppDriver can find it.");

            DesiredCapabilities appCapabilities = new DesiredCapabilities();
            appCapabilities.SetCapability("app", @"Root");
            Session = new IOSDriver <IOSElement>(new Uri(AppDriverUrl), appCapabilities);

            Verify.IsNotNull(Session);
            Actions = new Actions(Session);
            Verify.IsNotNull(Session);

            Globals.WaitForTimeout();

            // If we are running as admin, the child window title will have a prefix appended as it will also run as admin.
            //if (IsRunningAsAdmin())
            //{
            //    WindowTitleToFind = $"{AdminPrefix}{WindowTitleToFind}";
            //}

            Log.Comment($"Searching for window title '{WindowTitleToFind}'");
            this.UIRoot  = Session.FindElementByName(WindowTitleToFind);
            this.hStdOut = WinCon.GetStdHandle(WinCon.CONSOLE_STD_HANDLE.STD_OUTPUT_HANDLE);
            Verify.IsNotNull(this.hStdOut, "Ensure output handle is valid.");
            this.hStdErr = WinCon.GetStdHandle(WinCon.CONSOLE_STD_HANDLE.STD_ERROR_HANDLE);
            Verify.IsNotNull(this.hStdErr, "Ensure error handle is valid.");

            // Set the timeout to 15 seconds after we found the initial window.
            Session.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(15);
        }
示例#27
0
        public void TestKeyboardSelection()
        {
            using (RegistryHelper reg = new RegistryHelper())
            {
                reg.BackupRegistry();

                VersionSelector.SetConsoleVersion(reg, ConsoleVersion.V2);

                using (CmdApp app = new CmdApp(CreateType.ProcessOnly, TestContext))
                {
                    using (ViewportArea area = new ViewportArea(app))
                    {
                        WinCon.CONSOLE_SELECTION_INFO csi;
                        NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Get initial selection state.");
                        Log.Comment("Selection Info: {0}", csi);

                        Verify.AreEqual(csi.Flags, WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_NO_SELECTION, "Confirm no selection in progress.");
                        // ignore rectangle and coords. They're undefined when there is no selection.

                        // Get cursor position at the beginning of this operation. The anchor will start at the cursor position for v2 console.
                        // NOTE: It moved to 0,0 for the v1 console.
                        IntPtr hConsole = WinCon.GetStdHandle(WinCon.CONSOLE_STD_HANDLE.STD_OUTPUT_HANDLE);
                        Verify.IsNotNull(hConsole, "Ensure the STDOUT handle is valid.");

                        WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX cbiex = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
                        cbiex.cbSize = (uint)Marshal.SizeOf(cbiex);
                        NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref cbiex), "Get initial cursor position (from screen buffer info)");

                        // The expected anchor when we're done is this initial cursor position
                        WinCon.COORD expectedAnchor = new WinCon.COORD();
                        expectedAnchor.X = cbiex.dwCursorPosition.X;
                        expectedAnchor.Y = cbiex.dwCursorPosition.Y;

                        // The expected rect is going to start from this cursor position. We'll modify it after we perform some operations.
                        WinCon.SMALL_RECT expectedRect = new WinCon.SMALL_RECT();
                        expectedRect.Top    = expectedAnchor.Y;
                        expectedRect.Left   = expectedAnchor.X;
                        expectedRect.Right  = expectedAnchor.X;
                        expectedRect.Bottom = expectedAnchor.Y;

                        // Now set up the keyboard and enter mark mode.
                        // NOTE: We must wait after every keyboard sequence to give the console time to process before asking it for changes.
                        area.EnterMode(ViewportArea.ViewportStates.Mark);

                        NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Get state on entering mark mode.");
                        Log.Comment("Selection Info: {0}", csi);

                        Verify.AreEqual(csi.Flags, WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_SELECTION_IN_PROGRESS, "Selection should now be in progress since mark mode is started.");

                        // Select a small region
                        Log.Comment("1. Select a small region");

                        app.UIRoot.SendKeys(Keys.Shift + Keys.Right + Keys.Right + Keys.Right + Keys.Down + Keys.Shift);

                        Globals.WaitForTimeout();

                        // Adjust the expected rectangle for the commands we just entered.
                        expectedRect.Right  += 3; // same as the number of Rights we put in
                        expectedRect.Bottom += 1; // same as the number of Downs we put in

                        NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Get state of selected region.");
                        Log.Comment("Selection Info: {0}", csi);

                        Verify.AreEqual(csi.Flags, WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_SELECTION_IN_PROGRESS | WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_SELECTION_NOT_EMPTY, "Selection in progress and is no longer empty now that we've selected a region.");
                        Verify.AreEqual(csi.Selection, expectedRect, "Verify that the selected rectangle matches the keystrokes we entered.");
                        Verify.AreEqual(csi.SelectionAnchor, expectedAnchor, "Verify anchor didn't go anywhere since we started in the top left.");

                        // End selection by moving
                        Log.Comment("2. End the selection by moving.");

                        app.UIRoot.SendKeys(Keys.Down);

                        Globals.WaitForTimeout();

                        NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Move cursor to attempt to clear selection.");
                        Log.Comment("Selection Info: {0}", csi);

                        Verify.AreEqual(csi.Flags, WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_SELECTION_IN_PROGRESS, "Selection should be still running, but empty.");

                        // Select another region to ensure anchor moved.
                        Log.Comment("3. Select one more region from new position to verify anchor");

                        app.UIRoot.SendKeys(Keys.Shift + Keys.Right + Keys.Shift);

                        Globals.WaitForTimeout();

                        expectedAnchor.X = expectedRect.Right;
                        expectedAnchor.Y = expectedRect.Bottom;
                        expectedAnchor.Y++; // +1 for the Down in step 2. Not incremented in the line above because C# is unhappy with adding +1 to a short while assigning.

                        Verify.AreEqual(csi.SelectionAnchor, expectedAnchor, "Verify anchor moved to the new start position.");

                        // Exit mark mode
                        area.EnterMode(ViewportArea.ViewportStates.Normal);

                        NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Move cursor to attempt to clear selection.");
                        Log.Comment("Selection Info: {0}", csi);

                        Verify.AreEqual(csi.Flags, WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_NO_SELECTION, "Selection should be empty when mode is exited.");
                    }
                }
            }
        }
        private void TestSelectionImpl(CmdApp app, ViewportArea area, IntPtr hConsole, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiex, Queue <EventData> expected, WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal)
        {
            // A. Test single area click
            {
                // Move mouse pointer to where the cursor is
                NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Update console data.");
                Point pt = new Point(sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y);
                area.MouseMove(pt);

                // Click on this area.
                expected.Enqueue(new EventData(EventType.CaretSelection, sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y));
                area.MouseDown();
                area.MouseUp();

                Globals.WaitForTimeout();

                VerifyQueue(expected);

                // We may receive more than one caret and that's OK. Clear it out.
                this.received.Clear();

                // End selection with escape
                app.UIRoot.SendKeys(Keys.Escape);
                Globals.WaitForTimeout();

                // Expect to see the caret again after leaving selection mode
                expected.Enqueue(new EventData(EventType.CaretVisible, sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y));
                VerifyQueue(expected);
            }

            // B. Drag area click
            {
                // Move mouse pointer to where the cursor is
                NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiex), "Update console data.");
                Point pt = new Point(sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y);
                area.MouseMove(pt);

                // Click on this area.
                expected.Enqueue(new EventData(EventType.CaretSelection, sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y));
                area.MouseDown();

                Globals.WaitForTimeout();

                Point ptDrag = pt;
                // Drag down and right for "some" distance. 10 isn't for a specific reason, it's just "some".
                ptDrag.X += 10;
                ptDrag.Y += 10;

                area.MouseMove(ptDrag);

                Globals.WaitForTimeout();

                area.MouseUp();

                Globals.WaitForTimeout();

                // Verify that the first one in the queue starts with where we put the mouse down.
                VerifyQueue(expected);

                // Now we have to take the final message in the queue and make sure it is where we released the mouse
                EventData expectedLast = new EventData(EventType.CaretSelection, ptDrag.X, ptDrag.Y);
                EventData actualLast   = received.Last();
                Verify.AreEqual(expectedLast, actualLast);

                // Empty the received queue.
                received.Clear();

                // End selection with escape
                app.UIRoot.SendKeys(Keys.Escape);
                Globals.WaitForTimeout();

                // Expect to see the caret again after leaving selection mode
                expected.Enqueue(new EventData(EventType.CaretVisible, sbiex.dwCursorPosition.X, sbiex.dwCursorPosition.Y));
                VerifyQueue(expected);
            }
        }
示例#29
0
        public void TestMouseSelection()
        {
            using (CmdApp app = new CmdApp(CreateType.ProcessOnly, TestContext))
            {
                using (ViewportArea area = new ViewportArea(app))
                {
                    // Set up the area we're going to attempt to select
                    Point startPoint = new Point();
                    Point endPoint   = new Point();

                    startPoint.X = 1;
                    startPoint.Y = 2;

                    endPoint.X = 10;
                    endPoint.Y = 10;

                    // Save expected anchor
                    WinCon.COORD expectedAnchor = new WinCon.COORD();
                    expectedAnchor.X = (short)startPoint.X;
                    expectedAnchor.Y = (short)startPoint.Y;

                    // Also save bottom right corner for the end of the selection
                    WinCon.COORD expectedBottomRight = new WinCon.COORD();
                    expectedBottomRight.X = (short)endPoint.X;
                    expectedBottomRight.Y = (short)endPoint.Y;

                    // Prepare the mouse by moving it into the start position. Prepare the structure
                    WinCon.CONSOLE_SELECTION_INFO csi;
                    WinCon.SMALL_RECT             expectedRect = new WinCon.SMALL_RECT();

                    WinCon.CONSOLE_SELECTION_INFO_FLAGS flagsExpected = WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_NO_SELECTION;

                    // 1. Place mouse button down to start selection and check state
                    area.MouseMove(startPoint);
                    area.MouseDown();

                    Globals.WaitForTimeout();                                                           // must wait after mouse operation. No good waiters since we have no UI objects

                    flagsExpected |= WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_SELECTION_IN_PROGRESS; // a selection is occurring
                    flagsExpected |= WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_MOUSE_SELECTION;       // it's a "Select" mode not "Mark" mode selection
                    flagsExpected |= WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_MOUSE_DOWN;            // the mouse is still down
                    flagsExpected |= WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_SELECTION_NOT_EMPTY;   // mouse selections are never empty. minimum 1x1

                    expectedRect.Top    = expectedAnchor.Y;                                             // rectangle is just at the point itself 1x1 size
                    expectedRect.Left   = expectedAnchor.X;
                    expectedRect.Bottom = expectedRect.Top;
                    expectedRect.Right  = expectedRect.Left;

                    NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Check state on mouse button down to start selection.");
                    Log.Comment("Selection Info: {0}", csi);

                    Verify.AreEqual(csi.Flags, flagsExpected, "Check initial mouse selection with button still down.");
                    Verify.AreEqual(csi.SelectionAnchor, expectedAnchor, "Check that the anchor is equal to the start point.");
                    Verify.AreEqual(csi.Selection, expectedRect, "Check that entire rectangle is the size of 1x1 and is just at the anchor point.");

                    // 2. Move to end point and release cursor
                    area.MouseMove(endPoint);
                    area.MouseUp();

                    Globals.WaitForTimeout(); // must wait after mouse operation. No good waiters since we have no UI objects

                    // on button up, remove mouse down flag
                    flagsExpected &= ~WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_MOUSE_DOWN;

                    // anchor remains the same
                    // bottom right of rectangle now changes to the end point
                    expectedRect.Bottom = expectedBottomRight.Y;
                    expectedRect.Right  = expectedBottomRight.X;

                    NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Check state after drag and release mouse.");
                    Log.Comment("Selection Info: {0}", csi);

                    Verify.AreEqual(csi.Flags, flagsExpected, "Check selection is still on and valid, but button is up.");
                    Verify.AreEqual(csi.SelectionAnchor, expectedAnchor, "Check that the anchor is still equal to the start point.");
                    Verify.AreEqual(csi.Selection, expectedRect, "Check that entire rectangle reaches from start to end point.");

                    // 3. Leave mouse selection
                    area.ExitModes();

                    flagsExpected = WinCon.CONSOLE_SELECTION_INFO_FLAGS.CONSOLE_NO_SELECTION;

                    NativeMethods.Win32BoolHelper(WinCon.GetConsoleSelectionInfo(out csi), "Check state after exiting mouse selection.");
                    Log.Comment("Selection Info: {0}", csi);

                    Verify.AreEqual(csi.Flags, flagsExpected, "Check that selection state is reset.");
                }
            }
        }
示例#30
0
        public void TestCtrlHomeEnd()
        {
            using (CmdApp app = new CmdApp(CreateType.ProcessOnly, TestContext))
            {
                using (ViewportArea area = new ViewportArea(app))
                {
                    // Get console handle.
                    IntPtr hConsole = app.GetStdOutHandle();
                    Verify.IsNotNull(hConsole, "Ensure the STDOUT handle is valid.");

                    // Get us to an expected initial state.
                    app.UIRoot.SendKeys("C:" + Keys.Enter);
                    app.UIRoot.SendKeys(@"cd C:\" + Keys.Enter);
                    app.UIRoot.SendKeys("cls" + Keys.Enter);

                    // Get initial screen buffer position
                    WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexOriginal = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
                    sbiexOriginal.cbSize = (uint)Marshal.SizeOf(sbiexOriginal);
                    NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexOriginal), "Get initial viewport position.");

                    // Prep comparison structure
                    WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexCompare = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
                    sbiexCompare.cbSize = (uint)Marshal.SizeOf(sbiexCompare);

                    // Ctrl-End shouldn't move anything yet.
                    Log.Comment("Attempt Ctrl-End. Nothing should move yet.");
                    app.UIRoot.SendKeys(Keys.Control + Keys.End + Keys.Control);

                    Globals.WaitForTimeout();

                    NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexCompare), "Get comparison position.");
                    Verify.AreEqual <WinCon.SMALL_RECT>(sbiexOriginal.srWindow, sbiexCompare.srWindow, "Compare viewport positions before and after.");

                    // Ctrl-Home shouldn't move anything yet.
                    Log.Comment("Attempt Ctrl-Home. Nothing should move yet.");
                    app.UIRoot.SendKeys(Keys.Control + Keys.Home + Keys.Control);

                    Globals.WaitForTimeout();

                    Log.Comment("Now test the line with some text in it.");
                    // Retrieve original position (including cursor)
                    NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexOriginal), "Get position of viewport with nothing on edit line.");

                    // Put some text onto the edit line now
                    Log.Comment("Place some text onto the edit line to ensure behavior will change with edit line full.");
                    const string testText = "SomeTestText";
                    app.UIRoot.SendKeys(testText);

                    Globals.WaitForTimeout();

                    // Get the position of the cursor after the text is entered
                    WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX sbiexWithText = new WinCon.CONSOLE_SCREEN_BUFFER_INFO_EX();
                    sbiexWithText.cbSize = (uint)Marshal.SizeOf(sbiexWithText);
                    NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexWithText), "Get position of viewport with edit line text.");

                    // The cursor can't have moved down a line. We're going to verify the text by reading its "rectangle" out of the screen buffer.
                    // If it moved down a line, the calculation of what to select is more complicated than the simple rectangle assignment below.
                    Verify.AreEqual(sbiexOriginal.dwCursorPosition.Y, sbiexWithText.dwCursorPosition.Y, "There's an assumption here that the cursor stayed on the same line when we added our bit of text.");

                    // Prepare the read rectangle for what we want to get out of the buffer.
                    Rectangle readRectangle = new Rectangle(sbiexOriginal.dwCursorPosition.X,
                                                            sbiexOriginal.dwCursorPosition.Y,
                                                            (sbiexWithText.dwCursorPosition.X - sbiexOriginal.dwCursorPosition.X),
                                                            1);

                    Log.Comment("Verify that the text we keyed matches what's in the buffer.");
                    IEnumerable <string> text = area.GetLinesInRectangle(hConsole, readRectangle);
                    Verify.AreEqual(text.Count(), 1, "We should only have retrieved one line.");
                    Verify.AreEqual(text.First(), testText, "Verify text matches keyed input.");

                    // Move cursor into the middle of the text.
                    Log.Comment("Move cursor into the middle of the string.");

                    const int lefts = 4;
                    for (int i = 0; i < lefts; i++)
                    {
                        app.UIRoot.SendKeys(Keys.Left);
                    }

                    Globals.WaitForTimeout();

                    // Get cursor position now that it's moved.
                    NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexWithText), "Get position of viewport with cursor moved into the middle of the edit line text.");

                    Log.Comment("Ctrl-End should trim the end of the input line from the cursor (and not move the cursor.)");
                    app.UIRoot.SendKeys(Keys.Control + Keys.End + Keys.Control);

                    Globals.WaitForTimeout();

                    NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexCompare), "Get comparison position.");
                    Verify.AreEqual <WinCon.SMALL_RECT>(sbiexWithText.srWindow, sbiexCompare.srWindow, "Compare viewport positions before and after.");
                    Verify.AreEqual <WinCon.COORD>(sbiexWithText.dwCursorPosition, sbiexCompare.dwCursorPosition, "Compare cursor positions before and after.");

                    Log.Comment("Compare actual text visible on screen.");
                    text = area.GetLinesInRectangle(hConsole, readRectangle);
                    Verify.AreEqual(text.Count(), 1, "We should only have retrieved one line.");

                    // the substring length is the original length of the string minus the number of lefts
                    int substringCtrlEnd = testText.Length - lefts;
                    Verify.AreEqual(text.First().Trim(), testText.Substring(0, substringCtrlEnd), "Verify text matches keyed input without the last characters removed by Ctrl+End.");

                    Log.Comment("Ctrl-Home should trim the remainder of the edit line from the cursor to the beginning (restoring cursor to position before we entered anything.)");
                    app.UIRoot.SendKeys(Keys.Control + Keys.Home + Keys.Control);

                    Globals.WaitForTimeout();

                    NativeMethods.Win32BoolHelper(WinCon.GetConsoleScreenBufferInfoEx(hConsole, ref sbiexCompare), "Get comparison position.");
                    Verify.AreEqual <WinCon.SMALL_RECT>(sbiexOriginal.srWindow, sbiexCompare.srWindow, "Compare viewport positions before and after.");
                    Verify.AreEqual <WinCon.COORD>(sbiexOriginal.dwCursorPosition, sbiexCompare.dwCursorPosition, "Compare cursor positions before and after.");

                    Log.Comment("Compare actual text visible on screen.");
                    text = area.GetLinesInRectangle(hConsole, readRectangle);
                    Verify.AreEqual(text.Count(), 1, "We should only have retrieved one line.");

                    Verify.AreEqual(text.First().Trim(), string.Empty, "Verify text is now empty after Ctrl+Home from the end of it.");
                }
            }
        }