// 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)); }
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 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); }