Страницы

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

вторник, 10 декабря 2019 г.

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

#c_sharp #winapi #графика #gdi


Нужно написать на С# программу, которая будет определять над каким окном 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) вызывал и одиночно (по нажатию кнопки) и в
цикле (так как предполагал, что отрисовка может выполнятся за один кадр, после чего,
исчезать). Ни так ни так не работает.  

Подскажите, пожалуйста, в чем проблема... Скоро курсовой сдавать, а ничего не готово :(
    


Ответы

Ответ 1



Проблема в неверном подходе. Во-первых, забудьте про функцию 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); } Иллюстрация различия между этими двумя методами:

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

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