1

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

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

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

и понял, что смогу автоматизировать это.

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

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

ответ

2

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

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

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

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

+0

Ничего себе .. Выходи за меня замуж? Это в значительной степени то, что я имел в виду, и это даже мой любимый C# - большое спасибо :) – moraxy