После счастливого использования программного обеспечения с открытым исходным кодом много лет я полагал, что пора отдать. И так как документация обычно является слабым местом для многих проектов, плюс мои навыки C# точно не пользуются повышенным спросом в моем углу земли ВЫШИВАЛЬНОГО ШЕЛКА, я полагал, что запущу с учебных руководств и т.д.
После второго учебного руководства для горящего инструмента я уже раздражался из-за стандартной программы
и изображенный я мог автоматизировать это.
Я предполагаю то, что я ищу, программа, которая сделала бы снимок экрана в настоящее время открытого окна, краска, например, желтая панель вокруг сфокусированного управления (кнопка, возможно) затем открываются немного текстового поля для меня, чтобы ввести описание для изображения и наконец добавить все это к веб-сайту/базе данных/списку/и т.д.
Теперь для моего фактического вопроса: Если кто-либо не знает инструмент, который уже делает это, мне нужны некоторые начинающие о том, как получить доступ к размеру и положению средств управления на 'внешних' окнах так, чтобы я мог вычислить, где нарисовать тех, которые выделяют панели вокруг важных средств управления. Я помню те размаскировавшие пароль инструменты для Windows, который мог показать содержание любого ******
- защищенное текстовое поле, но я не могу найти открытые примеры на этом.. WinAPI наполняют, я предполагаю, WindowFromPoint + GetDlgItem или что-то как этот. Никакая идея, если это немного легче в Linux, любой, не будет прекрасна все же. Никакое предпочтение на языке программирования также.
Насколько мне известно, то, что вы хотите сделать, требует некоторого 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-программах.