/// <summary> /// Get a location where this window would be visible /// * if none is found return false, formLocation = the original location /// * if something is found, return true and formLocation = new location /// </summary> /// <param name="interopWindow">IInteropWindow, the window to find a location for</param> /// <param name="formLocation">NativePoint with the location where the window will fit</param> /// <returns>true if a location if found, and the formLocation is also set</returns> public static bool GetVisibleLocation(this IInteropWindow interopWindow, out NativePoint formLocation) { bool doesWindowFit = false; var windowRectangle = interopWindow.GetInfo().Bounds; // assume own location formLocation = windowRectangle.Location; var primaryDisplay = User32Api.AllDisplays().First(x => x.IsPrimary); using (var workingArea = new Region(primaryDisplay.Bounds)) { // Create a region with the screens working area foreach (var display in User32Api.AllDisplays()) { if (!display.IsPrimary) { workingArea.Union(display.Bounds); } } // If the formLocation is not inside the visible area if (!workingArea.AreRectangleCornersVisisble(windowRectangle)) { // If none found we find the biggest screen foreach (var display in User32Api.AllDisplays()) { var newWindowRectangle = new Rectangle(display.WorkingArea.Location, windowRectangle.Size); if (!workingArea.AreRectangleCornersVisisble(newWindowRectangle)) { continue; } formLocation = display.Bounds.Location; doesWindowFit = true; break; } } else { doesWindowFit = true; } } return(doesWindowFit); }
/// <summary> /// This method will capture the current Cursor by using User32 Code /// </summary> /// <returns>A Capture Object with the Mouse Cursor information in it.</returns> public static ICapture CaptureCursor(ICapture capture) { Log.Debug().WriteLine("Capturing the mouse cursor."); if (capture == null) { capture = new Capture(); } var cursorInfo = CursorInfo.Create(); if (!NativeCursorMethods.GetCursorInfo(ref cursorInfo)) { return(capture); } if (cursorInfo.Flags != CursorInfoFlags.Showing) { return(capture); } using (var safeIcon = NativeIconMethods.CopyIcon(cursorInfo.CursorHandle)) { if (!NativeIconMethods.GetIconInfo(safeIcon, out var iconInfo)) { return(capture); } using (iconInfo.BitmaskBitmapHandle) using (iconInfo.ColorBitmapHandle) { var cursorLocation = User32Api.GetCursorLocation(); // Allign cursor location to Bitmap coordinates (instead of Screen coordinates) var x = cursorLocation.X - iconInfo.Hotspot.X - capture.ScreenBounds.X; var y = cursorLocation.Y - iconInfo.Hotspot.Y - capture.ScreenBounds.Y; // Set the location capture.CursorLocation = new NativePoint(x, y); using (var icon = Icon.FromHandle(safeIcon.DangerousGetHandle())) { capture.Cursor = icon; } } } return(capture); }
/// <summary> /// Enumerate the windows / child windows (this is NOT lazy) /// </summary> /// <param name="parent">IInteropWindow with the hwnd of the parent, or null for all</param> /// <param name="wherePredicate">Func for the where</param> /// <param name="takeWhileFunc">Func which can decide to stop enumerating, the second argument is the current count</param> /// <returns>IEnumerable with InteropWindow</returns> public static IEnumerable <IInteropWindow> EnumerateWindows(IInteropWindow parent = null, Func <IInteropWindow, bool> wherePredicate = null, Func <IInteropWindow, int, bool> takeWhileFunc = null) { var result = new List <IInteropWindow>(); bool EnumWindowsProc(IntPtr hwnd, IntPtr param) { // check if we should continue var interopWindow = InteropWindowFactory.CreateFor(hwnd); if (wherePredicate == null || wherePredicate(interopWindow)) { result.Add(interopWindow); } return(takeWhileFunc == null || takeWhileFunc(interopWindow, result.Count)); } User32Api.EnumChildWindows(parent?.Handle ?? IntPtr.Zero, EnumWindowsProc, IntPtr.Zero); return(result); }
public MainWindow() { InitializeComponent(); this.AttachDpiHandler(); this.WinProcMessages() .Where(m => m.Message == WindowsMessages.WM_DESTROY) .Subscribe(m => { MessageBox.Show($"{m.Message}"); }); DeviceNotification.OnVolumeAdded().Subscribe(volumeInfo => Debug.WriteLine($"Drives {volumeInfo.Volume.Drives} were added")); DeviceNotification.OnVolumeRemoved().Subscribe(volumeInfo => Debug.WriteLine($"Drives {volumeInfo.Volume.Drives} were removed")); DeviceNotification .OnDeviceArrival() .Subscribe(deviceInterfaceChangeInfo => Debug.WriteLine("Device added: {0}, for more information goto {1}", deviceInterfaceChangeInfo.Device.FriendlyDeviceName, deviceInterfaceChangeInfo.Device.UsbDeviceInfoUri)); // A small example to lock the PC when a YubiKey is removed DeviceNotification.OnDeviceRemoved() .Where(deviceInterfaceChangeInfo => deviceInterfaceChangeInfo.Device.Name.Contains("Yubi")) .Subscribe(deviceInterfaceChangeInfo => User32Api.LockWorkStation()); }
/// <summary> /// Get the Windows caption (title) /// </summary> /// <param name="interopWindow">InteropWindow</param> /// <param name="forceUpdate">set to true to make sure the value is updated</param> /// <returns>string with the caption</returns> public static string GetCaption(this IInteropWindow interopWindow, bool forceUpdate = false) { if (interopWindow.Caption != null && !forceUpdate) { return(interopWindow.Caption); } // Calling User32Api.GetText (GetWindowText) for the current Process will hang, deadlock, this should be ignored if (interopWindow.IsOwnedByCurrentThread()) { // TODO: it might have a value, but can't get it. Returning null would be bad... so return empty interopWindow.Caption = string.Empty; Log.Warn().WriteLine("Do not call GetWindowText for a Window ({0}) which belongs the current thread! An empty string is returned.", interopWindow.Handle); } else { var caption = User32Api.GetText(interopWindow.Handle); interopWindow.Caption = caption; } return(interopWindow.Caption); }
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) //使用冒泡事件 { if (_direction != Direction.NONE) { var pos = this.PointToScreen(Mouse.GetPosition(this)); //鼠标屏幕坐标 var leftTop = this.PointToScreen(new Point(0, 0)); //左上 var right = this.PointToScreen(new Point(this.Width, 0)); //右 var bottom = this.PointToScreen(new Point(0, this.Height)); //下 if (pos.X - leftTop.X <= 2) //左 { User32Api.SetCursorPos((int)pos.X + 1, (int)pos.Y); } else if (pos.Y - leftTop.Y <= 2) //上 { User32Api.SetCursorPos((int)pos.X, (int)pos.Y + 1); } else if (right.X - pos.X <= 2) //右 { User32Api.SetCursorPos((int)pos.X - 1, (int)pos.Y); } else if (bottom.Y - pos.Y <= 2) //下 { User32Api.SetCursorPos((int)pos.X, (int)pos.Y - 1); } } else if (e.ClickCount == 2 && Mouse.GetPosition(this).Y <= this._headHeight) //双击最大化 { if (this.WindowState == WindowState.Normal) { this.WindowState = WindowState.Maximized; } else { this.WindowState = WindowState.Normal; } e.Handled = true; } base.OnMouseLeftButtonDown(e); }
private async Task TestWinEventHook() { // This takes care of having a WinProc handler, to make sure the messages arrive var winProcHandler = WinProcHandler.Instance; // This buffers the observable var replaySubject = new ReplaySubject <IInteropWindow>(); var winEventObservable = WinEventHook.WindowTileChangeObservable() .Select(info => InteropWindowFactory.CreateFor(info.Handle).Fill()) .Where(interopWindow => !string.IsNullOrEmpty(interopWindow?.Caption)) .Subscribe(interopWindow => { Log.Debug().WriteLine("Window title change: Process ID {0} - Title: {1}", interopWindow.Handle, interopWindow.Caption); replaySubject.OnNext(interopWindow); }, exception => Log.Error().WriteLine("An error occured", exception)); await Task.Delay(100); // Start a process to test against using (var process = Process.Start("notepad.exe")) { try { // Make sure it's started Assert.NotNull(process); // Wait until the process started it's message pump (listening for input) process.WaitForInputIdle(); User32Api.SetWindowText(process.MainWindowHandle, "TestWinEventHook - Test"); // Find the belonging window var notepadWindow = await replaySubject.Where(info => info != null && info.ProcessId == process.Id).FirstAsync(); Assert.Equal(process.Id, notepadWindow?.ProcessId); } finally { winEventObservable.Dispose(); process?.Kill(); } } }
/// <summary> /// Used to access system folder icons. /// </summary> /// <param name="size">Specify large or small icons.</param> /// <param name="folderType">Specify open or closed FolderType.</param> /// <returns>System.Drawing.Icon</returns> public static TIcon GetFolderIcon <TIcon>(IconSize size, FolderType folderType) where TIcon : class { // Need to add size check, although errors generated at present! var flags = ShellGetFileInfoFlags.Icon | ShellGetFileInfoFlags.UseFileAttributes; if (FolderType.Open == folderType) { flags |= ShellGetFileInfoFlags.OpenIcon; } if (IconSize.Small == size) { flags |= ShellGetFileInfoFlags.SmallIcon; } else { flags |= ShellGetFileInfoFlags.LargeIcon; } // Get the folder icon var shellFileInfo = new ShellFileInfo(); SHGetFileInfo(null, FILE_ATTRIBUTE_DIRECTORY, ref shellFileInfo, (uint)Marshal.SizeOf(shellFileInfo), flags); //Icon.FromHandle(shfi.hIcon); // Load the icon from an HICON handle // Now clone the icon, so that it can be successfully stored in an ImageList try { return(IconHelper.IconHandleTo <TIcon>(shellFileInfo.IconHandle)); } finally { if (shellFileInfo.IconHandle != IntPtr.Zero) { // Cleanup User32Api.DestroyIcon(shellFileInfo.IconHandle); } } }
/// <summary> /// Returns an icon for a given file extension - indicated by the name parameter. /// See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb762179(v=vs.85).aspx /// </summary> /// <param name="filename">Filename</param> /// <param name="size">Large or small</param> /// <param name="linkOverlay">Whether to include the link icon</param> /// <returns>System.Drawing.Icon</returns> public static TIcon GetFileExtensionIcon <TIcon>(string filename, IconSize size, bool linkOverlay) where TIcon : class { var shfi = new ShellFileInfo(); // UseFileAttributes makes it simulate, just gets the icon for the extension var flags = ShellGetFileInfoFlags.Icon | ShellGetFileInfoFlags.UseFileAttributes; if (linkOverlay) { flags |= ShellGetFileInfoFlags.LinkOverlay; } // Check the size specified for return. if (IconSize.Small == size) { flags |= ShellGetFileInfoFlags.SmallIcon; } else { flags |= ShellGetFileInfoFlags.LargeIcon; } SHGetFileInfo(Path.GetFileName(filename), FILE_ATTRIBUTE_NORMAL, ref shfi, (uint)Marshal.SizeOf(shfi), flags); // TODO: Fix bad practise for cleanup, and use generics to allow the user to specify if it's an icon/bitmap/-source // Copy (clone) the returned icon to a new object, thus allowing us to clean-up properly try { return(IconHelper.IconHandleTo <TIcon>(shfi.IconHandle)); } finally { if (shfi.IconHandle != IntPtr.Zero) { // Cleanup User32Api.DestroyIcon(shfi.IconHandle); } } }
/// <summary> /// Method to set the ScrollbarInfo, if we can get it /// </summary> /// <param name="forceUpdate">set to true to force an update, default is false</param> /// <returns>ScrollBarInfo?</returns> public ScrollBarInfo?GetScrollbarInfo(bool forceUpdate = false) { // Prevent updates, if there is already a value if (ScrollBar.HasValue && !forceUpdate) { return(ScrollBar); } var objectId = ObjectIdentifiers.Client; switch (ScrollBarType) { case ScrollBarTypes.Control: objectId = ObjectIdentifiers.Client; break; case ScrollBarTypes.Vertical: objectId = ObjectIdentifiers.VerticalScrollbar; break; case ScrollBarTypes.Horizontal: objectId = ObjectIdentifiers.HorizontalScrollbar; break; } var scrollbarInfo = ScrollBarInfo.Create(); var hasScrollbarInfo = User32Api.GetScrollBarInfo(ScrollBarWindow.Handle, objectId, ref scrollbarInfo); if (!hasScrollbarInfo) { var error = Win32.GetLastErrorCode(); if (Log.IsVerboseEnabled()) { Log.Verbose().WriteLine("Error retrieving Scrollbar info : {0}", Win32.GetMessage(error)); } return(null); } ScrollBar = scrollbarInfo; return(scrollbarInfo); }
/// <summary> /// Get the WindowInfo /// </summary> /// <param name="interopWindow">InteropWindow</param> /// <param name="forceUpdate">set to true to make sure the value is updated</param> /// <returns>WindowInfo</returns> public static WindowInfo GetInfo(this IInteropWindow interopWindow, bool forceUpdate = false) { if (interopWindow.Info.HasValue && !forceUpdate) { return(interopWindow.Info.Value); } var windowInfo = WindowInfo.Create(); User32Api.GetWindowInfo(interopWindow.Handle, ref windowInfo); // Now correct the bounds, for Windows 10 if (Dwm.IsDwmEnabled) { NativeRect extendedFrameBounds; bool gotFrameBounds = Dwm.GetExtendedFrameBounds(interopWindow.Handle, out extendedFrameBounds); if (gotFrameBounds && (interopWindow.IsApp() || WindowsVersion.IsWindows10OrLater && !interopWindow.IsMaximized())) { windowInfo.Bounds = extendedFrameBounds; } } interopWindow.Info = windowInfo; return(interopWindow.Info.Value); }
/// <summary> /// Extension method to create a WindowScroller /// </summary> /// <param name="interopWindow">IInteropWindow</param> /// <param name="scrollBarType">ScrollBarTypes</param> /// <param name="forceUpdate">true to force a retry, even if the previous check failed</param> /// <returns>WindowScroller or null</returns> public static WindowScroller GetWindowScroller(this IInteropWindow interopWindow, ScrollBarTypes scrollBarType = ScrollBarTypes.Vertical, bool forceUpdate = false) { if (!forceUpdate && interopWindow.CanScroll.HasValue && !interopWindow.CanScroll.Value) { return(null); } var initialScrollInfo = ScrollInfo.Create(ScrollInfoMask.All); if (User32Api.GetScrollInfo(interopWindow.Handle, scrollBarType, ref initialScrollInfo) && initialScrollInfo.Minimum != initialScrollInfo.Maximum) { var windowScroller = new WindowScroller { ScrollingWindow = interopWindow, ScrollBarWindow = interopWindow, ScrollBarType = scrollBarType, InitialScrollInfo = initialScrollInfo, WheelDelta = (int)(120 * (initialScrollInfo.PageSize / WindowScroller.ScrollWheelLinesFromRegistry)) }; interopWindow.CanScroll = true; return(windowScroller); } if (User32Api.GetScrollInfo(interopWindow.Handle, ScrollBarTypes.Control, ref initialScrollInfo) && initialScrollInfo.Minimum != initialScrollInfo.Maximum) { var windowScroller = new WindowScroller { ScrollingWindow = interopWindow, ScrollBarWindow = interopWindow, ScrollBarType = ScrollBarTypes.Control, InitialScrollInfo = initialScrollInfo, WheelDelta = (int)(120 * (initialScrollInfo.PageSize / WindowScroller.ScrollWheelLinesFromRegistry)) }; interopWindow.CanScroll = true; return(windowScroller); } interopWindow.CanScroll = false; return(null); }
/// <summary> /// Check if a Windows Store App (WinRT) is visible /// </summary> /// <param name="windowBounds"></param> /// <returns>true if an app, covering the supplied rect, is visisble</returns> public static bool AppVisible(NativeRect windowBounds) { if (AppVisibility == null) { return(true); } foreach (var screen in User32Api.AllDisplays()) { if (!screen.Bounds.Contains(windowBounds)) { continue; } if (windowBounds.Equals(screen.Bounds)) { // Fullscreen, it's "visible" when AppVisibilityOnMonitor says yes // Although it might be the other App, this is not "very" important var rect = screen.Bounds; var monitor = User32Api.MonitorFromRect(ref rect, MonitorFrom.DefaultToNearest); if (monitor != IntPtr.Zero) { var monitorAppVisibility = AppVisibility.ComObject.GetAppVisibilityOnMonitor(monitor); if (monitorAppVisibility == MonitorAppVisibility.MAV_APP_VISIBLE) { return(true); } } } else { // Is only partly on the screen, when this happens the app is allways visible! return(true); } } return(false); }
/// <summary> /// Get the WindowInfo /// </summary> /// <param name="interopWindow">InteropWindow</param> /// <param name="forceUpdate">set to true to make sure the value is updated</param> /// <param name="autoCorrect">enable auto correction, e,g, have the bounds cropped to the parent(s)</param> /// <returns>WindowInfo</returns> public static WindowInfo GetInfo(this IInteropWindow interopWindow, bool forceUpdate = false, bool autoCorrect = true) { if (interopWindow.Info.HasValue && !forceUpdate) { return(interopWindow.Info.Value); } var windowInfo = WindowInfo.Create(); User32Api.GetWindowInfo(interopWindow.Handle, ref windowInfo); // Test if we need to correct some values if (autoCorrect) { // Correct the bounds, for Windows 8+ if (Dwm.IsDwmEnabled) { // This only works for top level windows, otherwise a access denied is returned bool gotFrameBounds = Dwm.GetExtendedFrameBounds(interopWindow.Handle, out var extendedFrameBounds); if (gotFrameBounds && (interopWindow.IsApp() || WindowsVersion.IsWindows10OrLater && !interopWindow.IsMaximized())) { windowInfo.Bounds = extendedFrameBounds; } } var parentWindow = interopWindow.GetParentWindow(); if (interopWindow.HasParent) { var parentInfo = parentWindow.GetInfo(forceUpdate, true); windowInfo.Bounds = windowInfo.Bounds.Intersect(parentInfo.Bounds); windowInfo.ClientBounds = windowInfo.ClientBounds.Intersect(parentInfo.ClientBounds); } } interopWindow.Info = windowInfo; return(windowInfo); }
/// <summary> /// Default constructor, this opens the input destop with GENERIC_ALL /// This is needed to support marshalling!! /// </summary> public SafeCurrentInputDesktopHandle() : base(true) { var hDesktop = User32Api.OpenInputDesktop(0, true, DesktopAccessRight.GENERIC_ALL); if (hDesktop != IntPtr.Zero) { // Got desktop, store it as handle for the ReleaseHandle SetHandle(hDesktop); if (User32Api.SetThreadDesktop(hDesktop)) { Log.Debug().WriteLine("Switched to desktop {0}", hDesktop); } else { Log.Warn().WriteLine("Couldn't switch to desktop {0}", hDesktop); Log.Error().WriteLine(User32Api.CreateWin32Exception("SetThreadDesktop")); } } else { Log.Warn().WriteLine("Couldn't get current desktop."); Log.Error().WriteLine(User32Api.CreateWin32Exception("OpenInputDesktop")); } }
/// <summary> /// 按键按下 /// </summary> /// <param name="vKeyCoad"></param> public static void MykeyDown(VKKey vKeyCoad) { if (!IsInitialize) { return; } Thread.Sleep(100); int btScancode = 0; btScancode = User32Api.MapVirtualKey((uint)vKeyCoad, 0); KBCWait4IBE(); SetPortVal(KBC_KEY_DATA, (IntPtr)0x60, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_CMD, (IntPtr)0xD2, 1); KBCWait4IBE(); SetPortVal(KBC_KEY_DATA, (IntPtr)btScancode, 1); }
/// <summary> /// See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms648069%28v=vs.85%29.aspx /// </summary> /// <typeparam name="TIcon"></typeparam> /// <param name="location">The file (EXE or DLL) to get the icon from</param> /// <param name="index">Index of the icon</param> /// <param name="useLargeIcon">true if the large icon is wanted</param> /// <returns>Icon</returns> public static TIcon ExtractAssociatedIcon <TIcon>(string location, int index = 0, bool useLargeIcon = true) where TIcon : class { IntPtr large; IntPtr small; NativeInvokes.ExtractIconEx(location, index, out large, out small, 1); TIcon returnIcon = null; try { if (useLargeIcon && !IntPtr.Zero.Equals(large)) { returnIcon = IconHandleTo <TIcon>(large); } else if (!IntPtr.Zero.Equals(small)) { returnIcon = IconHandleTo <TIcon>(small); } else if (!IntPtr.Zero.Equals(large)) { returnIcon = IconHandleTo <TIcon>(large); } } finally { if (!IntPtr.Zero.Equals(small)) { User32Api.DestroyIcon(small); } if (!IntPtr.Zero.Equals(large)) { User32Api.DestroyIcon(large); } } return(returnIcon); }
/// <summary> /// Minimize the Window /// </summary> /// <param name="interopWindow">InteropWindow</param> /// <returns>IInteropWindow for fluent calls</returns> public static IInteropWindow Minimize(this IInteropWindow interopWindow) { User32Api.ShowWindow(interopWindow.Handle, ShowWindowCommands.Minimize); interopWindow.IsMinimized = true; return(interopWindow); }
protected override bool ReleaseHandle() { return(User32Api.CloseDesktop(handle)); }
/// <summary> /// Move the specified window to a new location /// </summary> /// <param name="interopWindow">IInteropWindow</param> /// <param name="location">NativePoint with the offset</param> /// <returns>IInteropWindow for fluent calls</returns> public static IInteropWindow MoveTo(this IInteropWindow interopWindow, NativePoint location) { User32Api.SetWindowPos(interopWindow.Handle, IntPtr.Zero, location.X, location.Y, 0, 0, WindowPos.SWP_NOSIZE | WindowPos.SWP_SHOWWINDOW | WindowPos.SWP_NOACTIVATE | WindowPos.SWP_NOZORDER); interopWindow.Info = null; return(interopWindow); }
/// <summary> /// Tests if the interopWindow still exists /// </summary> /// <param name="interopWindow">IInteropWindow</param> /// <returns>True if it's still there. /// Because window handles are recycled the handle could point to a different window! /// </returns> public static bool Exists(this IInteropWindow interopWindow) { return(User32Api.IsWindow(interopWindow.Handle)); }
/// <summary> /// Set the WindowPlacement /// </summary> /// <param name="interopWindow">InteropWindow</param> /// <param name="placement">WindowPlacement</param> /// <returns>IInteropWindow for fluent calls</returns> public static IInteropWindow SetPlacement(this IInteropWindow interopWindow, WindowPlacement placement) { User32Api.SetWindowPlacement(interopWindow.Handle, ref placement); interopWindow.Placement = placement; return(interopWindow); }
/// <summary> /// Set the WindowStyle /// </summary> /// <param name="interopWindow">InteropWindow</param> /// <param name="windowStyleFlags">WindowStyleFlags</param> /// <returns>IInteropWindow for fluent calls</returns> public static IInteropWindow SetStyle(this IInteropWindow interopWindow, WindowStyleFlags windowStyleFlags) { User32Api.SetWindowLongWrapper(interopWindow.Handle, WindowLongIndex.GWL_STYLE, new IntPtr((uint)windowStyleFlags)); interopWindow.Info = null; return(interopWindow); }
/// <summary> /// Test scrolling a window /// </summary> /// <returns></returns> //[StaFact] private async Task TestScrollingAsync() { var breakScroll = false; IDisposable keyboardhook = null; try { keyboardhook = KeyboardHook.KeyboardEvents.Where(args => args.Key == VirtualKeyCode.Escape).Subscribe(args => breakScroll = true); // Start a process to test against using (var process = Process.Start("notepad.exe", "C:\\Windows\\setupact.log")) { // Make sure it's started Assert.NotNull(process); // Wait until the process started it's message pump (listening for input) process.WaitForInputIdle(); try { // Find the belonging window, by the process id var notepadWindow = WindowsEnumerator.EnumerateWindows() .FirstOrDefault(interopWindow => { User32Api.GetWindowThreadProcessId(interopWindow.Handle, out var processId); return(processId == process.Id); }); Assert.NotNull(notepadWindow); // Create a WindowScroller var scroller = notepadWindow.GetChildren().Select(window => window.GetWindowScroller()).FirstOrDefault(); Assert.NotNull(scroller); // Notepad should have ScrollBarInfo scroller.GetScrollbarInfo(); Assert.True(scroller.ScrollBar.HasValue); Log.Info().WriteLine("Scrollbar info: {0}", scroller.ScrollBar.Value); User32Api.SetForegroundWindow(scroller.ScrollingWindow.Handle); await Task.Delay(1000); // Just make sure the window is changed KeyboardInputGenerator.KeyPresses(VirtualKeyCode.Next, VirtualKeyCode.Down); await Task.Delay(2000); scroller.ScrollMode = ScrollModes.WindowsMessage; scroller.ShowChanges = false; // Move the window to the start Assert.True(scroller.Start()); // A delay to make the window move await Task.Delay(2000); // Check if it did move to the start Assert.True(scroller.IsAtStart); // Loop do { if (breakScroll) { break; } // Next "page" Assert.True(scroller.Next()); // Wait a bit, so the window can update await Task.Delay(300); // Loop as long as we are not at the end yet } while (!scroller.IsAtEnd); scroller.Reset(); } finally { // Kill the process process.Kill(); } } } finally { keyboardhook?.Dispose(); } }
/// <summary> /// Get current position /// </summary> /// <returns>SCROLLINFO</returns> public bool GetPosition(out ScrollInfo scrollInfo) { scrollInfo = ScrollInfo.Create(ScrollInfoMask.All); return(User32Api.GetScrollInfo(ScrollBarWindow.Handle, ScrollBarType, ref scrollInfo)); }
/// <summary> /// The mouse move handler of the capture form /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnMouseMove(object sender, MouseEventArgs e) { // Make sure the mouse coordinates are fixed, when pressing shift _mouseMovePos = FixMouseCoordinates(User32Api.GetCursorLocation()); _mouseMovePos = WindowCapture.GetLocationRelativeToScreenBounds(_mouseMovePos); }
/// <summary> /// This method will use User32 code to capture the specified captureBounds from the screen /// </summary> /// <param name="captureBounds">NativeRect with the bounds to capture</param> /// <returns>Bitmap which is captured from the screen at the location specified by the captureBounds</returns> public static Bitmap CaptureRectangle(NativeRect captureBounds) { Bitmap returnBitmap = null; if (captureBounds.Height <= 0 || captureBounds.Width <= 0) { Log.Warn().WriteLine("Nothing to capture, ignoring!"); return(null); } Log.Debug().WriteLine("CaptureRectangle Called!"); // .NET GDI+ Solution, according to some post this has a GDI+ leak... // See http://connect.microsoft.com/VisualStudio/feedback/details/344752/gdi-object-leak-when-calling-graphics-copyfromscreen // Bitmap capturedBitmap = new Bitmap(captureBounds.Width, captureBounds.Height); // using (Graphics graphics = Graphics.FromImage(capturedBitmap)) { // graphics.CopyFromScreen(captureBounds.Location, NativePoint.Empty, captureBounds.Size, CopyPixelOperation.CaptureBlt); // } // capture.Image = capturedBitmap; // capture.Location = captureBounds.Location; using (var desktopDcHandle = SafeWindowDcHandle.FromDesktop()) { if (desktopDcHandle.IsInvalid) { // Get Exception before the error is lost var exceptionToThrow = CreateCaptureException("desktopDCHandle", captureBounds); // throw exception throw exceptionToThrow; } // create a device context we can copy to using (var safeCompatibleDcHandle = Gdi32Api.CreateCompatibleDC(desktopDcHandle)) { // Check if the device context is there, if not throw an error with as much info as possible! if (safeCompatibleDcHandle.IsInvalid) { // Get Exception before the error is lost var exceptionToThrow = CreateCaptureException("CreateCompatibleDC", captureBounds); // throw exception throw exceptionToThrow; } // Create BITMAPINFOHEADER for CreateDIBSection var bmi = BitmapInfoHeader.Create(captureBounds.Width, captureBounds.Height, 24); // TODO: Enable when the function is available again // Make sure the last error is set to 0 Win32.SetLastError(0); // create a bitmap we can copy it to, using GetDeviceCaps to get the width/height IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap. // TODO: Change the usage to an enum? using (var safeDibSectionHandle = Gdi32Api.CreateDIBSection(desktopDcHandle, ref bmi, 0, out bits0, IntPtr.Zero, 0)) { if (safeDibSectionHandle.IsInvalid) { // Get Exception before the error is lost var exceptionToThrow = CreateCaptureException("CreateDIBSection", captureBounds); exceptionToThrow.Data.Add("hdcDest", safeCompatibleDcHandle.DangerousGetHandle().ToInt32()); exceptionToThrow.Data.Add("hdcSrc", desktopDcHandle.DangerousGetHandle().ToInt32()); // Throw so people can report the problem throw exceptionToThrow; } // select the bitmap object and store the old handle using (safeCompatibleDcHandle.SelectObject(safeDibSectionHandle)) { // bitblt over (make copy) // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags Gdi32Api.BitBlt(safeCompatibleDcHandle, 0, 0, captureBounds.Width, captureBounds.Height, desktopDcHandle, captureBounds.X, captureBounds.Y, RasterOperations.SourceCopy | RasterOperations.CaptureBlt); } // get a .NET image object for it // A suggestion for the "A generic error occurred in GDI+." E_FAIL/0×80004005 error is to re-try... var success = false; ExternalException exception = null; for (var i = 0; i < 3; i++) { try { // Collect all screens inside this capture var screensInsideCapture = new List <Screen>(); foreach (var screen in Screen.AllScreens) { if (screen.Bounds.IntersectsWith(captureBounds)) { screensInsideCapture.Add(screen); } } // Check all all screens are of an equal size bool offscreenContent; using (var captureRegion = new Region(captureBounds)) { // Exclude every visible part foreach (var screen in screensInsideCapture) { captureRegion.Exclude(screen.Bounds); } // If the region is not empty, we have "offscreenContent" using (var screenGraphics = Graphics.FromHwnd(User32Api.GetDesktopWindow())) { offscreenContent = !captureRegion.IsEmpty(screenGraphics); } } // Check if we need to have a transparent background, needed for offscreen content if (offscreenContent) { using (var tmpBitmap = Image.FromHbitmap(safeDibSectionHandle.DangerousGetHandle())) { // Create a new bitmap which has a transparent background returnBitmap = BitmapFactory.CreateEmpty(tmpBitmap.Width, tmpBitmap.Height, PixelFormat.Format32bppArgb, Color.Transparent, tmpBitmap.HorizontalResolution, tmpBitmap.VerticalResolution); // Content will be copied here using (var graphics = Graphics.FromImage(returnBitmap)) { // For all screens copy the content to the new bitmap foreach (var screen in Screen.AllScreens) { // Make sure the bounds are offsetted to the capture bounds var screenBounds = screen.Bounds; screenBounds.Offset(-captureBounds.X, -captureBounds.Y); graphics.DrawImage(tmpBitmap, screenBounds, screenBounds.X, screenBounds.Y, screenBounds.Width, screenBounds.Height, GraphicsUnit.Pixel); } } } } else { // All screens, which are inside the capture, are of equal size // assign image to Capture, the image will be disposed there.. returnBitmap = Image.FromHbitmap(safeDibSectionHandle.DangerousGetHandle()); } // We got through the capture without exception success = true; break; } catch (ExternalException ee) { Log.Warn().WriteLine(ee, "Problem getting bitmap at try " + i + " : "); exception = ee; } } if (!success) { Log.Error().WriteLine(null, "Still couldn't create Bitmap!"); if (exception != null) { throw exception; } } } } } return(returnBitmap); }
/// <summary> /// Message handler of the Per_Monitor_DPI_Aware window. /// The handles the WM_DPICHANGED message and adjusts window size, graphics and text based on the DPI of the monitor. /// The window message provides the new window size (lparam) and new DPI (wparam) /// See /// <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083(v=vs.85).aspx">WM_DPICHANGED message</a> /// </summary> /// <param name="windowMessageInfo">WindowMessageInfo</param> /// <returns>IntPtr</returns> internal bool HandleWindowMessages(WindowMessageInfo windowMessageInfo) { bool handled = false; var currentDpi = DefaultScreenDpi; bool isDpiMessage = false; switch (windowMessageInfo.Message) { // Handle the WM_NCCREATE for Forms / controls, for WPF this is done differently case WindowsMessages.WM_NCCREATE: if (Log.IsVerboseEnabled()) { Log.Verbose().WriteLine("Processing {0} event, enabling DPI scaling for window {1}", windowMessageInfo.Message, windowMessageInfo.Handle); } TryEnableNonClientDpiScaling(windowMessageInfo.Handle); break; // Handle the WM_CREATE, this is where we can get the DPI via system calls case WindowsMessages.WM_CREATE: isDpiMessage = true; if (Log.IsVerboseEnabled()) { Log.Verbose().WriteLine("Processing {0} event, retrieving DPI for window {1}", windowMessageInfo.Message, windowMessageInfo.Handle); } currentDpi = NativeDpiMethods.GetDpi(windowMessageInfo.Handle); _scopedThreadDpiAwarenessContext.Dispose(); break; // Handle the DPI change message, this is where it's supplied case WindowsMessages.WM_DPICHANGED: isDpiMessage = true; if (Log.IsVerboseEnabled()) { Log.Verbose().WriteLine("Processing {0} event, resizing / positioning window {1}", windowMessageInfo.Message, windowMessageInfo.Handle); } // Retrieve the adviced location var lprNewRect = (NativeRect)Marshal.PtrToStructure(windowMessageInfo.LongParam, typeof(NativeRect)); // Move the window to it's location, and resize User32Api.SetWindowPos(windowMessageInfo.Handle, IntPtr.Zero, lprNewRect.Left, lprNewRect.Top, lprNewRect.Width, lprNewRect.Height, WindowPos.SWP_NOZORDER | WindowPos.SWP_NOOWNERZORDER | WindowPos.SWP_NOACTIVATE); currentDpi = (uint)windowMessageInfo.WordParam & 0xFFFF; // specify that the message was handled handled = true; break; case WindowsMessages.WM_PAINT: // This is a workaround for non DPI aware applications, these don't seem to get a WM_CREATE if (Dpi == 0) { isDpiMessage = true; currentDpi = NativeDpiMethods.GetDpi(windowMessageInfo.Handle); } break; case WindowsMessages.WM_SETICON: // This is a workaround for handling WinProc outside of the class if (_needsListenerWorkaround) { isDpiMessage = true; // disable workaround _needsListenerWorkaround = false; currentDpi = NativeDpiMethods.GetDpi(windowMessageInfo.Handle); } break; case WindowsMessages.WM_DPICHANGED_BEFOREPARENT: if (Log.IsVerboseEnabled()) { Log.Verbose().WriteLine("Dpi changed on {0} before parent", windowMessageInfo.Handle); } break; case WindowsMessages.WM_DPICHANGED_AFTERPARENT: if (Log.IsVerboseEnabled()) { Log.Verbose().WriteLine("Dpi changed on {0} after parent", windowMessageInfo.Handle); } break; case WindowsMessages.WM_DESTROY: if (Log.IsVerboseEnabled()) { Log.Verbose().WriteLine("Completing the observable for {0}", windowMessageInfo.Handle); } // If the window is destroyed, we complete the subject _onDpiChanged.OnCompleted(); // Dispose all resources Dispose(); break; } // Check if the DPI was changed, if so call the action (if any) if (!isDpiMessage) { return(false); } if (Dpi != currentDpi) { var beforeDpi = Dpi; if (Log.IsVerboseEnabled()) { Log.Verbose().WriteLine("Changing DPI from {0} to {1}", beforeDpi, currentDpi); } Dpi = currentDpi; _onDpiChanged.OnNext(new DpiChangeInfo(beforeDpi, currentDpi)); } else if (Log.IsVerboseEnabled()) { Log.Verbose().WriteLine("DPI was unchanged from {0}", Dpi); } return(handled); }
/// <summary> /// Retrieves the cursor location safely, accounting for DPI settings in Vista/Windows 7. This implementation /// can conveniently be used when the cursor location is needed to deal with a fullscreen bitmap. /// </summary> /// <returns> /// NativePoint with cursor location, relative to the top left corner of the monitor setup (which itself might actually not /// be on any screen) /// </returns> public static NativePoint GetCursorLocationRelativeToScreenBounds() { return(GetLocationRelativeToScreenBounds(User32Api.GetCursorLocation())); }
/// <summary> /// Start the capture /// </summary> /// <returns>Bitmap</returns> public Bitmap Capture() { if (_windowScroller.NeedsFocus()) { User32Api.SetForegroundWindow(_windowScroller.ScrollBarWindow.Handle); Application.DoEvents(); Thread.Sleep(Delay); Application.DoEvents(); } // Find the area which is scrolling // 1. Take the client bounds var clientBounds = _windowScroller.ScrollBarWindow.GetInfo().ClientBounds; // Use a region for steps 2 and 3 using (var region = new Region(clientBounds)) { // 2. exclude the children, if any foreach (var interopWindow in _windowScroller.ScrollBarWindow.GetChildren()) { region.Exclude(interopWindow.GetInfo().Bounds); } // 3. exclude the scrollbar, if it can be found if (_windowScroller.ScrollBar.HasValue) { region.Exclude(_windowScroller.ScrollBar.Value.Bounds); } // Get the bounds of the region using (var screenGraphics = Graphics.FromHwnd(User32Api.GetDesktopWindow())) { var rectangleF = region.GetBounds(screenGraphics); clientBounds = new NativeRect((int)rectangleF.X, (int)rectangleF.Y, (int)rectangleF.Width, (int)rectangleF.Height); } } if (clientBounds.Width * clientBounds.Height <= 0) { return(null); } // Move the window to the start _windowScroller.Start(); // Register a keyboard hook to make it possible to ESC the capturing var breakScroll = false; var keyboardHook = KeyboardHook.KeyboardEvents .Where(args => args.Key == VirtualKeyCodes.ESCAPE) .Subscribe(args => { args.Handled = true; breakScroll = true; }); Bitmap resultImage = null; try { // A delay to make the window move Application.DoEvents(); Thread.Sleep(Delay); Application.DoEvents(); if (_windowScroller.IsAtStart) { using (var bitmapStitcher = new BitmapStitcher()) { bitmapStitcher.AddBitmap(WindowCapture.CaptureRectangle(clientBounds)); // Loop as long as we are not at the end yet while (!_windowScroller.IsAtEnd && !breakScroll) { // Next "page" _windowScroller.Next(); // Wait a bit, so the window can update Application.DoEvents(); Thread.Sleep(Delay); Application.DoEvents(); // Capture inside loop bitmapStitcher.AddBitmap(WindowCapture.CaptureRectangle(clientBounds)); } resultImage = bitmapStitcher.Result(); } } else { resultImage = WindowCapture.CaptureRectangle(clientBounds); } } catch (Exception ex) { Log.Error().WriteLine(ex); } finally { // Remove hook for escape keyboardHook.Dispose(); // Try to reset location _windowScroller.Reset(); } return(resultImage); }