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

Тема в разделе "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.031
    Надеюсь поможет, надо проверять на пракике:

    --- ---


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


    Вы почти правы. Последовательность действий для курсора действительно проста:
    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. IDC_ICON1 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. local hCursor:qword
    13.        xor ebx,ebx
    14.        lea rdi,ClassName
    15.        lea rsi,WndProc
    16.        invoke LoadCursorFromFileA,"Printer.ani"
    17.        mov hCursor,rax
    18.        invoke LoadIconA,IMAGE_BASE,IDC_ICON1
    19. SAVE <rax,rdi,rbx,COLOR_WINDOWTEXT,hCursor,rax,IMAGE_BASE,rbx,rsi,(sizeof WNDCLASSEX)>
    20.        invoke RegisterClassExA,rsp;в rsp адрес WNDCLASSEX
    21. SAVE <rbx,IMAGE_BASE,rbx,rbx,300,600,100,100>
    22.        sub esp,20h
    23.        invoke CreateWindowExA,0,edi,edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE ;Стили окна
    24.        lea edi,msg
    25. @@:    invoke GetMessageA,edi,NULL,0,0
    26.        invoke DispatchMessageA,edi
    27.        jmp @b
    28. WinMain endp
    29. WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
    30.        mov hWnd,rcx
    31. for uMsg,<DESTROY,CREATE,TIMER>
    32.        cmp edx,WM_&uMsg
    33.        je wm&uMsg
    34. endm
    35.        cmp edx,WM_DESTROY
    36.        je wmDESTROY
    37.        leave
    38.        jmp NtdllDefWindowProc_
    39. wmDESTROY:invoke RtlExitUserProcess,NULL
    40. wmCREATE:invoke  SetTimer,hWnd,1,100,NULL
    41.        jmp wmBYE
    42. wmTIMER:and hIcon,111b
    43.        inc hIcon
    44.        invoke  LoadIconA,IMAGE_BASE,hIcon
    45.        invoke  SendMessageA,hWnd,WM_SETICON,ICON_BIG,rax
    46. wmBYE: leave
    47.        retn
    48. WndProc endp
    49. .data
    50. ClassName db "Анимированные курсор и иконка",0
    51. hIcon dq 1
    52. 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 КБ
      Просмотров:
      5
    Последнее редактирование: 23 сен 2026 в 16:05
    GRAFik и Application нравится это.
  5. GRAFik

    GRAFik Active Member

    Публикаций:
    0
    Регистрация:
    14 мар 2020
    Сообщения:
    548
    На базе кода от 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.031
    Зашифрованные макросы 'укорачивают' размер исходника до 52 строк :)
     
  7. GRAFik

    GRAFik Active Member

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

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

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

    GRAFik Active Member

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

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

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

    GRAFik Active Member

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

    Ahimov Active Member

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