сделайте снимок экрана и визуально выделите сфокусированное управление

После счастливого использования программного обеспечения с открытым исходным кодом много лет я полагал, что пора отдать. И так как документация обычно является слабым местом для многих проектов, плюс мои навыки C# точно не пользуются повышенным спросом в моем углу земли ВЫШИВАЛЬНОГО ШЕЛКА, я полагал, что запущу с учебных руководств и т.д.

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

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

и изображенный я мог автоматизировать это.

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

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

1
задан Tshepang 4 May 2014 в 08:13
поделиться

1 ответ

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

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

Редактировать

Ваш вопрос вдохновил меня на небольшое кодирование в воскресенье днем. Я обнаружил, что GetForegroundWindow вернет вам окно переднего плана, но не уровень Control. Но есть другая полезная функция, 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-программах.

2
ответ дан 3 September 2019 в 00:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: