Blog. Just Blog

Образ мышления: Assembler

То, что не удается запрограммировать на Ассемблере, приходится паять
Образ мышления: Assembler - RSS-канал Образ мышления: Assembler - Карта сайта

Принудительное обновление иконок в трее

12.01.2012 | Категория: Образ мышления: Assembler | Автор: ManHunter
В случае аварийного завершения или некорректной работы некоторых приложений, в системном трее могут оставаться "мертвые" иконки, которые уже не принадлежат ни одному запущенному процессу. Глюк хоть и не смертельный, но все равно неприятный. И основная проблема в том, что область трея никак не реагирует на внешние сообщения типа WM_REPAINT, и функции типа UpdateWindow и InvalidateRect. То есть автоматически обновить или перерисовать его, чтобы избавиться от "мертвых" иконок, не получится. Но такие иконки удаляются, если провести курсором мышки над ними. Значит единственный способ перерисовать иконки в трее - это сэмулировать движение мыши над окном трея. Как найти окно трея и его хэндл мы уже знаем, тут ничего нового. В сегменте данных те же значения:
  1. ; Сегмент данных
  2. section '.data' data readable writeable 
  3.  
  4. class1  db 'Shell_TrayWnd',0    ; Название класса окна трея
  5. class2  db 'TrayNotifyWnd',0    ; Название класса панели уведомлений
  6. class3  db 'SysPager',0         ; Трей
  7. class4  db 'ToolbarWindow32',0  ; Панель с иконками
  8.  
  9. ToolbarHandle   dd ?            ; Хэндл окна трея
  10. ToolbarRect     RECT            ; Размер окна трея
С поиском окна тоже никаких проблем. Способ универсальный, прекрасно работает на Windows XP и Windows 7.
  1.         ; Найти окно трея
  2.         invoke  FindWindow,class1,NULL
  3.         or      eax,eax
  4.         jz      exit_process
  5.  
  6.         ; Найти панель уведомлений
  7.         invoke  FindWindowEx,eax,NULL,class2,NULL
  8.         or      eax,eax
  9.         jz      exit_process
  10.  
  11.         ; Найти трей
  12.         invoke  FindWindowEx,eax,NULL,class3,NULL
  13.         or      eax,eax
  14.         jz      exit_process
  15.  
  16.         ; Найти панель иконок в трее
  17.         invoke  FindWindowEx,eax,NULL,class4,NULL
  18.         or      eax,eax
  19.         jz      exit_process
  20.  
  21.         ; Сохранить хэндл окна с иконками
  22.         mov     [ToolbarHandle],eax
Окно трея найдено, осталось сэмулировать движение мышки. Для этого надо просто получить размеры окна трея и в цикле отправить сообщение WM_MOUSEMOVE. Не обязательно эмулировать движение мышки над каждой точкой окна, достаточно пройтись один раз над каждой иконкой. Так как размер маленькой иконки 16х16 пикселов, то шаг выберем также 16.

Читать статью целиком »
Просмотров: 8776 | Комментариев: 21

Как получить название текущего трека из Winamp и AIMP

01.12.2011 | Категория: Образ мышления: Assembler | Автор: ManHunter
Winamp был и остается самым популярным мультимедийным плеером для Windows. Такая популярность не могла остаться незамеченной, поэтому появились программы, использующие информацию из него в своих целях. Например, плагины для интернет-мессенджеров устанавливают название воспроизводимого трека в качестве статуса, а моя программа My Music Web Agent отправляет эту информацию в интернет. Для взаимодействия сторонних приложений с Winamp был разработан интерфейс API со своими командами и синтаксисом. По какой-то причине разработчики Winamp сейчас вообще убрали с сайта информацию об API, но в интернете эти данные остались. Я приложил описание Winamp Application Programming Interface в архиве с примером программы, рекомендую ознакомиться с ним перед прочтением статьи, чтобы не возникало вопросов откуда взялись те или иные значения.
  1. ; Сегмент данных
  2. section '.data' data readable writeable
  3.  
  4. ; Название класса окна Winamp/AIMP
  5. wcl      db      'Winamp v1.x',0
  6.  
  7. buff     rb 300h  ; Буфер для получения текста из заголовка окна
  8. play_now rb 300h  ; Буфер для получения названия трека
Плеер Winamp, как и другие приложения для Windows, принимает и обрабатывает сообщения, отправляемые ему через SendMessage. Но для этого сперва надо определить хэндл его окна. Сделать это очень просто, окно Winamp всегда имеет название класса "Winamp v1.x", поэтому воспользуемся функцией FindWindow. Дальше надо узнать состояние плеера. Для этого надо послать окну Winamp сообщение WM_USER с wParam = 0 и lParam = 104. Как написано в документации про это сообщение: "Returns the status of playback. If 'ret' is 1, Winamp is playing. If 'ret' is 3, Winamp is paused. Otherwise, playback is stopped." То есть, если вернулось значение не равное 1, то Winamp ничего не воспроизводит или находится в режиме паузы.
  1. ; Сегмент кода
  2. section '.code' code readable executable
  3.         ...
  4.         ; Получить хэндл окна Winamp
  5.         invoke  FindWindow,wcl,0
  6.         or      eax,eax
  7.         ; Окно Winamp не найдено
  8.         jz      .no_winamp
  9.  
  10.         ; Сохранить хэндл окна
  11.         mov     ebx,eax
  12.  
  13.         ; Что-то сейчас воспроизводится?
  14.         invoke  SendMessage,ebx,WM_USER,NULL,104
  15.         cmp     eax,1
  16.         ; Winamp не находится в состоянии "Play"
  17.         jne     .no_winamp
  18.         ...
Окно мы нашли, состояние проигрывателя знаем, осталось получить название трека, который сейчас воспроизводится. Казалось бы все просто - надо послать еще одно сообщение, которое также описано в документации, и получить результат. Но не тут-то было. Сообщения для получения информации о воспроизводимом файле и названии трека доступны только из контекста процесса самого Winamp, то есть могут использоваться только в плагинах Winamp. Логика разработчиков тут не совсем ясна, ведь для сторонних приложений доступна информация о битрейте текущего трека, размере плей-листа, текущей позиции воспроизведения и еще куча другой малополезной информации, а простейшего названия трека нет.

Читать статью целиком »
Просмотров: 7760 | Комментариев: 9

Как узнать, что программа запущена под Администратором

20.11.2011 | Категория: Образ мышления: Assembler | Автор: ManHunter
Иногда требуется узнать, запущена ли ваша программа под учетной записью с правами Администратора, или же от обычного пользователя. Для чего это нужно? Например, некоторые операции с реестром или файлами требуют права Администратора. При попытке выполнить их обычному пользователю вернется ошибка ERROR_ACCESS_DENIED, но для более точного анализа ситуации надо будет проверить права доступа и уведомить об этом пользователя. Проверить, что программа запущена под Администратором, можно несколькими способами.

Первый способ наиболее универсальный и работает даже на старых операционных системах. Он заключается в том, что надо получить группы доступа для токена текущего процесса, а затем проверить, входит ли хоть одна из них в группу Администраторов локального компьютера. Вот пример реализации:
  1. ; Сегмент данных
  2. section '.data' data readable writeable
  3.  
  4. SECURITY_NT_AUTHORITY       = 5
  5. TOKEN_READ                  = 0x00020008
  6. SECURITY_BUILTIN_DOMAIN_RID = 0x00000020
  7. DOMAIN_ALIAS_RID_ADMINS     = 0x00000220
  8. TokenGroups                 = 0x00000002
  9.  
  10. BUFF_SIZE = 1024h ;  Размер буфера для групп доступа токена
  11.  
  12. NtAuthority     db 0,0,0,0,0,SECURITY_NT_AUTHORITY
  13.  
  14. hTokenHandle    dd ?
  15. dInfoSize       dd ?
  16. psidAdmins      dd ?
  17. hHeap           dd ?
  18. pTokenGroups    dd ?
  19.  
  20. ;---------------------------------------------
  21.  
  22. ; Сегмент кода
  23. section '.code' code readable executable
  24.         ...
  25.         ; Получить токен текущего процесса
  26.         invoke  GetCurrentProcess
  27.         invoke  OpenProcessToken,eax,TOKEN_READ,hTokenHandle
  28.  
  29.         ; Выделить память для массива групп
  30.         invoke  GetProcessHeap
  31.         mov     [hHeap],eax
  32.  
  33.         invoke  HeapAlloc,eax,HEAP_ZERO_MEMORY,BUFF_SIZE
  34.         mov     [pTokenGroups],eax
  35.  
  36.         ; Получить информацию о группах доступа токена
  37.         invoke  GetTokenInformation,[hTokenHandle],TokenGroups,\
  38.                 [pTokenGroups],dword BUFF_SIZE,dInfoSize
  39.  
  40.         ; Прибраться за собой
  41.         invoke  CloseHandle,[hTokenHandle]
  42.  
  43.         invoke  AllocateAndInitializeSid,NtAuthority,2,\
  44.                 SECURITY_BUILTIN_DOMAIN_RID,\
  45.                 DOMAIN_ALIAS_RID_ADMINS,0,0,0,0,0,0,psidAdmins
  46.  
  47.         ; Количество записей в структуре TOKEN_GROUPS
  48.         mov     esi,[pTokenGroups]
  49.         mov     ebx,dword [esi]
  50.         ; Указатель на массив SID_AND_ATTRIBUTES
  51.         add     esi,4
  52. @@:
  53.         ; Проверить соответствие SID
  54.         mov     eax,dword [esi]
  55.         invoke  EqualSid,[psidAdmins],eax
  56.         or      eax,eax
  57.         jnz     loc_admin
  58.  
  59.         ; Следующая группа
  60.         add     esi,8
  61.         dec     ebx
  62.         or      ebx,ebx
  63.         jnz     @b
  64.  
  65. loc_not_admin:
  66.         ; Пользователь не Администратор
  67.         ...
  68.  
  69. loc_admin:
  70.         ; Пользователь Администратор
  71.         ...
Обратите внимание, что никаких проверок на ошибки не выполняется, оставлен только рабочий код. Полный вариант вы можете посмотреть в приложении к статье.

Читать статью целиком »
Просмотров: 10302 | Комментариев: 22

Диалог открытия файлов и юзабилити Windows

02.11.2011 | Категория: Образ мышления: Assembler | Автор: ManHunter
При всех удобствах Windows некоторые моменты меня очень сильно раздражают. Особенно поведение системы при вызове диалогов открытия файлов. Сперва немного предыстории. При работе с файлами через функцию GetOpenFileName или GetSaveFileName в структуре OPENFILENAME есть возможность указать путь, который должен открыться по умолчанию. Если это значение не задано, то система сама где-то запоминает папку, в которой последний раз был удачно открыт файл (то есть окно выбора файла было закрыто через кнопку "Ok"). Где именно хранится эта информация - я пока не выяснил, да и не особо надо. Второй вариант. Предположим, что некоторая программа самостоятельно запоминает путь к папке, в которой последний раз ею выполнялись какие-то действия с файлами. Это может быть, например, текстовый редактор, просмотрщик графики и т.п., не суть. Главное, что задумка очень хорошая и правильная. При следующем запуске или вызове диалога выбора файла в соответствующее поле OPENFILENAME будет подставлен сохраненный путь и пользователь продолжит работу с того места, где он в прошлый раз остановился. Что-то типа такого:
  1.         ...
  2.         invoke  GetModuleHandle,0
  3.         mov     [ofn.hInstance],eax
  4.         mov     [ofn.lStructSize], sizeof.OPENFILENAME
  5.         mov     [ofn.hwndOwner],0
  6.         mov     [ofn.nMaxFile],MAX_PATH
  7.         mov     [ofn.lpstrFile],buff
  8.         ; Открывать с последней сохраненной папки
  9.         mov     [ofn.lpstrInitialDir],saved_dir
  10.         mov     [ofn.Flags],OFN_EXPLORER+OFN_FILEMUSTEXIST
  11.         invoke  GetOpenFileName,ofn
  12.         ...
Неадекватное, на мой взгляд, поведение системы заключается в следующем. Вполне может возникнуть ситуация, что какая-то часть из сохраненного или запрошенного пути пропала. Например, я в просмотрщике рассортировал папку с фотографиями, в графическом редакторе подправил несколько файлов, а затем в файловом менеджере перенес всю папку с фотографиями в другое место на диске. В этом случае при попытке вернуться к просмотру в просмотрщике, повторно вызвать диалог открытия или сохранения файла в графическом редакторе, при любом раскладе в качестве дефолтного пути будет открыта какая-нибудь херня типа Библиотеки, Моих документов или вообще папки, куда установлена программа. Закономерности я тут тоже не уловил, видимо принятие решения остается за Windows и зависит от уровня осадков в Зимбабве. В итоге пользователю приходится снова топать весь путь из библиотеки до места работы.

Читать статью целиком »
Просмотров: 7140 | Комментариев: 6

Расчет CRC8 на Ассемблере

25.09.2011 | Категория: Образ мышления: Assembler | Автор: ManHunter
Контрольная сумма CRC8 применяется в основном для коротких сетевых пакетов и в микроконтроллерах. Из-за большой вероятности появления коллизий, использовать ее можно или для контроля целостности небольших объемах данных (оптимально - до 15 байт информации), или в случаях, когда возможность появления искажений исходных данных крайне мала. Алгоритм расчета CRC8 реализуется очень просто, работает очень быстро, из-за чего до сих пор находит свое применение. Первый вариант - прямой расчет.
  1. ;-----------------------------------------------------------------------
  2. ; Функция вычисления хеша CRC8
  3. ; by ManHunter / PCL
  4. ; http://www.manhunter.ru
  5. ;-----------------------------------------------------------------------
  6. ; Параметры:
  7. ;       lpData - указатель на строку
  8. ;       dSize  - длина строки
  9. ; На выходе:
  10. ;       AL = полученный хеш
  11. ;-----------------------------------------------------------------------
  12. proc    CRC8 lpData:DWORD, dSize:DWORD
  13.         push    ebx ecx edx esi edi
  14.  
  15.         CRC8_POLYNOM = 31h
  16.  
  17.         ; Инициализация
  18.         mov     al,0FFh
  19.  
  20.         ; Длина строки
  21.         cmp     [dSize],0
  22.         je      .loc_ret
  23.  
  24.         ; Указатель на начало строки
  25.         xor     ecx,ecx
  26. @@:
  27.         ; Получить символ из строки
  28.         mov     ebx,[lpData]
  29.         xor     al,byte [ebx+ecx]
  30.  
  31.         xor     esi,esi
  32. .loc_cycle:
  33.         test    al,80h
  34.         jz      .loc_1
  35.  
  36.         shl     al,1
  37.         xor     al,CRC8_POLYNOM
  38.         jmp     .loc_next
  39. .loc_1:
  40.         shl     al,1
  41. .loc_next:
  42.         inc     esi
  43.         cmp     esi,8
  44.         jb      .loc_cycle
  45.  
  46.         ; Следующий символ
  47.         inc     ecx
  48.         cmp     ecx,[dSize]
  49.         jb      @b
  50.  
  51. .loc_ret:
  52.         and     eax,0FFh
  53.  
  54.         pop     edi esi edx ecx ebx
  55.         ret
  56. endp
Этот вариант компактный, но не самый быстрый по скорости вычисления. Более скоростной алгоритм расчета CRC8 - с использованием заранее подготовленной таблицы.

Читать статью целиком »
Просмотров: 11639 | Комментариев: 3

prev 01 ... 52 53 54 55 56 57 58 ... 68 next
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.06 сек. / MySQL: 2 (0.0028 сек.) / Память: 4.5 Mb
Наверх