截屏并在视觉上突出显示焦点控件

Posted

技术标签:

【中文标题】截屏并在视觉上突出显示焦点控件【英文标题】:take a screenshot and visually highlight the focused control 【发布时间】:2011-02-22 22:36:34 【问题描述】:

在愉快地使用开源软件多年后,我认为是时候回馈社会了。由于文档对于许多项目来说通常是一个弱点,而且我的 C# 技能在我所在的 FLOSS 领域的需求并不高,所以我想我会从教程等开始。

烧录工具第二篇教程后,我已经厌烦了

    截图 突出显示任何重要部分 注释 添加到网站 重复

并认为我可以自动化。

我想我正在寻找的是一个程序,它会截取当前打开的窗口的屏幕截图,例如绘画。焦点控件周围的黄色条(可能是按钮)然后弹出一个小文本框让我输入图片的描述,最后将其全部添加到网站/数据库/列表/等。

现在我的实际问题是:除非有人知道已经可以做到这一点的工具,否则我需要一些初学者来了解如何访问“外国”窗口上控件的大小和位置,以便我可以计算在哪里绘制重要的那些突出显示条控制。我记得那些用于 Windows 的密码解密工具可以揭示任何受 ****** 保护的文本框的内容,但我找不到任何开放的例子。我猜是 WinAPI 的东西,WindowFromPoint + GetDlgItem 或类似的东西。不知道在 Linux 中是否更容易,不过任何一个都可以。对编程语言也没有偏好。

【问题讨论】:

【参考方案1】:

据我所知,您想要做的事情需要一些 P/Invoke,因为 .NET 没有任何用于访问其他应用程序窗口的 API。

您可能首先使用GetForegroundWindow 获取当前窗口(您需要使用全局热键或计时器触发该代码,因为如果您切换窗口以截取屏幕截图,您将返回您自己的窗口来自 GetForegroundWindow)。

编辑

我受到您的问题的启发,想在周日下午进行一些编码。我发现,GetForegroundWindow 将为您提供前景窗口,但不是控制级别。但是还有另一个有用的功能,GetGUIThreadInfo,它将为您提供当前集中的控件和其他一些信息。我们可以使用GetWindowInfo来获取Window的信息(可能是顶层窗口中包含的控件)。

将这些东西放在一起,我们可以创建一个 Window 类来抽象出所有坚韧的 P/Invoke 调用:

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;

namespace dr.***.ScreenshotTest

    public class Window
    
        private WINDOWINFO info;
        private readonly IntPtr handle;

        internal Window(IntPtr handle)
        
            this.handle = handle;
        

        public int Handle
        
            get  return handle.ToInt32(); 
        

        // Note - will not work on controls in other processes.
        public string Text
        
            get
            
                int length = GetWindowTextLength(handle);
                if ( length > 0 )
                
                    StringBuilder buffer = new StringBuilder(length);
                    if (0 < GetWindowText(handle, buffer, length))
                    
                        return buffer.ToString();
                    
                
                return "<unknown>";
            
        

        public Rectangle WindowArea
        
            get
            
                EnsureWindowInfo();
                return info.rcWindow;
            
        

        public override string ToString()
        
            return String.Format("0 0x1", Text, handle.ToString("x8"));
        

        private unsafe void EnsureWindowInfo()
        
            if (info.cbSize == 0)
            
                info.cbSize = sizeof (WINDOWINFO);
                if ( !GetWindowInfo(handle, out info) ) 
                    throw new ApplicationException("Unable to get Window Info");

            
        

        public static Window GetForeground()
        
            IntPtr handle = GetForegroundWindow();
            if (handle == IntPtr.Zero)
                return null;

            return new Window(handle);
        

        public unsafe static Window GetFocus()
        
            IntPtr foreground = GetForegroundWindow();
            int procId;
            int tId = GetWindowThreadProcessId(foreground, out procId);
            if (0 != tId)
            
                GUITHREADINFO threadInfo = new GUITHREADINFO() cbSize = sizeof (GUITHREADINFO);
                if ( GetGUIThreadInfo(tId, out threadInfo) )
                
                    return new Window(threadInfo.hwndFocus);
                
            
            return null;
        

        [StructLayout(LayoutKind.Sequential)]
        private struct WINDOWINFO
        
            public int cbSize;
            public RECT rcWindow;
            public RECT rcClient;
            public int dwStyle;
            public int dwExStyle;
            public int dwWindowStatus;
            public uint cxWindowBorders;
            public uint cyWindowBorders;
            public int atomWindowType;
            public int wCreatorVersion;
         

        [StructLayout(LayoutKind.Sequential)]
        private struct GUITHREADINFO
        

            public int cbSize;
            public int flags;
            public IntPtr hwndActive;
            public IntPtr hwndFocus;
            public IntPtr hwndCapture;
            public IntPtr hwndMenuOwner;
            public IntPtr hwndMoveSize;
            public IntPtr hwndCaret;
            public RECT rcCaret;
        

        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        
            public int left;
            public int top;
            public int right;
            public int bottom;

            public static implicit operator Rectangle(RECT rhs)
            
                return new Rectangle(rhs.left, rhs.top, rhs.right - rhs.left, rhs.bottom - rhs.top);
            
        

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool GetWindowInfo(IntPtr hwnd, out WINDOWINFO pwi);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern bool GetGUIThreadInfo(int threadId, out GUITHREADINFO threadInfo);

        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int GetWindowTextLength(IntPtr hWnd);

        [DllImport("user32", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int GetWindowText(IntPtr hWnd, [Out, MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpString, int nMaxCount);
    

然后我们可以使用它制作一个示例程序:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;

namespace dr.***.ScreenshotTest

    class Program
    
        static void Main(string[] args)
        
            Console.WriteLine("Sleeping for 3 seconds (switch to a window of interest)");
            Thread.Sleep(3000);

            Window currentWindow = Window.GetForeground();
            Window focusedWindow = Window.GetFocus();       
            if ( currentWindow != null )
            
                Console.WriteLine("Foreground window");
                Console.WriteLine(currentWindow.Text);
                Console.WriteLine(currentWindow.WindowArea);
            
            if (focusedWindow != null)
            
                Console.WriteLine("\tFocused window");
                Console.WriteLine("\t0", focusedWindow.WindowArea);
            

            if (focusedWindow !=null && currentWindow != null && focusedWindow.Handle != currentWindow.Handle)
            
                Console.WriteLine("\nTaking a screenshot");
                Rectangle screenshotArea = currentWindow.WindowArea;
                Bitmap bm = new Bitmap(currentWindow.WindowArea.Width,currentWindow.WindowArea.Height);
                using(Graphics g = Graphics.FromImage(bm))
                
                    g.CopyFromScreen(screenshotArea.Left,screenshotArea.Top, 0,0, new Size(screenshotArea.Width,screenshotArea.Height));
                    Rectangle focusBox = focusedWindow.WindowArea;
                    focusBox.Offset(screenshotArea.Left * -1, screenshotArea.Top * -1);
                    focusBox.Inflate(5,5);
                    g.DrawRectangle(Pens.Red,focusBox);
                
                bm.Save("D:\\screenshot.png", ImageFormat.Png);
            
        
    

这将制作当前前景窗口的屏幕截图,其中红色框突出显示当前聚焦的控件。请注意,这是示例代码,并且具有最少的错误检查功能 :-) 当您运行它时,Alt-Tab 到感兴趣的窗口并停留在那里直到程序完成。

虽然有一些限制。我发现的最重要的一点是这种方法在 WPF 应用程序中不起作用 - 仅仅是因为各个控件不是 Windows,因为它们在其他 Windows 程序中。

【讨论】:

哇..嫁给我?这几乎正​​是我的想法,甚至是我心爱的 C# - 非常感谢 :)

以上是关于截屏并在视觉上突出显示焦点控件的主要内容,如果未能解决你的问题,请参考以下文章

在 WPF 中加载的控件上显示焦点元素

C# 中菜单的默认突出显示颜色是啥?

TextBox - 当它失去焦点时,我可以保持选择突出显示吗?

如何更改焦点 JComboBox 的突出显示颜色

删除焦点上的蓝色突出显示文本

如何在 WinRT/UWP RichEditBox 中保持突出显示的文本失去焦点?