Простейшее окно с анимированной иконкой и анимированным курсором

Тема в разделе "WASM.BEGINNERS", создана пользователем Mikl___, 16 сен 2026.

  1. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.289
    Есть ли идеи? С чего начать? Что следует посмотреть? Существует ли редактор, который перегонит gif-файл в иконку или курсор? Достаточно ли просто найти анимированную иконку/курсор и присоединить ее при помощи LoadCursorFromFile к WC структуре и вызвать RegisterClassEx? Или нужны еще какие-то пляски с бубном?
     
    Последнее редактирование: 16 сен 2026
  2. Application

    Application Moderator Команда форума

    Публикаций:
    1
    Регистрация:
    8 дек 2007
    Сообщения:
    1.039
    Надеюсь поможет, надо проверять на пракике:

    --- ---


    Анимированный курсор: всё проще, чем кажется


    Вы почти правы. Последовательность действий для курсора действительно проста:
    1. Конвертируйте: Возьмите ваш GIF и превратите его в формат .ani.
    2. Загрузите: Используйте LoadCursorFromFile() для получения хэндла.
    3. Примените: Передайте этот хэндл в поле hCursor структуры WNDCLASSEX перед вызовом RegisterClassEx().
    --- ---

    Анимированная иконка: нужен «костыль».

    Здесь есть важный нюанс. Стандартные поля hIcon и hIconSm ожидают статическую иконку (.ico). Если вы попытаетесь передать туда .ani, система, скорее всего, просто покажет первый кадр или ничего-6-12.
    Чтобы сделать иконку анимированной, нужно самостоятельно управлять её отрисовкой:
    1. Загрузите несколько статических иконок (HICON) в массив.
    2. Создайте таймер (SetTimer).
    3. В обработчике WM_TIMER меняйте текущую иконку окна, отправляя сообщение WM_SETICON.
    --- ---

    1. Использование библиотеки Clickgen.
    Эта библиотека специально создана для сборки курсоров (включая Windows .ani) и абстрагирует всю работу с форматом.

    pip install clickgen

    2. Подготовка кадров:
    Вам нужно сначала разбить ваш GIF на отдельные PNG-файлы (по одному на каждый кадр). Это можно сделать любым удобным способом (например, через Pillow или онлайн-конвертер). Пусть они лежат в папке frames/ с именами типа frame-001.png, frame-002.png и т.д.

    3. Скрипт конвертации:
    Код (Text):
    1. from glob import glob
    2. from clickgen.parser import open_blob
    3. from clickgen.writer import to_win
    4.  
    5. # 1. Собираем пути ко всем PNG-кадрам
    6. fnames = sorted(glob("frames/*.png"))
    7.  
    8. # 2. Читаем файлы как бинарные данные
    9. pngs = []
    10. for f in fnames:
    11.     with open(f, "rb") as p:
    12.         pngs.append(p.read())
    13.  
    14. # 3. Создаем объект курсора.
    15. # hotspot — это координаты "горячей точки" (x, y), которую система считает точкой клика.
    16. # Для стрелки это обычно (0, 0), для других курсоров нужно подбирать.
    17. cur = open_blob(pngs, hotspot=(0, 0))
    18.  
    19. # 4. Конвертируем в формат Windows
    20. # 'to_win' возвращает кортеж: (расширение_файла, бинарные_данные)
    21. ext, result = to_win(cur.frames)
    22.  
    23. # 5. Сохраняем результат
    24. output_filename = f"my_cursor{ext}"
    25. with open(output_filename, "wb") as o:
    26.     o.write(result)
    27.  
    28. print(f"Готово! Файл сохранен как {output_filename}")
    Самая важная часть в коде — параметр hotspot в функции open_blob. Это координаты (x, y) внутри картинки, которые Windows будет считать «острием» курсора. Если поставить их неправильно (например, для курсора-руки поставить в левый верхний угол), то при клике система будет думать, что вы кликаете не туда, куда указывает курсор.
    Для простой стрелки обычно подходит (0, 0), но в некоторых случаях (например, если у вас есть прозрачные поля вокруг рисунка) их нужно подбирать вручную.

    --- ---

    Чем еще можно конвертировать GIF в ANI?

    С конвертацией GIF в .ani проблем нет, есть несколько путей:
    • Онлайн-сервисы: Самый быстрый вариант. Например, iotools.cloud позволяет загрузить GIF, выбрать размер, задать «горячую точку» (hotspot) и скачать готовый .ani-4.
    • Специализированный софт: AniTuner — бесплатная программа для Windows, умеет создавать и редактировать .ani из GIF, AVI или набора картинок-14.
    • Steam: Simple Cursor Maker — платная, но удобная утилита в Steam, которая конвертирует GIF/PNG в .ani и позволяет сразу собрать установочный пакет-7.
    --- ---
     
    Mikl___ нравится это.
  3. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.289
    Application, спасибо :good3:
    Нашел статью у Manhanter'a Создание анимированной иконки приложения Там он загружает 8 иконок,
    Код (ASM):
    1.   .wminitdialog: invoke  LoadIcon,[hInstance],1; Загрузить иконку    
    2.         invoke  SendMessage,[hwnddlg],WM_SETICON,ICON_BIG,eax; Установить иконку окна    
    3.         invoke  SetTimer,[hwnddlg],1,100,NULL; Установить таймер обновления иконки
    4.         mov     [nIcon],1
    5.         jmp     .processed
    6.   .animate: inc     [nIcon]; Анимация иконки окна
    7.         cmp     [nIcon],8
    8.         jbe     @f    
    9.         mov     [nIcon],1; Перейти к первому кадру
    10.   @@:   invoke  LoadIcon,[hInstance],[nIcon]; Загрузить следующую иконку    
    11.         invoke  SendMessage,[hwnddlg],WM_SETICON,ICON_BIG,eax; Обновить иконку окна
    12.         jmp     .processed
     
    Последнее редактирование: 16 сен 2026
    Application нравится это.
  4. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.289
    Код (ASM):
    1. ; GUI #
    2. include win64a.inc
    3. idTimer equ 1
    4. SAVE macro REG
    5.        for R,<REG>
    6.               push R
    7.        endm
    8. endm
    9. .code
    10. WinMain proc
    11. local msg:MSG
    12.        xor ebx,ebx
    13.        lea rdi,ClassName
    14.        lea rsi,WndProc
    15.        invoke LoadCursorFromFileA,"Printer.ani"
    16. SAVE <rbx,rdi,rbx,COLOR_WINDOWTEXT,rax,rbx,IMAGE_BASE,rbx,rsi,(sizeof WNDCLASSEX)>
    17.        invoke RegisterClassExA,rsp;в rsp адрес WNDCLASSEX
    18. SAVE <rbx,IMAGE_BASE,rbx,rbx,300,600,100,100>
    19.        sub esp,20h
    20.        invoke CreateWindowExA,0,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE ;Стили окна
    21.        lea edi,msg
    22. @@:    invoke GetMessageA,edi,NULL,0,0
    23.        invoke DispatchMessageA,edi
    24.        jmp @b
    25. WinMain endp
    26. WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    27.        mov hWnd,rcx
    28. for uMsg,<DESTROY,CREATE,TIMER>
    29.        cmp edx,WM_&uMsg
    30.        je wm&uMsg
    31. endm
    32.        leave
    33.        jmp NtdllDefWindowProc_
    34. wmDESTROY:invoke KillTimer,hWnd,idTimer
    35.        invoke RtlExitUserProcess,NULL
    36. wmCREATE:invoke  SetTimer,hWnd,idTimer,100,NULL
    37.        jmp wmBYE
    38. wmTIMER:and hIcon,111b
    39.        inc hIcon
    40.        invoke  LoadIconA,IMAGE_BASE,hIcon
    41.        invoke  SendMessageA,hWnd,WM_SETICON,ICON_BIG,rax
    42. wmBYE: leave
    43.        retn
    44. WndProc endp
    45. .data
    46. ClassName db "Анимированные курсор и иконка",0
    47. hIcon dq 1
    48. end
    rc-файл
    Код (C):
    1. 1 ICON "icon1.ico"
    2. 2 ICON "icon2.ico"
    3. 3 ICON "icon3.ico"
    4. 4 ICON "icon4.ico"
    5. 5 ICON "icon5.ico"
    6. 6 ICON "icon6.ico"
    7. 7 ICON "icon7.ico"
    8. 8 ICON "icon8.ico"
    В аттаче ani-курсор, фазы для иконки с сайта Manhunter'a, asm/rc/exe-файлы
     

    Вложения:

    • animate.zip
      Размер файла:
      14,8 КБ
      Просмотров:
      36
    Последнее редактирование: 25 сен 2026 в 17:27
    GRAFik и Application нравится это.
  5. GRAFik

    GRAFik Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2020
    Сообщения:
    558
    На базе кода от Mikl___ сделал ещё один вариант - свободный от "зашифрованных" макросов Mikl___ . :)
    Код (ASM):
    1. option casemap:none
    2. ; ==========================================================
    3. ; Собирается: ml64 + rc + link /ENTRY:WinMain
    4. ; Стек НЕ "дышит", как у VS2022
    5. ; ==========================================================
    6.  
    7. WM_CREATE       equ 1
    8. WM_DESTROY      equ 2
    9. WM_TIMER        equ 0113h
    10. WM_SETICON      equ 80h
    11. ICON_BIG        equ 1
    12. ICON_SMALL      equ 0
    13. WS_OVERLAPPEDWINDOW equ 000CF0000h
    14. WS_VISIBLE      equ 10000000h
    15. IDC_ARROW       equ 32512
    16. IMAGE_CURSOR    equ 2
    17. COLOR_WINDOW    equ 5
    18.  
    19. ; ---------- ПРОТОТИПЫ СТРУКТУР ----------
    20. MY_POINT STRUCT
    21.     pt_x    DWORD ?
    22.     pt_y    DWORD ?
    23. MY_POINT ENDS
    24.  
    25. MY_MSG STRUCT
    26.     msg_hwnd        QWORD ?
    27.     msg_message     DWORD ?
    28.     msg_pad1        DWORD ?
    29.     msg_wParam      QWORD ?
    30.     msg_lParam      QWORD ?
    31.     msg_time        DWORD ?
    32.     msg_pad2        DWORD ?
    33.     msg_pt          MY_POINT <>
    34. MY_MSG ENDS
    35.  
    36. MY_WNDCLASSEXA STRUCT
    37.     wc_cbSize           DWORD ?
    38.     wc_style            DWORD ?
    39.     wc_lpfnWndProc      QWORD ?
    40.     wc_cbClsExtra       DWORD ?
    41.     wc_cbWndExtra       DWORD ?
    42.     wc_hInstance        QWORD ?
    43.     wc_hIcon            QWORD ?
    44.     wc_hCursor          QWORD ?
    45.     wc_hbrBackground    QWORD ?
    46.     wc_lpszMenuName     QWORD ?
    47.     wc_lpszClassName    QWORD ?
    48.     wc_hIconSm          QWORD ?
    49. MY_WNDCLASSEXA ENDS
    50.  
    51. includelib user32.lib
    52. includelib kernel32.lib
    53.  
    54. extern LoadIconA:PROC
    55. extern LoadCursorA:PROC
    56. extern LoadCursorFromFileA:PROC
    57. extern LoadImageA:PROC
    58. extern RegisterClassExA:PROC
    59. extern CreateWindowExA:PROC
    60. extern GetMessageA:PROC
    61. extern DispatchMessageA:PROC
    62. extern DefWindowProcA:PROC
    63. extern SendMessageA:PROC
    64. extern SetTimer:PROC
    65. extern GetModuleHandleA:PROC
    66. extern ExitProcess:PROC
    67.  
    68. .data
    69. ClassName   db 0C0h,0EDh,0E8h,0ECh,0E8h,0F0h,0EEh,0E2h,0E0h,0EDh,0EDh,0FBh,0E5h,20h,0EAh,0F3h,0F0h,0F1h,0EEh,0F0h,20h,0E8h,20h,0E8h,0EAh,0EEh,0EDh,0EAh,0E0h,0
    70. szAniFile   db "Printer.ani",0
    71. g_IconId    dq 1
    72. g_hInstance dq 0
    73. g_hCursor   dq 0
    74. g_Msg       MY_MSG <>
    75.  
    76. .code
    77.  
    78. WndProc proc FRAME
    79.     sub rsp, 38h
    80.     .allocstack 38h
    81.     .endprolog
    82.     mov qword ptr [rsp+20h], rcx
    83.     cmp edx, WM_DESTROY
    84.     je  _WmDestroy
    85.     cmp edx, WM_CREATE
    86.     je  _WmCreate
    87.     cmp edx, WM_TIMER
    88.     je  _WmTimer
    89. _WmDefault:
    90.     mov rcx, qword ptr [rsp+20h]
    91.     call DefWindowProcA
    92.     jmp _WmExit
    93. _WmDestroy:
    94.     xor ecx, ecx
    95.     call ExitProcess
    96. _WmCreate:
    97.     mov rcx, qword ptr [rsp+20h]
    98.     mov edx, 1
    99.     mov r8d, 100
    100.     xor r9d, r9d
    101.     call SetTimer
    102.     xor eax, eax
    103.     jmp _WmExit
    104. _WmTimer:
    105.     mov rax, g_IconId
    106.     and rax, 7
    107.     inc rax
    108.     mov g_IconId, rax
    109.     mov rcx, g_hInstance
    110.     mov rdx, rax
    111.     call LoadIconA
    112.     test rax, rax
    113.     jz _WmExit
    114.     mov rcx, qword ptr [rsp+20h]
    115.     mov edx, WM_SETICON
    116.     mov r8d, ICON_BIG
    117.     mov r9, rax
    118.     call SendMessageA
    119.     mov rcx, qword ptr [rsp+20h]
    120.     mov edx, WM_SETICON
    121.     mov r8d, ICON_SMALL
    122.     mov r9, rax
    123.     call SendMessageA
    124.     xor eax, eax
    125. _WmExit:
    126.     add rsp, 38h
    127.     ret
    128. WndProc endp
    129.  
    130. public WinMain
    131.  
    132. WinMain proc FRAME
    133.      ; Приводим в порядок стек : C8h = shadow 20h + args 40h + WNDCLASSEXA 50h + выравнивание 18h
    134.     sub rsp, 0C8h
    135.     .allocstack 0C8h
    136.     .endprolog
    137.  
    138.     ; --- WinMain - стабильный RSP до конца ---
    139.  
    140.     xor ecx, ecx
    141.     call GetModuleHandleA
    142.     mov g_hInstance, rax
    143.  
    144.     mov rcx, rax
    145.     mov rdx, 100
    146.     call LoadCursorA
    147.     test rax, rax
    148.     jnz _HaveCur
    149.     mov rcx, g_hInstance
    150.     mov rdx, 100
    151.     mov r8d, IMAGE_CURSOR
    152.     xor r9d, r9d
    153.     mov qword ptr [rsp+20h], 0
    154.     mov qword ptr [rsp+28h], 0
    155.     call LoadImageA
    156.     test rax, rax
    157.     jnz _HaveCur
    158.     lea rcx, szAniFile
    159.     call LoadCursorFromFileA
    160.  
    161. _HaveCur:
    162.     mov g_hCursor, rax
    163.     test rax, rax
    164.     jnz _LoadIcon
    165.     xor ecx, ecx
    166.     mov edx, IDC_ARROW
    167.     call LoadCursorA
    168.     mov g_hCursor, rax
    169.  
    170. _LoadIcon:
    171.     mov rcx, g_hInstance
    172.     mov rdx, g_IconId
    173.     call LoadIconA
    174.     mov rbx, rax
    175.     mov qword ptr [rsp+30h], rbx ; сохраним локально (вне перекрытия с будущими args)
    176.  
    177.     ; WNDCLASSEXA на rsp+40h (80 байт) -> занимает 40h..90h, не пересекается с shadow
    178.     lea rcx, [rsp+40h]
    179.     mov dword ptr [rcx+MY_WNDCLASSEXA.wc_cbSize], 80
    180.     mov dword ptr [rcx+MY_WNDCLASSEXA.wc_style], 0
    181.     lea rax, WndProc
    182.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_lpfnWndProc], rax
    183.     mov dword ptr [rcx+MY_WNDCLASSEXA.wc_cbClsExtra], 0
    184.     mov dword ptr [rcx+MY_WNDCLASSEXA.wc_cbWndExtra], 0
    185.     mov rax, g_hInstance
    186.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_hInstance], rax
    187.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_hIcon], rbx
    188.     mov rax, g_hCursor
    189.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_hCursor], rax
    190.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_hbrBackground], COLOR_WINDOW+1
    191.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_lpszMenuName], 0
    192.     lea rax, ClassName
    193.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_lpszClassName], rax
    194.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_hIconSm], rbx
    195.  
    196.     call RegisterClassExA
    197.  
    198.     ; CreateWindowExA использует тот же rsp, без доп. sub
    199.     xor ecx, ecx
    200.     lea rdx, ClassName
    201.     lea r8, ClassName
    202.     mov r9d, WS_OVERLAPPEDWINDOW or WS_VISIBLE
    203.  
    204.     mov dword ptr [rsp+20h], 100
    205.     mov dword ptr [rsp+28h], 100
    206.     mov dword ptr [rsp+30h], 600
    207.     mov dword ptr [rsp+38h], 300
    208.     mov qword ptr [rsp+40h], 0
    209.     mov qword ptr [rsp+48h], 0
    210.     mov rax, g_hInstance
    211.     mov qword ptr [rsp+50h], rax
    212.     mov qword ptr [rsp+58h], 0
    213.  
    214.     call CreateWindowExA
    215.  
    216. _MsgLoop:
    217.     lea rcx, g_Msg
    218.     xor edx, edx
    219.     xor r8d, r8d
    220.     xor r9d, r9d
    221.     call GetMessageA
    222.     test eax, eax
    223.     jz _MsgExit
    224.     lea rcx, g_Msg
    225.     call DispatchMessageA
    226.     jmp _MsgLoop
    227.  
    228. _MsgExit:
    229.     xor ecx, ecx
    230.     call ExitProcess
    231.  
    232.     ; сюда не доходим, но для FRAME нужно восстановление
    233.     add rsp, 0C8h
    234.     ret
    235. WinMain endp
    236.  
    237. public start
    238. start:
    239.     jmp WinMain
    240.  
    241. end
    242.  

    P.S. Все ресурсы вшиты в EXE-файл.
     

    Вложения:

    Application нравится это.
  6. Application

    Application Moderator Команда форума

    Публикаций:
    1
    Регистрация:
    8 дек 2007
    Сообщения:
    1.039
    Зашифрованные макросы 'укорачивают' размер исходника до 48 строк :)
     
  7. GRAFik

    GRAFik Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2020
    Сообщения:
    558
    Согласен, но также иногда, удлиняют понимание работы этого исходника. Чем шире выбор - тем лучше (кашу маслом не испортишь). :)
     
  8. Application

    Application Moderator Команда форума

    Публикаций:
    1
    Регистрация:
    8 дек 2007
    Сообщения:
    1.039
    Я бы добавил
    Код (ASM):
    1. zeroday:
    2.     xor eax, eax
    3. @@: push rax
    4.     jmp @b
     
  9. GRAFik

    GRAFik Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2020
    Сообщения:
    558
    А для чего, можете пояснить ? Или вам поиздеваться - медом не корми ? :=))) :)
     
  10. Application

    Application Moderator Команда форума

    Публикаций:
    1
    Регистрация:
    8 дек 2007
    Сообщения:
    1.039
    Вместо
    Код (ASM):
    1. invoke ExitProcess, 0
     
    Ahimov нравится это.
  11. GRAFik

    GRAFik Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2020
    Сообщения:
    558
    Ага, а потом, у Инди на компьютере запустить, чтобы выяснить сколько матерных выражений он помнит наизусть. :)
     
  12. Ahimov

    Ahimov Active Member

    Публикаций:
    0
    Регистрация:
    14 окт 2024
    Сообщения:
    874
    Это ядерный эквивалент:
    Код (Text):
    1. ZwTerminateProcess(NtCurrentProcess()
    Если прокинуть фаулт в юзер не получается, значит поврежден стек и задача неисполнима.
     
  13. GRAFik

    GRAFik Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2020
    Сообщения:
    558
    Внёс небольшие поправки в код анимации иконки и курсора, чтобы он был стабилен при различных ситуациях:
    Код (ASM):
    1. option casemap:none
    2. ; ==========================================================
    3. ; Ver. 2 Исправлены разные недочёты, чтобы код не падал
    4. ; при различных обстоятельствах
    5. ; Собирается: ml64 + rc + link /ENTRY:WinMain
    6. ; Стек НЕ "дышит", как у VS2022. Полное соблюдение x64 ABI.
    7. ; ==========================================================
    8. WM_CREATE       equ 1
    9. WM_DESTROY      equ 2
    10. WM_TIMER        equ 0113h
    11. WM_SETICON      equ 80h
    12. ICON_BIG        equ 1
    13. ICON_SMALL      equ 0
    14. WS_OVERLAPPEDWINDOW equ 000CF0000h
    15. WS_VISIBLE      equ 10000000h
    16. IDC_ARROW       equ 32512
    17. IMAGE_CURSOR    equ 2
    18. COLOR_WINDOW    equ 5
    19. ; ---------- ПРОТОТИПЫ СТРУКТУР ----------
    20. MY_POINT STRUCT
    21.     pt_x    DWORD ?
    22.     pt_y    DWORD ?
    23. MY_POINT ENDS
    24. MY_MSG STRUCT
    25.     msg_hwnd        QWORD ?
    26.     msg_message     DWORD ?
    27.     msg_pad1        DWORD ?
    28.     msg_wParam      QWORD ?
    29.     msg_lParam      QWORD ?
    30.     msg_time        DWORD ?
    31.     msg_pad2        DWORD ?
    32.     msg_pt          MY_POINT <>
    33. MY_MSG ENDS
    34. MY_WNDCLASSEXA STRUCT
    35.     wc_cbSize           DWORD ?
    36.     wc_style            DWORD ?
    37.     wc_lpfnWndProc      QWORD ?
    38.     wc_cbClsExtra       DWORD ?
    39.     wc_cbWndExtra       DWORD ?
    40.     wc_hInstance        QWORD ?
    41.     wc_hIcon            QWORD ?
    42.     wc_hCursor          QWORD ?
    43.     wc_hbrBackground    QWORD ?
    44.     wc_lpszMenuName     QWORD ?
    45.     wc_lpszClassName    QWORD ?
    46.     wc_hIconSm          QWORD ?
    47. MY_WNDCLASSEXA ENDS
    48. includelib user32.lib
    49. includelib kernel32.lib
    50. extern LoadIconA:PROC
    51. extern LoadCursorA:PROC
    52. extern LoadCursorFromFileA:PROC
    53. extern LoadImageA:PROC
    54. extern RegisterClassExA:PROC
    55. extern CreateWindowExA:PROC
    56. extern GetMessageA:PROC
    57. extern DispatchMessageA:PROC
    58. extern DefWindowProcA:PROC
    59. extern SendMessageA:PROC
    60. extern SetTimer:PROC
    61. extern GetModuleHandleA:PROC
    62. extern ExitProcess:PROC
    63. .data
    64. ClassName   db 0C0h,0EDh,0E8h,0ECh,0E8h,0F0h,0EEh,0E2h,0E0h,0EDh,0EDh,0FBh,0E5h,20h,0EAh,0F3h,0F0h,0F1h,0EEh,0F0h,20h,0E8h,20h,0E8h,0EAh,0EEh,0EDh,0EAh,0E0h,0
    65. szAniFile   db "Printer.ani",0
    66. g_IconId    dq 1
    67. g_hInstance dq 0
    68. g_hCursor   dq 0
    69. g_Msg       MY_MSG <>
    70. .code
    71. WndProc proc FRAME
    72.     sub rsp, 38h
    73.     .allocstack 38h
    74.     .endprolog
    75.    
    76.     ; [ИСПРАВЛЕНО]: Храним hWnd (RCX) в локальной области стека (rsp+20h),
    77.     ; которая находится ВЫШЕ 32-байтного Shadow Space для вызываемых функций.
    78.     mov qword ptr [rsp+20h], rcx
    79.    
    80.     cmp edx, WM_DESTROY
    81.     je  _WmDestroy
    82.     cmp edx, WM_CREATE
    83.     je  _WmCreate
    84.     cmp edx, WM_TIMER
    85.     je  _WmTimer
    86.  
    87. _WmDefault:
    88.     mov rcx, qword ptr [rsp+20h]
    89.     call DefWindowProcA
    90.     jmp _WmExit
    91.  
    92. _WmDestroy:
    93.     xor ecx, ecx
    94.     call ExitProcess
    95.  
    96. _WmCreate:
    97.     mov rcx, qword ptr [rsp+20h]
    98.     mov edx, 1
    99.     mov r8d, 100
    100.     xor r9d, r9d
    101.     call SetTimer
    102.     xor eax, eax
    103.     jmp _WmExit
    104.  
    105. _WmTimer:
    106.     mov rax, g_IconId
    107.     and rax, 7
    108.     inc rax
    109.     mov g_IconId, rax
    110.     mov rcx, g_hInstance
    111.     mov rdx, rax
    112.     call LoadIconA
    113.     test rax, rax
    114.     jz _WmExit
    115.    
    116.     mov rcx, qword ptr [rsp+20h]  ; Восстанавливаем hWnd после вызова LoadIconA
    117.     mov edx, WM_SETICON
    118.     mov r8d, ICON_BIG
    119.     mov r9, rax
    120.     call SendMessageA
    121.    
    122.     mov rcx, qword ptr [rsp+20h]  ; Восстанавливаем hWnd после вызова SendMessageA
    123.     mov edx, WM_SETICON
    124.     mov r8d, ICON_SMALL
    125.     mov r9, rax
    126.     call SendMessageA
    127.    
    128.     xor eax, eax
    129.  
    130. _WmExit:
    131.     add rsp, 38h
    132.     ret
    133. WndProc endp
    134. public WinMain
    135. WinMain proc FRAME
    136.     ; [ИСПРАВЛЕНО]: 0xB8 = 0x20 (shadow) + 0x40 (args для CreateWindowEx) + 0x50 (WNDCLASSEXA) + 0x08 (padding)
    137.     sub rsp, 0B8h
    138.     .allocstack 0B8h
    139.     .endprolog
    140.     xor ecx, ecx
    141.     call GetModuleHandleA
    142.     mov g_hInstance, rax
    143.     mov rcx, rax
    144.     mov rdx, 100
    145.     call LoadCursorA
    146.     test rax, rax
    147.     jnz _HaveCur
    148.    
    149.     mov rcx, g_hInstance
    150.     mov rdx, 100
    151.     mov r8d, IMAGE_CURSOR
    152.     xor r9d, r9d
    153.     mov qword ptr [rsp+20h], 0
    154.     mov qword ptr [rsp+28h], 0
    155.     call LoadImageA
    156.     test rax, rax
    157.     jnz _HaveCur
    158.    
    159.     lea rcx, szAniFile
    160.     call LoadCursorFromFileA
    161. _HaveCur:
    162.     mov g_hCursor, rax
    163.     test rax, rax
    164.     jnz _LoadIcon
    165.    
    166.     xor ecx, ecx
    167.     mov edx, IDC_ARROW
    168.     call LoadCursorA
    169.     mov g_hCursor, rax
    170. _LoadIcon:
    171.     mov rcx, g_hInstance
    172.     mov rdx, g_IconId
    173.     call LoadIconA
    174.     ; [ИСПРАВЛЕНО]: Не используем RBX, чтобы не нарушать ABI.
    175.     ; RAX останется нетронутым до момента записи в структуру ниже.
    176.     ; [ИСПРАВЛЕНО]: WNDCLASSEXA теперь лежит на rsp+60h, не перекрываясь с аргументами вызовов
    177.     lea rcx, [rsp+60h]
    178.     mov dword ptr [rcx+MY_WNDCLASSEXA.wc_cbSize], 80
    179.     mov dword ptr [rcx+MY_WNDCLASSEXA.wc_style], 0
    180.     lea r8, WndProc
    181.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_lpfnWndProc], r8
    182.     mov dword ptr [rcx+MY_WNDCLASSEXA.wc_cbClsExtra], 0
    183.     mov dword ptr [rcx+MY_WNDCLASSEXA.wc_cbWndExtra], 0
    184.     mov r8, g_hInstance
    185.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_hInstance], r8
    186.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_hIcon], rax      ; Передаем RAX (хэндл иконки) напрямую
    187.     mov r8, g_hCursor
    188.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_hCursor], r8
    189.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_hbrBackground], COLOR_WINDOW+1
    190.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_lpszMenuName], 0
    191.     lea r8, ClassName
    192.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_lpszClassName], r8
    193.     mov qword ptr [rcx+MY_WNDCLASSEXA.wc_hIconSm], rax    ; Передаем RAX напрямую
    194.     call RegisterClassExA
    195.     ; Вызов CreateWindowExA. Аргументы безопасно размещаются в зоне [rsp+20h] .. [rsp+58h]
    196.     xor ecx, ecx
    197.     lea rdx, ClassName
    198.     lea r8, ClassName
    199.     mov r9d, WS_OVERLAPPEDWINDOW or WS_VISIBLE
    200.     mov dword ptr [rsp+20h], 100 ; X
    201.     mov dword ptr [rsp+28h], 100 ; Y
    202.     mov dword ptr [rsp+30h], 600 ; nWidth
    203.     mov dword ptr [rsp+38h], 300 ; nHeight
    204.     mov qword ptr [rsp+40h], 0   ; hWndParent
    205.     mov qword ptr [rsp+48h], 0   ; hMenu
    206.     mov rax, g_hInstance
    207.     mov qword ptr [rsp+50h], rax ; hInstance
    208.     mov qword ptr [rsp+58h], 0   ; lpParam
    209.     call CreateWindowExA
    210. _MsgLoop:
    211.     lea rcx, g_Msg
    212.     xor edx, edx
    213.     xor r8d, r8d
    214.     xor r9d, r9d
    215.     call GetMessageA
    216.    
    217.     ; [ИСПРАВЛЕНО]: Правильная проверка, отлавливающая как 0 (WM_QUIT), так и -1 (ошибка)
    218.     cmp eax, 0
    219.     jle _MsgExit
    220.    
    221.     lea rcx, g_Msg
    222.     call DispatchMessageA
    223.     jmp _MsgLoop
    224. _MsgExit:
    225.     xor ecx, ecx
    226.     call ExitProcess
    227.     add rsp, 0B8h
    228.     ret
    229. WinMain endp
    230. public start
    231. start:
    232.     jmp WinMain
    233. end
     

    Вложения:

    Application нравится это.