сделать скриншот и визуально выделить фокус - PullRequest
1 голос
/ 23 мая 2010

После долгих лет счастливого использования программного обеспечения с открытым исходным кодом я решил, что пришло время вернуть. А поскольку документация обычно является слабым местом для многих проектов, плюс мои навыки в C # не очень востребованы в моем уголке земли FLOSS, я решил, что начну с уроков и т. Д.

После второго урока по инструменту горения я уже разозлился из-за процедуры

  1. сделать скриншот
  2. выделите любую важную часть
  3. комментировать
  4. добавить на сайт
  5. повтор

и решил, что смогу автоматизировать это.

Полагаю, мне нужна программа, которая сделает снимок экрана с открытым в данный момент окном, например, рисует. желтая полоса вокруг сфокусированного элемента управления (возможно, кнопка), затем всплывающее маленькое текстовое поле для меня, чтобы ввести описание для картинки и, наконец, добавить все это на сайт / базу данных / список / и т. д.

Теперь по моему актуальному вопросу: если кто-то не знает инструмента, который уже делает это, мне нужно немного узнать о том, как получить доступ к размеру и расположению элементов управления на «чужих» окнах, чтобы я мог рассчитать, где рисовать эти подсвечивающие полосы вокруг важных управления. Я помню те инструменты для снятия паролей для Windows, которые могли бы раскрыть содержимое любого ****** -защищенного текстового поля, но я не могу найти никаких открытых примеров на этот счет ... WinAPI, я думаю, WindowFromPoint + GetDlgItem или что-то в этом роде. Не знаю, проще ли в Linux, но все будет хорошо. Нет предпочтения в языке программирования.

1 Ответ

2 голосов
/ 23 мая 2010

Насколько мне известно, для того, что вы хотите сделать, требуется P / Invoke, поскольку .NET не поддерживает API для доступа к окнам других приложений.

Возможно, вы могли бы начать с использования GetForegroundWindow чтобы получить текущее окно (вам нужно запустить этот код с помощью глобальной горячей клавиши или таймера, потому что если вы переключите окна, чтобы сделать снимок экрана, вы получите свое собственное окно, возвращенное из GetForegroundWindow).

Edit

Меня вдохновил ваш вопрос сделать небольшое воскресное дневное кодирование.Я обнаружил, что GetForegroundWindow даст вам окно переднего плана, но не уровень управления.Но есть и другая полезная функция, GetGUIThreadInfo , которая даст вам текущий элемент управления и некоторую другую информацию.Мы можем использовать GetWindowInfo , чтобы получить информацию об Окне (который может быть элементом управления, содержащимся в окне верхнего уровня).

Собрав все это вместе, мы можем создать класс Window, которыйудаляет все грубые вызовы P / Invoke:

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

namespace dr.Stackoverflow.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} 0x{1}", 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.Stackoverflow.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("\t{0}", 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...