Страницы

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

Показаны сообщения с ярлыком gdi+. Показать все сообщения
Показаны сообщения с ярлыком gdi+. Показать все сообщения

понедельник, 24 февраля 2020 г.

Почему у меня SetPixel не отрисовывает пиксель?

#winapi #ассемблер #masm32 #gdi


Почему у меня SetPixel не отрисовывает пиксель на форме?

WndProc proc uses ebx edi esi, hwnd:DWORD, wmsg:DWORD, wparam:DWORD, lparam:DWORD
  switch wmsg
    case WM_INITDIALOG
      invoke SendMessage, hwnd, WM_SETICON, 1, FUNC(LoadIcon, NULL, IDI_ASTERISK)
    case WM_PAINT
      invoke SetPixel, hwnd, 15, 20, 65285
    case WM_CLOSE
      exit_program:
      invoke EndDialog, hwnd, 0
    endsw
  xor eax,eax
ret
WndProc endp

    


Ответы

Ответ 1



В качестве первого параметра SetPixel принимает не идентификатор окна, а идентификатор контекста устройства (в данном случае, идентификатор контекста окна). Контекст устройства упрощенно можно сравнить с холстом, на котором происходит рисование. В вашем случае нужно получить этот идентификатор с помощью функции BeginPaint, нарисовать пиксель, потом освободить контекст при помощи EndPaint. Код для обработки сообщения WM_PAINT будет примерно такой: invoke BeginPaint, hwnd, addr ps invoke SetPixel, eax, 15, 20, 65285 invoke EndPaint, hwnd, addr ps Также необходимо объявить переменную ps, которая является структурой PAINTSTRUCT. Скорее всего определение этой структуры уже есть в каком-то из подключаемых файлов пакета MASM. addr - ключевое слово для подстановки адреса переменной. Для нескольких вызовов SetPixel можно сделать например так: invoke BeginPaint, hwnd, addr ps push ebx mov ebx, eax invoke SetPixel, ebx, 15, 20, 65285 invoke SetPixel, ebx, 30, 20, 65285 invoke SetPixel, ebx, 45, 20, 65285 pop ebx invoke EndPaint, hwnd, addr ps Это вариант без использования дополнительной переменной. По соглашениям stdcall и cdecl, вызываемые процедуры не должны модифицировать регистры ebx, edi, esi (но могут модифицировать eax, ecx, edx), поэтому ebx, например, можно использовать для временного хранения значений, которые не должны затираться вызовами процедур. Но так как процедура WndProc тоже должна соответствовать соглашению stdcall, то перед модификацией ebx его значение нужно сохранить, а потом восстановить. Это можно сделать либо локально, вокруг места, где происходит модификция ebx (как показано в этом примере), либо сохранить в начале процедуры, а в конце (перед ret) - восстановить. Еще одно замечание - код цвета лучше писать в шестнацатеричном виде, в данном случае это будет 00FF05h, тогда будет сразу понятно, что этот цвет ближе к зеленому (порядок цветов - 0x00bbggrr, согласно описанию типа COLORREF).

Ответ 2



Потому что SetPixel ожидает первым параметром hdc, а не hwnd. Вам нужно вызвать BeginPaint для получения hdc, а после отрисовки вызвать EndPaint.

пятница, 14 февраля 2020 г.

Как конвертировать цвет пикселя ABGR в ARGB?

#cpp #gdi


ARGB getPixelColor(HWND hWnd, int x, int y)
{
    HDC dc = GetDC(hWnd);
    ARGB color = GetPixel(dc, x, y);
    ReleaseDC(hWnd, dc);
    return color;
}


использую эту функцию получаю значение ABGR например 0x000248ac но мне нужно ARGB
0x00ac4802 подскажите как мне "инвертировать" это значение?
    


Ответы

Ответ 1



uint32_t ABGR_to_ARGB(uint32_t abgr) { uint32_t tmp = ((abgr >> 16) ^ abgr) & 0xFF; return abgr ^ ((tmp << 16) | tmp); } Другой вариант (ссылаясь на ответ от @Maxim Egorushkin здесь) uint32_t ABGR_to_ARGB(uint32_t abgr) { uint32_t grab = abgr >> 16 | abgr << 16; return (abgr & 0xFF0FF00) | (grab & 0x00FF00FF); } Если ваш компилятор достаточно умен для того, чтобы сообразить, что abgr >> 16 | abgr << 16 - это ни что иное как циклический сдвиг abgr на 16, то этот вариант странслируется в очень компактный машинный код на платформах с инструкцией циклического сдвига.

Ответ 2



unsigned int ABRGtoARGB(unsigned int rgba) { return (rgba & 0x00FF0000) >> 16 | (rgba & 0x0000FF00) | (rgba & 0x000000FF) << 16; } разобрался

суббота, 21 декабря 2019 г.

Работа с картинками через GDI+ VS DirectX

#c_sharp #net #directx #gdi #gdi+


Допустим, имеется задача преобразовать N картинок из одного формата в другой, с последующей
сменой размера картинок, уменьшение качества картинок и т п вещи.

Имеется ПК с Core i5 и видеокарта, которая поддерживает DirectX 9 с 512 МБ ОЗУ.

Интересует следующее:


Выполнится конвертирование через DirectX быстрее, чем через GDI+ ?
Предоставляет ли .NET какие-то обертки над DirectX, похожие на GDI+ по юзабельности?
Я знаю, что WPF использует для отрисовки аппаратное ускорение. Можно ли воспользоваться
какими-то его примитивами.
Какой подход используется в популярных пакетных конвертировщиках?
Они используют GDI+ или пишут свое более быстрое решение, которое не использует GDI+(Т.е
опять же CPU зависимые) или используют аппаратные возможности?

    


Ответы

Ответ 1



По сути я могу ошибаться, но скажу следующее: Приходилось работать с изображениями, с их генерацией, и в наследие доставался код на WinForms (с отрисовкой на GDI+), из которого сразу было решено выпилить любое упоминание WinForms и сделать всё это на том, что есть в WPF. 1) Работать стало быстрее, а если еще, как вы говорите конвертацию - сделайте её в пуле потоков Parallel.ForEach(); Тогда вообще будет очень хорошо. Только про потокобезопасность не забывайте. 2) Да, представляет. Может не в таком обилии как GDI+, но стоит разобраться и вам этого достаточно будет. Можно дополнить сторонними библиотеками на крайний случай. 3) Они используют как и CPU, так и GPU. Вообще использованием ресурсов занимается сам DX и пиковой производительности добиваются их совместным использованием. PS. Если я в чём-то не прав, то критика приветствуется.

Ответ 2



Как раз таки, DirectX использует аппаратное ускорение, у GDI с этим проблемы. .NET с шарпом имеют SharpDX - это как раз что то вроде обертки над DirectX, юзать не сказать что удобно, но в сравнении с тем же на Си++ разница чувствуется.

вторник, 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); } Иллюстрация различия между этими двумя методами:

воскресенье, 7 июля 2019 г.

Как конвертировать Bitmap в HBITMAP без потери прозрачности?

Здравствуйте! Мне нужно загрузить из файла/ресурса png рисунок с альфа пикселями и вывести его на контекст. С загрузкой хорошо справляется GDI+, но вот с выводом изображения Graphics::DrawImage() всё плохо, поэтому я решил использовать GDI (StretchBlt и т.д.). Но в StretchBlt Bitmap не запихнёшь, нужен HBITMAP. У Bitmap есть метод GetHBITMAP(Color, HBITMAP*), и в нём-то и проблема: он, почему-то конвертирует с потерей прозрачности. Я пробовал передавать Color::Black и Color::Transparent, но ничего не помогает. Как тогда получить HBITMAP с прозрачностью?
ОБНОВЛЕНО
Я понял в чём была ошибка и она глупая. Ниже вы можете увидить мой код, звёздочками я выделил то чего не было. То есть я рисовал изображение на второй буффер, на котором ничего не было, и удивлялся почему-это, альфа пиксели чёрные. Теперь я додумался что нужно сначала нарисовать на буфер фон, а потом уже рисовать изображение. Но опять проблема: Рисую несколько картинок, вместо картинки 1920/1080(мой экран) просто чёрное(у картинки нет альфа пикселей), картинки других размеров, 100/100 например, в произвольных расположениях рисуются, но с чёрным прямоугольником в левом верхнем углу(у картинки нет альфа пикселей). Если закрасить главный фон(перед выводом изображения 1920/1080, то фон таким же и будет(картинка вообще никак не выводится, получается?), у маленьких картинок цвет прямоугольника такой же становится(походу это прозрачность(но альфа пикселей,ещё раз, там нет).
Я так понимаю, второй раз использовать SelectObject нельзя, да? В общем, подскажите в чём тут проблема?
class Imagee { HDC hdc; HBITMAP bm;
Imagee(HDC hdc, HBITMAP bm, another args) { this->hdc=CreateCompatibleDC(hdc); this->bm=bm; SelectObject(this->hdc,this->bm); }
void draw(int hdcc, int x,int y, int cx, int cy) { **StretchBlt(this->hdc,0,0,cx,cy,hdcc,x,y,cx,cy,SRCCOPY); SelectObject(this->hdc,this->bm); **
StretchBlt(this->hdcc,x,y,cx,cy,hdc,0,0,cx,cy,SRCCOPY); };
};
Imagee *image;
void render() { for(;;) {
//здесь я загружая через гди+ битмам, конвертирую его в хбитмап, записывая в перменную HBITMAP hbm
if(image==0) image=new Imagee(hdc_mem, hbm, x, y и т.д.);
image->draw(hdc_mem, x, y и т.д.);
StretchBlt(hdc_main,0,0,1920,1080,hdc_mem,0,0,1920,1080,SRCCOPY); //hdc_main главный дс, hdc_mem буфер
} }


Ответ

Отвечая на вопрос:
_Check_return_ ::HBITMAP LoadPng(_In_z_ const ::LPWSTR psz_file_path) { ::HBITMAP bitmap_handle{}; if(psz_file_path) { const ::BOOL embedded_color_management{TRUE}; ::Gdiplus::Bitmap bitmap{psz_file_path, embedded_color_management}; if(::Gdiplus::Status::Ok == bitmap.GetLastStatus()) { const ::Gdiplus::Color background{0, 0, 0, 0}; if(::Gdiplus::Status::Ok != bitmap.GetHBITMAP(background, &bitmap_handle)) { bitmap_handle = NULL; } } } return(bitmap_handle); }
Как ни странно, но Color::Transparent это не то же самое, что 0, 0, 0, 0. Еще стоит отметить что полученная картинка будет уже с предварительно умноженным (premultiplied) альфа каналом, т.е. пригодная для рисования посредством GdiAlphaBlend и прочих функций.

четверг, 2 мая 2019 г.

Почему у меня SetPixel не отрисовывает пиксель?

Почему у меня SetPixel не отрисовывает пиксель на форме?
WndProc proc uses ebx edi esi, hwnd:DWORD, wmsg:DWORD, wparam:DWORD, lparam:DWORD switch wmsg case WM_INITDIALOG invoke SendMessage, hwnd, WM_SETICON, 1, FUNC(LoadIcon, NULL, IDI_ASTERISK) case WM_PAINT invoke SetPixel, hwnd, 15, 20, 65285 case WM_CLOSE exit_program: invoke EndDialog, hwnd, 0 endsw xor eax,eax ret WndProc endp


Ответ

В качестве первого параметра SetPixel принимает не идентификатор окна, а идентификатор контекста устройства (в данном случае, идентификатор контекста окна). Контекст устройства упрощенно можно сравнить с холстом, на котором происходит рисование.
В вашем случае нужно получить этот идентификатор с помощью функции BeginPaint, нарисовать пиксель, потом освободить контекст при помощи EndPaint
Код для обработки сообщения WM_PAINT будет примерно такой:
invoke BeginPaint, hwnd, addr ps invoke SetPixel, eax, 15, 20, 65285 invoke EndPaint, hwnd, addr ps
Также необходимо объявить переменную ps, которая является структурой PAINTSTRUCT. Скорее всего определение этой структуры уже есть в каком-то из подключаемых файлов пакета MASM. addr - ключевое слово для подстановки адреса переменной.
Для нескольких вызовов SetPixel можно сделать например так:
invoke BeginPaint, hwnd, addr ps push ebx mov ebx, eax invoke SetPixel, ebx, 15, 20, 65285 invoke SetPixel, ebx, 30, 20, 65285 invoke SetPixel, ebx, 45, 20, 65285 pop ebx invoke EndPaint, hwnd, addr ps
Это вариант без использования дополнительной переменной. По соглашениям stdcall и cdecl, вызываемые процедуры не должны модифицировать регистры ebx, edi, esi (но могут модифицировать eax, ecx, edx), поэтому ebx, например, можно использовать для временного хранения значений, которые не должны затираться вызовами процедур. Но так как процедура WndProc тоже должна соответствовать соглашению stdcall, то перед модификацией ebx его значение нужно сохранить, а потом восстановить. Это можно сделать либо локально, вокруг места, где происходит модификция ebx (как показано в этом примере), либо сохранить в начале процедуры, а в конце (перед ret) - восстановить.
Еще одно замечание - код цвета лучше писать в шестнацатеричном виде, в данном случае это будет 00FF05h, тогда будет сразу понятно, что этот цвет ближе к зеленому (порядок цветов - 0x00bbggrr, согласно описанию типа COLORREF).

понедельник, 15 апреля 2019 г.

Как конвертировать цвет пикселя ABGR в ARGB?

ARGB getPixelColor(HWND hWnd, int x, int y) { HDC dc = GetDC(hWnd); ARGB color = GetPixel(dc, x, y); ReleaseDC(hWnd, dc); return color; }
использую эту функцию получаю значение ABGR например 0x000248ac но мне нужно ARGB 0x00ac4802 подскажите как мне "инвертировать" это значение?


Ответ

uint32_t ABGR_to_ARGB(uint32_t abgr) { uint32_t tmp = ((abgr >> 16) ^ abgr) & 0xFF; return abgr ^ ((tmp << 16) | tmp); }
Другой вариант (ссылаясь на ответ от @Maxim Egorushkin здесь)
uint32_t ABGR_to_ARGB(uint32_t abgr) { uint32_t grab = abgr >> 16 | abgr << 16; return (abgr & 0xFF0FF00) | (grab & 0x00FF00FF); }
Если ваш компилятор достаточно умен для того, чтобы сообразить, что abgr >> 16 | abgr << 16 - это ни что иное как циклический сдвиг abgr на 16, то этот вариант странслируется в очень компактный машинный код на платформах с инструкцией циклического сдвига.

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