Assembler
.NET
Delphi
Windows
Reversing&Cracking
Шаолинь
Other
Форум Monah'а

Чудо-курсор

Автор: Adrax

Подсистема Win32 базируется на сервере оконной подсистемы csrss.exe и драйвере win32k.sys и представляет собой "святую троицу": kernel32.dll, user32.dll и gdi32.dll. Kernel32.dll обеспечивает базовые функции работы с памятью и дисками. User32.dll нужен для создания окон. Gdi32.dll обеспечивает графический интерфейс GDI

В общем, как-то раз на форуме Исходников.Ру загорелся я идеей создать курсор в виде перекрестья во весь экран. Основная загвоздка - Windows ограничивает размер курсора максимум 32x32, потому, чтобы получить такое перекрестье, надо было написать приложение, отслеживающее позицию курсора и проводящее через эту точку две линии крест-накрест. Остальные форумчане отнеслись к этой идее с пониманием, а Deus даже создал рабочий прототип. Сегодня мы с вами сами сварганим такой курсорчик. Но сначала правильно будет сказать пару слов о GDI

GDI расшифровывается как Graphics Device Interface, и представляет собой интерфейс, который Windows использует для рисования 2D графики: точек, линий и даже букв. Этот интерфейс универсален для любых устройств: чтобы вывести изображение на экран, принтер, плоттер, используются одни и те же функции. Программа получает контекст устройства, рисует на нём средствами GDI, а потом уже драйвер устройства получает это изображение и пытается его вывести, исходя из своих возможностей (ведь нарисованная на контексте устройства картинка может иметь больше цветовых оттенков, чем может вывести девайс, и т.д.). Таким образом, контекст устройства служит нам как бы альбомным листом

Windows понимает 4 типа контекста устройств: контекст дисплея, контекст принтера, контекст памяти и контекст информации. Насчёт первых двух, надеюсь, всё понятно, контекст памяти позволяет рисовать на загруженной в память картинке, а контекст информации нужен, как написано, "для восстановления данных устройства"

Разумеется, чтобы рисовать, нужны инструменты. GDI предоставляет нам широкий выбор: это разнообразные перья, кисти, текстовые шрифты и битовые изображения
В WinXP графический интерфейс сменился на GDI+, добавивший ещё несколько нужных API-функций. В Vista, говорят, графический интерфейс претерпел ещё одну серию изменений и стал называться Avalon

Чудо-курсор я буду реализовывать средствами GDI и GDI+, а это значит, что я абсолютно не гарантирую работоспособности этой программы на до-XP'шных версиях Windows

Итак, начнём с основ: попытаемся понять, как же рисовать в окне. Чтобы в окне можно было рисовать, оно должно иметь так называемую клиентскую область. Если таковая имеется, то при обновлении окна Windows пошлёт ему сообщение WM_PAINT. В ответ на это сообщение принято вызывать функцию BeginPaint, которая делает две важные вещи: получает хэндл контекста устройства, на котором мы будем рисовать, и структуру PAINTSTRUCT, содержащую координаты invalid rectangle - той части окна, что нуждается в перерисовке. Получив хэндл контекста устройства, можно начинать свои художества, не забыв в конце вызвать функцию EndPaint. Таков традиционный путь

Можно и иначе: не вызывать BeginPaint, а самостоятельно получить хэндл контекста устройства функцией GetDC. Главное - не забыть его освободить потом, вызвав ReleaseDC

Ниже я приведу описуху вышеперечисленных функций из MSDN:

HDC GetDC(HWND hWnd);	// хэндл окна

HDC BeginPaint(
HWND hwnd, // хэндл окна
LPPAINTSTRUCT lpPaint // указатель на PAINTSTRUCT
);

Обе эти функции возвращают в eax хэндл контекста устройства. Поработав с ним, необходимо его освободить:

int ReleaseDC(
HWND hWnd, // хэндл окна
HDC hDC  // хэндл контекста устройства
);

BOOL EndPaint(
HWND hWnd, // хэндл окна
CONST PAINTSTRUCT *lpPaint  // указатель на PAINTSTRUCT
);

И ReleaseDC, и EndPaint возвращают 1, если всё хорошо, и 0, если всё плохо

Я предлагаю подойти к реализации нашего чудо-курсора постепенно, по пути осваивая премудрости GDI. Поэтому разобьём нашу статью на пункты:

1.Рисуем в окне линию

Чтобы нарисовать линию, мы должны определить её начало и конец. Начальная точка устанавливается функцией MoveToEx, координаты конечной - передаются функции LineTo. Вот их прототипы из MSDN:

BOOL MoveToEx(
HDC hdc, // хэндл контекста устройства
int X, // абсцисса точки
int Y, // ордината точки
LPPOINT lpPoint  // указатель на переменную типа POINT для хранения прежних координат
);

BOOL LineTo(
HDC hdc, // хэндл контекста устройства
int nXEnd, // абсцисса точки
int nYEnd  // ордината точки
);

Несколько слов про структуру POINT: она состоит из двух двойных слов, первое из которых хранит абсциссу точки, а второе - ординату. Функция MoveToEx, выделяя новую точку, сохраняет координаты прежней выделенной точки в переменную типа POINT, но нам прежние координаты до лампочки, поэтому вместо указателя на переменную мы подсунем функции NULL

Для рисования линии нужно перо. Перо в GDI создаётся функцией CreatePen, её прототип:

HPEN CreatePen(
int fnPenStyle, // стиль пера
int nWidth, // ширина пера в пикселях
COLORREF crColor  // цвет пера
);

Стилей у пера несколько: PS_SOLID - сплошная линия, PS_DASH - пунктирная линия, PS_DOT - линия рисуется точками, PS_DASHDOT - штрихпунктирная линия, PS_DASHDOTDOT - пунктир с двумя точками, PS_NULL - невидимая линия, PS_INSIDEFRAME - тоже сплошная, но какая-то особенная...
Цвет задаётся 4-байтовой структурой RGB. Первый её байт равен нулю, второй отвечает за синий цвет, третий - за зелёный, четвёртый - за красный. Каждый из этих байтов может принимать значение от 00h до FFh, и чем больше значение, тем интенсивнее цвет

Создав перо, нужно его взять функцией SelectObject, принимающей в качестве параметров хэндлы контекста устройства и пера. Возвращает эта функция хэндл прежнего пера, который просто так выкинуть нельзя - нужно его сохранить. Порисовав, нужно освободить перо, вызвав функцию DeleteObject и передав ей хэндл пера

Как видите, при работе с GDI приходится соблюдать неписаное правило "взял - положь!": взяли контекст - освободили контекст, взяли перо - освободили перо и т.д. В противном случае, если забывать освобождать объекты, мы получим т.н. "утечку", которая выразится в огромном потреблении памяти и может привести к краху нашего приложения

Ладненько, правило уяснили, апишки выучили... Let's code now!:

include 'win32axp.inc'
.data
  tityl db 'My program',0
  klass db 'My klass',0
  hdc dd ?;dword под хэндл устройства
  hp dd ?;dword под хэндл пера
  wc WNDCLASS 0,Procedura,0,0,NULL,NULL,NULL,COLOR_BTNFACE+1,NULL,klass
  msg MSG
  rc RECT;переменная под клиентскую область
  ps PAINTSTRUCT;переменная типа PAINTSTRUCT

.code
 fuck:
  invoke  GetModuleHandle,0
  mov     [wc.hInstance],eax
  invoke  LoadIcon,0,IDI_APPLICATION
  mov     [wc.hIcon],eax
  invoke  LoadCursor,0,IDC_ARROW
  mov     [wc.hCursor],eax
  invoke  RegisterClass,wc
invoke CreateWindowEx,0,klass,tityl,WS_VISIBLE+WS_DLGFRAME+WS_SYSMENU, 128,128,192,192,NULL, NULL,[wc.hInstance],NULL
  ;начало самое обычное
  invoke UpdateWindow,eax
  ;в eax - хэндл созданного окна.
  ;обновляем его, при этом в цикл
  ;обработки сообщений посылается
  ;WM_PAINT

 msg_loop:
   invoke  GetMessage,msg,NULL,0,0
   or      eax,eax
   jz      end_loop
   invoke  TranslateMessage,msg
   invoke  DispatchMessage,msg
   jmp     msg_loop
 end_loop:
   invoke  ExitProcess,[msg.wParam]

proc Procedura hwnd,wmsg,wparam,lparam
  push ebx
  push esi
  push edi
  cmp [wmsg],WM_DESTROY
  je Destroy
  cmp [wmsg],WM_PAINT;если сообщение - это WM_PAINT,
  je Paint;то прыгаем на метку Paint
  invoke DefWindowProc,[hwnd],[wmsg],[wparam],[lparam]
  jmp finish

 Destroy:
  invoke  PostQuitMessage,0
  xor     eax,eax

 Paint:
 ;обработчик сообщения WM_PAINT
   invoke BeginPaint,[hwnd],ps
   mov [hdc],eax
   ;получили хэндл контекста устройства,
   ;положили его в hdc
   invoke GetClientRect,[hwnd],rc
   ;получили клиентскую область
   invoke CreatePen,PS_SOLID,3,00ff0000h
   ;создали перо: сплошная линия,
   ;толщина 3 пикселя, цвет синий
   ;(потому что синяя составляющая RGB
   ;выставлена на максимум - FFh,
   ;а остальные равны 0)
   mov [hp],eax
   ;кладём хэндл пера в hp
   invoke SelectObject,[hdc],[hp]
   ;"берём" перо
   push eax
   ;хэндл старого пера сохраняем в стеке
   invoke MoveToEx,[hdc],[rc.left],[rc.top],NULL
   ;устанавливаем перо в верхний левый угол
   ;клиентской области
   invoke LineTo,[hdc],[rc.right],[rc.bottom]
   ;и рисуем линию до нижнего правого угла
   pop eax
   ;выталкиваем хэндл старого пера из стека
   invoke DeleteObject,[hp]
   ;освобождаем перо
   invoke EndPaint,[hwnd],ps
   ;освобождаем контекст устройства

 finish:
  pop edi
  pop esi
  pop ebx
  ret
endp
.end fuck

Всё просто, не правда ли? Переходим к следующему этапу...

2.Поиграем с прозрачностью

Во всех предыдущих статьях мы вызывали функцию CreateWindowEx с первым параметром, равным 0. Сегодня мы подставим на место этого нуля расширенный стиль окна WS_EX_LAYERED. Обычный стиль окна мы также изменим - на WS_POPUP+WS_VISIBLE, чтобы у окошка не было элементов управления в заголовке

До окна, имеющего расширенный стиль WS_EX_LAYERED, можно достучаться функцией SetLayeredWindowAttributes, имеющей следующий прототип:

SetLayeredWindowAttributes(
HWND hwnd, // хэндл окна
COLORREF crKey, // цвет прозрачности
BYTE bAlpha, // степень прозрачности
DWORD dwFlags // флаги
);

Флагов у этой функции два: LWA_ALPHA и LWA_COLORKEY. Если мы используем LWA_ALPHA, то цвет прозрачности для нас значения не имеет: мы будем управлять прозрачностью всего окна - важна лишь степень: от 00h до FFh, причём, чем больше - тем непрозрачнее. Для примера смастерим такой код:

include 'win32axp.inc'
.data
  tityl db 'My program',0
  klass db 'My klass',0
  hdc dd ?
  hp dd ?
  hwnd dd ?;dword под хэндл окна
  wc WNDCLASS 0,Procedura,0,0,NULL,NULL,NULL,COLOR_BTNFACE+1,NULL,klass
  msg MSG
  rc RECT
  ps PAINTSTRUCT

.code
 fuck:
  invoke  GetModuleHandle,0
  mov     [wc.hInstance],eax
  invoke  LoadIcon,0,IDI_APPLICATION
  mov     [wc.hIcon],eax
  invoke  LoadCursor,0,IDC_ARROW
  mov     [wc.hCursor],eax
  invoke  RegisterClass,wc
invoke CreateWindowEx, WS_EX_LAYERED,klass,tityl, WS_POPUP+WS_VISIBLE, 128,128,192,192,NULL, NULL,[wc.hInstance],NULL
  ;сменили стили
  mov [hwnd],eax
  invoke SetLayeredWindowAttributes,[hwnd],00h,0c4h,LWA_ALPHA
  ;делаем всё окно прозрачным.
  ;степень прозрачности C4h
  invoke UpdateWindow,[hwnd]

 msg_loop:
   invoke  GetMessage,msg,NULL,0,0
   or      eax,eax
   jz      end_loop
   invoke  TranslateMessage,msg
   invoke  DispatchMessage,msg
   jmp     msg_loop
 end_loop:
   invoke  ExitProcess,[msg.wParam]

proc Procedura hwnd,wmsg,wparam,lparam
  push ebx
  push esi
  push edi
  cmp [wmsg],WM_DESTROY
  je Destroy
  cmp [wmsg],WM_PAINT
  je Paint
  invoke DefWindowProc,[hwnd],[wmsg],[wparam],[lparam]
  jmp finish

 Destroy:
  invoke  PostQuitMessage,0
  xor     eax,eax

 Paint:
   invoke BeginPaint,[hwnd],ps
   mov [hdc],eax
   invoke GetClientRect,[hwnd],rc
   invoke CreatePen,PS_SOLID,3,0000ff00h
   ;перо сделаем зелёного цвета
   mov [hp],eax
   invoke SelectObject,[hdc],[hp]
   push eax
   invoke MoveToEx,[hdc],[rc.left],[rc.top],NULL
   invoke LineTo,[hdc],[rc.right],[rc.bottom]
   pop eax
   invoke DeleteObject,[hp]
   invoke EndPaint,[hwnd],ps

 finish:
  pop edi
  pop esi
  pop ebx
  ret
endp
.end fuck

Поиграйте со значениями степени прозрачности и убедитесь в том, что применение флага LWA_ALPHA позволяет управлять прозрачностью целого окна. Но нам это не подходит: нам нужно сделать прозрачный фон и на нём - видимую линию. Поэтому мы применим флаг LWA_COLORKEY, который позволяет "прозрачнить" определённый цвет. Вот только нам придётся изменить параметры структуры wc типа WNDCLASS: мы используем как цвет окна COLOR_BTNFACE+1 - неоднородный, градиентный. А для "запрозрачнивания" нужен однородный оттенок. Поэтому код перепишем так (привожу только начало):

include 'win32axp.inc'
.data
  tityl db 'My program',0
  klass db 'My klass',0
  hwnd dd ?
  hdc dd ?
  hp dd ?
  wc WNDCLASS 0,Procedura,0,0,NULL,NULL,NULL,00h,NULL,klass
  ;присваиваем окну цвет 00h (чёрный, вроде бы...)
  msg MSG
  rc RECT
  ps PAINTSTRUCT

.code
 fuck:
  invoke  GetModuleHandle,0
  mov     [wc.hInstance],eax
  invoke  LoadIcon,0,IDI_APPLICATION
  mov     [wc.hIcon],eax
  invoke  LoadCursor,0,IDC_ARROW
  mov     [wc.hCursor],eax
  invoke  RegisterClass,wc
invoke CreateWindowEx, WS_EX_LAYERED,klass,tityl, WS_POPUP+WS_VISIBLE,128,128,192,192,NULL, NULL,[wc.hInstance],NULL
  ;сменили стили
  mov [hwnd],eax
  invoke SetLayeredWindowAttributes,[hwnd],00h,00h,LWA_COLORKEY
  ;делаем цвет 00h прозрачным
  .....

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

3.Курсор

Итак, мы научились делать окна прозрачными и рисовать на них. Однако, для того, чтобы отвечать поставленной задаче, наше окно должно быть ещё и "прозрачным для мыши", т.е. "пропускать" мышиные клики до нижележащих окон. Это элементарно реализуется путём использования расширенных стилей окна. Поэтому задумаемся над другим вопросом: как отслеживать курсор?

Для начала нужно иметь представление о самой системе экранных координат. В своей программе я использовал следующие имена переменных: xx и yy - для абсциссы и ординаты текущего положения курсора, shirina и vysota - для ширины и высоты экрана соответственно. Система экранных координат будет выглядеть следующим образом:

Чтобы узнать ширину и высоту экрана, мы вызовем функцию GetSystemMetrics с параметрами SM_CXSCREEN и SM_CYSCREEN соответственно. Для определения текущей позиции курсора заюзаем GetCursorPos, передав ей указатель на переменную типа POINT, куда функция запишет координаты. Отслеживать перемещение курсора будем по возникающему сообщению WM_MOUSEMOVE. Let's code!

include 'win32axp.inc'
.data
 tityl db 'My program',0
 klass db 'My klass',0
 hwnd dd ?
 hdc dd ?
 hp dd ?
  vysota dd ?
  shirina dd ?
  xx dd ?
  yy dd ?
 wc WNDCLASS 0,Procedura,0,0,NULL,NULL,NULL,00h,NULL,klass
 msg MSG
 rc RECT
 ps PAINTSTRUCT
  lpo POINT;переменная под координаты курсора

.code
 fuck:
  invoke GetSystemMetrics,SM_CXSCREEN
  mov [shirina],eax
  invoke GetSystemMetrics,SM_CYSCREEN
  mov [vysota],eax
  ;определили ширину и высоту экрана
  invoke  GetModuleHandle,0
  mov     [wc.hInstance],eax
  invoke  LoadIcon,0,IDI_APPLICATION
  mov     [wc.hIcon],eax
  invoke  LoadCursor,0,IDC_ARROW
  mov     [wc.hCursor],eax
  invoke  RegisterClass,wc
invoke CreateWindowEx,WS_EX_TOPMOST or WS_EX_TRANSPARENT or WS_EX_LAYERED, klass,tityl,WS_POPUP+WS_VISIBLE,0,0,[shirina],[vysota], NULL,NULL,[wc.hInstance],NULL
  ;создали окно, размером во весь экран,
  ;"прозрачное для мыши"
  mov [hwnd],eax
  invoke SetLayeredWindowAttributes,[hwnd],00h,00h,LWA_COLORKEY
  invoke SetWindowPos,[hwnd],HWND_TOPMOST,0,0,0,0,SWP_NOMOVE or SWP_NOSIZE
  ;несмотря на то, что окно у нас WS_EX_TOPMOST,
  ;его необходимо "вручную" передвинуть наверх.
  ;ещё мы объявляем его неперемещаемым и не меняющим размеров
  invoke UpdateWindow,[hwnd]

 msg_loop:
   invoke  GetMessage,msg,NULL,0,0
   or      eax,eax
   jz      end_loop
   invoke  TranslateMessage,msg
   invoke  DispatchMessage,msg
   jmp     msg_loop
 end_loop:
   invoke  ExitProcess,[msg.wParam]

proc Procedura hwnd,wmsg,wparam,lparam
  push ebx
  push esi
  push edi
  cmp [wmsg],WM_MOUSEMOVE
  je Moving
  cmp [wmsg],WM_DESTROY
  je Destroy
  cmp [wmsg],WM_PAINT
  je Paint
  invoke DefWindowProc,[hwnd],[wmsg],[wparam],[lparam]
  jmp finish

 Destroy:
  invoke  PostQuitMessage,0
  xor     eax,eax

 Paint:
   invoke BeginPaint,[hwnd],ps
   mov [hdc],eax
   invoke GetClientRect,[hwnd],rc
   invoke CreatePen,PS_SOLID,3,0000ff00h
   mov [hp],eax
   invoke SelectObject,[hdc],[hp]
   push eax

   invoke GetCursorPos,lpo
   ;получили координаты курсора,
   ;положили их в переменную lpo
   mov eax,dword [lpo]
   mov [xx],eax
   ;считали первые 4 байта из lpo - абсциссу
   ;и положили в xx
   mov eax,dword [lpo+4]
   mov [yy],eax
   ;считали следующие 4 байта из lpo - ординату
   ;и положили в yy
   invoke MoveToEx,[hdc],0,[yy],NULL
   invoke LineTo,[hdc],[shirina],[yy]
   invoke MoveToEx,[hdc],[xx],0,NULL
   invoke LineTo,[hdc],[xx],[vysota]
   ;прочертили перекрестье линий

   pop eax
   invoke DeleteObject,[hp]
   invoke EndPaint,[hwnd],ps

 Moving:
   invoke InvalidateRect,[hwnd],rc,TRUE
   invoke UpdateWindow,[hwnd]
   ;обрабатываем сообщение WM_MOUSEMOVE:
   ;объявляем клиентскую область недействительной
   ;и обновляем окно, чтобы система послала ему
   ;WM_PAINT

 finish:
  pop edi
  pop esi
  pop ebx
  ret
endp
.end fuck

Картина впечатляющая: перекрестье линий верно следует за курсором, однако линии не стираются, а потому очень скоро ничего не будет видно:

4.Ластик

Что же нам делать? Перья в инструментарии GDI есть, а вот ластиков не придумали. Поэтому придётся обратиться к битовой арифметике: 1 xor 1 = 0. Значит, нам нужно перо, реализующее операцию "исключающее ИЛИ". Такое перо создаётся функцией SetROP2, миксующей цвета фона и пера:

int SetROP2(
HDC hdc, // хэндл контекста устройства
int fnDrawMode // режим рисования
);

Режимов куча! Перечислю: R2_BLACK - без комментариев, R2_COPYPEN - повторяет цвет пера, R2_MASKNOTPEN - микс общих оттенков фона и инвертированного цвета пера, R2_MASKPEN - микс общих оттенков пера и фона, R2_MASKPENNOT - микс общих оттенков цвета пера и инвертированного фона, R2_MERGENOTPEN - микс цвета фона и инвертированного цвета пера, R2_MERGEPEN - цвет пера + цвет фона, R2_MERGEPENNOT - цвет пера + инвертированный фон, R2_NOP - цвет не меняется (невидимая линия?), R2_NOT - инвертированный цвет фона, R2_NOTCOPYPEN - инвертированный цвет пера, R2_NOTMASKPEN - инвертированный R2_MASKPEN, R2_NOTMERGEPEN - инвертированный R2_MERGEPEN, R2_NOTXORPEN - инвертированный R2_XORPEN, R2_WHITE - без комментариев, R2_XORPEN - микс оттенков, различных у пера и фона

Мне приглянулся режим №5. Его я и буду использовать в своей программе. Суть такова: провели пером один раз - линия появилась на экране, провели по тому же месту ещё раз - линия исчезла. Вполне удобно и отвечает нашей задумке. Воплотим же идею в коде!

По сравнению с предыдущим исходником изменился лишь обработчик сообщения WM_PAINT, его я здесь и привожу:

Paint:
  invoke GetCursorPos,lpo
  mov eax,dword [lpo]
  mov [xx],eax
  mov eax,dword [lpo+4]
  mov [yy],eax
  ;определили позицию курсора
  invoke BeginPaint,[hwnd],ps
  mov [hdc],eax
  invoke GetClientRect,[hwnd],rc
  invoke CreatePen,PS_SOLID,2,0ff0000h
  mov [hp],eax
  invoke SelectObject,[hdc],[hp]
  push eax
  ;создали и взяли в руки перо
  invoke SetROP2,[hdc],5
  ;выбираем 5 режим рисования
  invoke MoveToEx,[hdc],0,[yy],NULL
  invoke LineTo,[hdc],[shirina],[yy]
  invoke MoveToEx,[hdc],[xx],0,NULL
  invoke LineTo,[hdc],[xx],[vysota]
  ;чертим перекрестье
  invoke Sleep,17
  ;ждём 17 миллисекунд
  invoke MoveToEx,[hdc],0,[yy],NULL
  invoke LineTo,[hdc],[shirina],[yy]
  invoke MoveToEx,[hdc],[xx],0,NULL
  invoke LineTo,[hdc],[xx],[vysota]
  ;затираем нарисованное перекрестье
  pop eax
  invoke DeleteObject,[hp]
  invoke EndPaint,[hwnd],ps
  ;конец обработчика

Вау! То, что нужно! Но огорчает одна маленькая деталь: мерцание перекрестья, причём частота мерцания меняется в зависимости от загруженности процессора (читатели раздела Windows уже, наверное, догадались, что всё дело в функции Sleep, отдающей квант процессорного времени). Видимо, стирать линии надо как-то иначе...

Есть у меня и другой вариант обработчика сообщения WM_PAINT:

Paint:
  invoke BeginPaint,[hwnd],ps
  mov [hdc],eax
  invoke GetClientRect,[hwnd],rc
  invoke CreatePen,PS_SOLID,2,0ff0000h
  mov [hp],eax
  invoke SelectObject,[hdc],[hp]
  push eax
  invoke SetROP2,[hdc],5
  invoke MoveToEx,[hdc],0,[yy],NULL
  invoke LineTo,[hdc],[shirina],[yy]
  invoke MoveToEx,[hdc],[xx],0,NULL
  invoke LineTo,[hdc],[xx],[vysota]
  ;прочертили перекрестье.
  ;в первый раз оно ляжет вне видимой области,
  ;т.к. позиция точки по умолчанию 0;0,
  ;а потом будет затирать уже нарисованное
  invoke GetCursorPos,lpo
  mov eax,dword [lpo]
  mov [xx],eax
  mov eax,dword [lpo+4]
  mov [yy],eax
  ;определяем позицию курсора и чертим крест
  invoke MoveToEx,[hdc],0,[yy],NULL
  invoke LineTo,[hdc],[shirina],[yy]
  invoke MoveToEx,[hdc],[xx],0,NULL
  invoke LineTo,[hdc],[xx],[vysota]
  pop eax
  invoke DeleteObject,[hp]
  invoke EndPaint,[hwnd],ps

Этот вариант ещё лучше! Никакого мерцания, всё замечательно... кроме 70-100%-й загрузки проца:(

Скорее всего, это связано с тем, что после обработчика WM_PAINT у меня сразу же идёт обработчик WM_MOUSEMOVE, и программа, исполняясь линейно, лишний раз перерисовывает окно. Я пытался с этим бороться, но тупая вставка jmp finish в конец обработчика или смена их взаимного расположения приводят к неработоспособности программы, а вызов Sleep приводит к мерцанию перекрестья, что тоже нехорошо

В общем, так я до конца и не реализовал свою идею. Задача написания чудо-курсора остаётся нерешённой. Я надеюсь, читателям эта тема покажется интересной, и они помогут мне сбалансировать и оптимизировать код этой маленькой программки. Если вам удастся улучшить её - пишите мне, и мы вместе напишем следующую статью по этой теме. А в том, что статьи будут, не сомневайтесь - на примере этой маленькой программки я хочу показать вам множество интереснейших вещей, добавляя в исходный код новые фишки

До новых встреч на страницах сайта RFTeam!!


Добавлено

Снизить нагрузку на процессор поможет invoke Sleep,1 в самом начале обработчика сообщения WM_MOUSEMOVE. Читатели, знакомые с основами вытесняющей многозадачности Windows, уже поняли, что таким образом мы будем осуществлять немедленное переключение контекста, передавая право на исполнение другому процессу. Этот способ, конечно, не лищён недостатков, но помогает существенно снизить ресурсоёмкость нашего приложения, не вызывая заметного мерцания перекрестья

Главная страница Windows Delphi Assembler .NET Delphi Reversing Шаолинь Other Форум Monah'а

Создатель команды, главный редактор, художник и web-мастер: Adrax

Дизайн сайта: WargaL

Ответственный за форум: Monah

RussianFuckersTeam©