Страницы

Поиск по вопросам

понедельник, 15 октября 2018 г.

Рисование на окне другого приложения с помощью WinAPI

Нужно написать на С# программу, которая будет определять над каким окном Windows сейчас находится курсор и обводить это окно рамкой (указывая пользователю, над каким элементом находится курсор, ведь в WinApi, окном является не только само окно, содержащее в себе множество других элементов, но и эти элементы тоже являются окнами).
Handler окна под курсором получить легко:
... Point point; WinApi.GetCursorPos(out point); WinApi.WindowFromPoint(point); ...
Но вот нарисовать что то на окне не выходит, хоть убейте.
public static void drawSelectionRectangle(IntPtr handler) { Rectangle rectangle; WinApi.GetWindowRect(handler, out rectangle);
WinApi.PAINTSTRUCT paintProperties; IntPtr paintContext = WinApi.BeginPaint(handler, out paintProperties);
IntPtr pen = WinApi.CreatePen(WinApi.PenStyle.PS_SOLID, 5, (uint) ColorTranslator.ToWin32(Color.Red)); WinApi.SelectObject(paintContext, pen);
WinApi.Rectangle(paintContext, rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom);
WinApi.ValidateRect(handler, IntPtr.Zero); WinApi.EndPaint(handler, ref paintProperties); }
drawSelectionRectangle(IntPtr handler) вызывал и одиночно (по нажатию кнопки) и в цикле (так как предполагал, что отрисовка может выполнятся за один кадр, после чего, исчезать). Ни так ни так не работает.
Подскажите, пожалуйста, в чем проблема... Скоро курсовой сдавать, а ничего не готово :(


Ответ

Проблема в неверном подходе. Во-первых, забудьте про функцию BeginPaint (вне обработки сообщения WM_PAINT), во-вторых, рисовать надо не в контексте целевого окна, а в контексте его родительского окна (контекст окна позволяет рисовать только в его клиентской области, а рамка-то нам нужна снаружи).
Я предлагаю сделать как-то так:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices;
namespace DrawingTest { public partial class Form1 : Form { [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool GetCursorPos(out POINT lpPoint);
[DllImport("user32.dll")] static extern IntPtr WindowFromPoint(POINT p);
[DllImport("user32.dll", SetLastError = true)] static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll")] static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)] public static extern IntPtr GetParent(IntPtr hWnd);
public Form1() { InitializeComponent(); }
private void timer1_Tick(object sender, EventArgs e) { //получаем окно в текущей позиции курсора POINT pt; GetCursorPos(out pt); IntPtr hwnd = WindowFromPoint(pt);
//получаем родительское окно IntPtr hwnd_p = GetParent(hwnd);
//получаем границы окна RECT rc; GetWindowRect(hwnd, out rc);
//перевод из экранных координат в клиентские POINT pt1=new POINT(rc.Left,rc.Top), pt2=new POINT(rc.Right,rc.Bottom); ScreenToClient(hwnd_p, ref pt1); ScreenToClient(hwnd_p, ref pt2); RECT rc_client = new RECT(); rc_client.Left = pt1.X-1; rc_client.Top = pt1.Y-1; rc_client.Right = pt2.X; rc_client.Bottom = pt2.Y;
//формируем структуру для GDI+ Rectangle rect = new Rectangle(rc_client.Left, rc_client.Top, rc_client.Right - rc_client.Left, rc_client.Bottom - rc_client.Top);
//получаем контекст окна Graphics g = System.Drawing.Graphics.FromHwnd(hwnd_p); using (g) { //рисуем прямоугольник g.DrawRectangle(Pens.Red, rect); }
} }
[StructLayout(LayoutKind.Sequential)] public struct POINT { public int X; public int Y;
public POINT(int x, int y) { this.X = x; this.Y = y; }
public POINT(System.Drawing.Point pt) : this(pt.X, pt.Y) { }
public static implicit operator System.Drawing.Point(POINT p) { return new System.Drawing.Point(p.X, p.Y); }
public static implicit operator POINT(System.Drawing.Point p) { return new POINT(p.X, p.Y); } }
[StructLayout(LayoutKind.Sequential)] public struct RECT { public int Left; // x position of upper-left corner public int Top; // y position of upper-left corner public int Right; // x position of lower-right corner public int Bottom; // y position of lower-right corner } }

Если целевое окно перекрывается другими окнами, соответствующая часть рамки будет скрыта. Чтобы рисовать рамку поверх всех окон, код рисования нужно изменить, используя контекст рабочего стола:
//получаем окно в текущей позиции курсора POINT pt; GetCursorPos(out pt); IntPtr hwnd = WindowFromPoint(pt);
//получаем границы окна RECT rc; GetWindowRect(hwnd, out rc);
//формируем структуру для GDI+ Rectangle rect = new Rectangle(rc.Left, rc.Top, rc.Right - rc.Left, rc.Bottom - rc.Top);
//получаем контекст рабочего стола Graphics g = System.Drawing.Graphics.FromHwnd((IntPtr)0); using (g) { //рисуем прямоугольник g.DrawRectangle(Pens.Red, rect); }
Иллюстрация различия между этими двумя методами:

Комментариев нет:

Отправить комментарий