Как написать AddIn для WinAsm Studio — Архив WASM.RU
"Дай порулить!"
(Неизвестный автор)0. Предисловие.
Каждому в глубине души хочеться иметь "свой" редактор. Однако добавить в IDE новую возможность значительно проще, чем разработать ее целиком. Основным препятствием для этого (кроме лени) является необходимость изучения работы IDE и интерфейса для AddIn-ов. Сами же разработчики IDE заинтересованы в такой помощи по понятным причинам, и стремятся упростить это узкое место. Данная статья призвана пролить некоторый свет на этот вопрос пользователям WinAsm Studio и базируется на файлах WinAsm.chm, WAAddIn.inc, анализе кода других AddIn-ов, в том числе примеров, а также на небольшом собственном опыте. Естественно, она не является ни полным переводом, ни исчерпывающим пособием.
В настоящее время я использую версию WinAsm-а 4.0.3, однако большинство из написанного действительно и для версий 3.х.х и даже более раних. Необходимо заметить, что Антонис Киприянов, автор WinAsm-а не только постоянно совершенствует свою IDE, но и развивает интерфейс для AddIn-ов, внемля просьбам трудящихся.
Автор WinAsm Studio: Antonis Kyprianou.
Составитель WinAsm.chm: Jim Giordano.
Сайт программы: http://www.winasm.net
1. Что есть AddIn и как его использовать в WinAsm.
AddIn - это подключаемый код, предназначенный для расширения функций редактора и размещенный в отдельной dll. Для того, чтобы WinAsm его нашел, dll нужно разместить в подкаталоге AddIns домашнего каталога WinAsm-а. Под "подключением" AddIn-а имеется ввиду загрузка и инициализация соответствующей dll. Для того, чтобы загрузить AddIn, необходимо открыть менеджер AddIn-ов (Add-Ins Manager) в меню Add-Ins. Выбрав нужный AddIn в списке, отметить чекбокс "Loaded/Unloaded" чтобы его загрузить. Также можно отметить чекбокс "Load On StartUp", тогда AddIn будет загружаться автоматически при загрузке WinAsm. Для своего функционирования AddIn должен экспортировать функции, часть из которых обязательна. WinAsm, в свою очередь, предоставляет AddIn-у указатели на внутренние структуры с важными переменными, а также выполняет ряд сервисов, обрабатывая дополнительные сообщения в оконной процедуре главного окна. Основной поток сообщений, получаемых окнами WinAsm-а, пропускается вначале через соответствующие процедуры загруженых AddIn-ов, при этом AddIn может не только выполнить свой код, получив соответствующее сообщение, но и прекратить его дальнейшую обработку, вернув TRUE в eax, полностью подменив, таким образом, некоторую встроенную функцию своим кодом.
2. Что нужно для написания AddIn-а.
Исходник AddIn-а представляет из себя обычный проект dll и включает в себя, как минимум:
- addin.asm - исходный текст.
- addin.def - файл, в котором определяются имена экспортируемых функций.
- WAAddin.inc - файл, в котором определены структуры, используемые в WinAsm, и ряд констант, в том числе и дополнительные сообщения, обрабатываемые контролом окна редактора CodeHi.dll.
WAAddin.inc регулярно обновляется вместе с появлением новых функций в WinAsm-е. Он обширно откомментирован и снабжен примерами использования некоторых возможностей. Чтобы быстро создать шаблон нового AddIn-а, можно вызвать мастер новых проектов Ctrl+N, и в разделе "Bare Bone" выбрать тип проекта "AddIn". Можно также использовать исходные тексты примеров или рабочих AddIn-ов, так как практически все они ими снабжены.
Также для написания AddIn-а удобно сразу же в имя результирующего бинарного файла включить путь к папке с AddIn-ами. При компиляции необходимо учитывать, что она возможна только тогда, когда AddIn выгружен. Для обхождения этого неудобства можно использовать другой AddIn - Enchanced Add-Ins Manager - улучшенный менеджер AddIn-ов (автор - Mario Vilas), который при компиляции автоматически выгружает текущий AddIn, а после нее - загружает. Однако он не всегда это делает успешно, если при компиляции произошла ошибка. При написании AddIn-а необходимо быть готовым к тому, что WinAsm из-за него рухнет, поэтому не торопитесь включать для него опцию "Load On StartUp". Если же это произошло при изменении существующего AddIn-а, который загружается вместе с WinAsm-ом и рушит его, отключить его автоматическую загрузку можно убрав строку с названием соответствующей dll из раздела [ADDINS] файла winasm.ini (расположен в домашнем каталоге WinAsm-а). WinAsm позволяет запускать себя в "защищенном от AddIn-ов" режиме при удержании F8, однако это оказалось малоэффективном при старте его из разных файловых менеджеров, которые сами обрабатывают нажатие F8. Возможно, в дальнейшем это будет исправлено. Для отладки AddIn-ов можно использовать как vkdebug, идущий в составе пакета masm32, так и любые другие отладчики, запуская под ними WinAsm, загружая AddIn и переходя к отладке. Для отладки я обычно использую следующий макрос, исключающий сообщение об ошибке при запуске WinAsm-а с AddIn-ом вне отладчика:
deb macro local nodeb push eax invoke IsDebuggerPresent test eax,eax pop eax jz nodeb int 3 nodeb: endm3. Механизм загрузки, выгрузки и настройки AddIn-а.
Возможны следующие варианты обращения к dll AddIn-а:
А. Получение сведений об AddIn-е менеджером AddIn-ов
- AddIn не загружен:
DllEntry (DLL_PROCESS_ATTACH) GetWAAddinData DllEntry (DLL_PROCESS_DETACH)- AddIn загружен:
GetWAAddInDataБ. Настройка AddIn-а из менеджера AddIn-ов
- AddIn не загружен:
DllEntry (DLL_PROCESS_ATTACH) WAAddInConfig DllEntry (DLL_PROCESS_DETACH)- AddIn загружен:
WAAddInConfigВ. Загрузка AddIn-а:
DllEntry (DLL_PROCESS_ATTACH) WAAddInLoadГ. Выгрузка AddIn-а:
WAAddInUnload DllEntry (DLL_PROCESS_DETACH)4. Обязательные процедуры.
A. DllEntry
Это стандартная процедура DllMain, она вызывается при загрузке/выгрузке динамической библиотеки. В AddIn-е используется для получения хэндла dll при загрузке.
DllEntry Proc hInst:HINSTANCE, reason:DWORD, reserved1:DWORDhInst - хэндл модуля dll;
reason - параметр, определяющий причину вызова данной функции, в нашем случае может принимать следующие значения:
DLL_PROCESS_ATTACH - выполняется загрузка библиотеки в память процесса,
DLL_PROCESS_DETACH - выполняется выгрузка библиотеки из памяти процесса;
reserved1 - он и в Африке reserved1.Процедура DllEntry присутствует в шаблоне и обычно не нуждается в модификации.
Б. GetWAAddInData
GetWAAddInData Proc lpFriendlyName:PTR BYTE, lpDescription:PTR BYTE Invoke lstrcpy, lpDescription, Offset szDescription Invoke lstrcpy, lpFriendlyName, Offset szFriendlyName RET GetWAAddInData EndPПростая процедура, вызываемая WinAsm для получения удобочитаемых названий и описаний AddIn-ов для последующего их отображения в списке менеджера. Также присутствует в шаблоне. В модификации нуждаются текстовые переменные szFriendlyName и szDescription, содержащие соответственно имя и краткое описание AddIn-а, выводимые менеджером AddIn-ов в списке.
Наример, в AddIn.Inc записываем:
szFriendlyName DB "Простой AddIn",0 szDescription DB "Этот AddIn позволяет читать и " DB "вставлять слово под курсором.",0В. WAAddInLoad
Процедура, вызываемая при инициализации AddIn-а.
WAAddInLoad Proc pWinAsmHandles:DWORD, features:PTR DWORDВ качестве параметров передаются указатели на структуру с важнейшими хэндлами HANDLES (pWinAsmHandles) и на структуру с разными переменными features (содержит номер версии WinAsm-а, указатели на списки процедур загруженых AddIn-ов, на переменную с хэндлом дочернего окна редактора ресурсов и на список меток редактора ресурсов). Эти структуры подробнее описаны в WAAddIn.inc.
В этой процедуре AddIn может скопировать в свои переменные такие важные значения, как хэндл главного окна WinAsm-а (HANDLES.hMain), хэндл клиентской части главного окна (HANDLES.hClient), хэндл главного меню (HANDLES.hMenu), и многие другие. Конечно же, можно запомнить и сам указатель на структуру WinAsmHandles, однако нельзя сказать, что это удобней.
Также в этой процедуре AddIn может произвести собственную инициализацию: выделить память, добавить свои опции в меню и т.п. Значение, возвращаемое этой процедурой, свидетельствует об успешности инициализации AddIn-а, и в случае неудачи (eax=TRUE) AddIn будет выгружен.
Г. WAAddInUnload
Данная процедура не имеет параметров и вызывается при выгрузке AddIn-а. Здесь AddIn должен освободить выделенную ему память, удалить свои опции меню, и т.п. То есть, восстановить status quo.
5. Необязательные процедуры.
Для использования необязательных процедур AddIn-а в WinAsm их названия необходимо вписать (или декомментировать) в разделе EXPORTS def-файла проекта.
А. WAAddInConfig
Процедура настройки AddIn-а. Вызывается кнопкой из менеджера AddIn-ов и имеет те же параметры, что и процедура WAAddInLoad. Удобна для настройки AddIn-ов, не создающих свои опции меню или окна, а также позволяет настраивать AddIn-ы, которые в данный момент не загружены.
Б. Процедуры обработки сообщений окон:
FrameWindowProc - получает все сообщения, посылаемые главному (MDI) окну WinAsm.
ChildWindowProc - получает все сообщения, посылаемые всем дочерним окнам WinAsm.
ProjectExplorerProc - получает все сообщения, посылаемые окну менеджера проекта.
OutWindowProc - получает все сообщения, посыламые окну вывода результатов компиляции.
все эти процедуры имеют одинаковый формат, соответствующий формату процедуре окна:
Procname Proc hWnd:DWORD uMsg:UINT, wParam:WPARAM, lParam:LPARAMПеред тем, как процедура окна WinAsm обработает сообщение сама, это сообщение будет пропущено через все соответствующие процедуры AddIn-ов. Если какая-либо процедура AddIn-а вернула eax=TRUE, дальнейшая обработка данного сообщения прекращается. При этом следует заметить, что если два AddIn-а обрабатывают одинаковое сообщение, то первенство будет согласно загрузки, а порядок загрузки AddIn-ов пока не регламентировался. После обработки сообщения WM_COMMAND самим WinAsm-ом всем процедурам FrameWindowProc дополнительно посылается сообщение WAE_COMMANDFINISHED с теми же параметрами, что дает возможность выполнить код после обработки команды редактором (а не перед).
6. Работа с меню.
Вероятно, AddIn-у потребуется создать свою опцию меню. Для этого могут понадобиться либо хэндл главного меню, либо хэндл одного из существующих выпадающих меню. Их можно взять из структуры HANDLES и вложеной структуры POPUPMENUS, их соответствие понятно из названий:
POPUPMENUS STRUCT hFileMenu HWND hEditMenu HWND hViewMenu HWND hProjectMenu HWND hFormatMenu HWND hDialogMenu HWND hMakeMenu HWND hSetActiveBuildMenu HWND субменю в меню "Make" hToolsMenu HWND hAddInsMenu HWND hWindowMenu HWND hHelpMenu HWND hNewFileMenu HWND субменю в меню "File" hConvertMenu HWND субменю в меню "Format" POPUPMENUS ENDSНапример, если мы решили создать новую опцию в выпадающем меню "Add-Ins", то его хэндл мы можем получить так (в процедуре WAAddInLoad):
mov ebx,pWinAsmHandles ; загружаем в ebx указатель на структуру с хэндлами push [ebx].HANDLES.POPUPMENUS.hAddInsMenu ; получаем хэндл субменю "Add-Ins" pop hSubM ; сохраняем хэндл в нашей переменнойДалее, для создания новой опции меню нам потребуется уникальный идентификатор, который можно получить, послав сообщение WAM_GETNEXTMENUID главному окну:
invoke SendMessage,hMain,WAM_GETNEXTMENUID,0,0 mov MenuID,eaxТеперь можно создавать свою опцию меню:
invoke InsertMenu,hSubM,MenuID,MF_BYCOMMAND or MF_OWNERDRAW,MenuID,\ ADDR menu_captionПри выгрузке AddIn-а (в процедуре WAAddInUnload) следует удалить свою опцию меню:
invoke DeleteMenu,hSubM,MenuID,MF_BYCOMMANDХэндл главного окна нужно взять из структуры HANDLES. Там же можно получить и хэндл главного меню:
... push [ebx].HANDLES.hMain ; получаем хэндл главного окна pop hMain push [ebx].HANDLES.hMenu ; получаем хэндл меню pop hMenu ...Иногда AddIn может обрабатывать существующие команды меню, тогда создавать свои идентификаторы не нужно. Встроенные идентификаторы перечислены в WAAddIn.inc. Обработав таким образом некоторый идентификатор и вернув в eax значене TRUE AddIn может полностью подменить соответствующую функцию WinAsm-а своей. AddIn может также заставлять WinAsm выполнять нужные функции, посылая встроенные идентификаторы главному окну WinAsm-а.
7. Создание плавающих окон (docking windows).
Для создания такого рода окон необходимо использовать внутренние сообщения главному окну WinAsm-а, описанные в разделе "Docking windows" файла WAAddIns.inc:
WAM_CREATEDOCKINGWINDOW - создать окно
WAM_GETCLIENTRECT - получить размеры клиентской области окна
WAM_DESTROYDOCKINGWINDOW - удалить окноТакже для создания такого окна необходимо будет заполнить структуру DOCKINGDATA, описанную там же.
8. Работа с окном редактора.
Вероятно, AddIn-у потребуется обратиться к окну редактора для анализа/модификации редактируемого текста. Для этого вначале необходима небольшая рутинная последовательность:
... ; получаем хэндл активного дочернего окна invoke SendMessage,hClient,WM_MDIGETACTIVE,0,0 ; если таковое отсутствует - выходим... test eax,eax jz wp_exit ; получаем указатель на структуру с данными окна invoke GetWindowLong,EAX,0 ; получаем из структуры хэндл контрола ричедита mov eax,[eax].CHILDDATA.hEditor mov hEditor,eax ...Также во многих случаях желательно убедиться в том, что окно видимо при помощи функции IsWindowVisible. Теперь можно работать с окном редактора. Сообщения, которые поддерживает контрол, перечислены в WAAddIn.Inc и состоят как из ряда стандартных сообщений, так и дополнительных, которые начинаются на CHM_ (CodeHi Message). Стандартные сообщения в WAAddIn.inc закомментированы, так как они уже описаны в windows.inc. Смысл большинства сообщений понятен из названия.
Например, получим слово, находящееся под курсором:
invoke SendMessage,hEditor,CHM_GETWORD,128,offset BufferForWordТакже в структуре CHILDDATA можно найти другие полезные данные: имя файла с полным путем, внутренний тип файла и некоторые другие. Эта структура также описана в WAAddIn.inc.
9. Работа с акселераторами.
Для добавления своих клавиатурных акселераторов можно воспользоваться хэндлом таблицы акселераторов, указатель на него имеется в структуре HANDLES: HANDLES.phAcceleratorTable. Работа с таблицей акселераторов показана на примере (неидеальном, но работающем) в демонстрационном AddIn-е AccelaratorDemoAddIn.
10. Хранение настроек.
Для настройки AddIn-а можно использовать как процедуру WAAddInConfig, так и другие методы. Преимущество первого способа заключается в том, что процедура настройки конфигурации может быть вызвана у незагруженного AddInа, или в том случае, когда у AddIn-а нет своих окон, что избавляет от необходимости создавать специальные опции меню только для настройки AddIn-а. Соблюдения порядка ради автором WinAsm-а предлагается хранить настройки AddIn-а в файле WAAddIns.ini, находящегося в каталоге AddIns вместе с AddInами. Для хранения своих настроек желательно создать свою секцию, соответствующую названию AddIn-а, где и хранить значения переменных. Запись/чтение переменных производится при помощи функций WritePrivateProfileString и GetPrivateProfileString.
11. Дополнительные возможности для разработки AddIn-ов.
На сайте WinAsm Studio меется пакет WAAddInLib, разработанный Mario Vilas (QvasiModo), в котором часть рутинного кода оформлено в виде процедур, что позволяет ускорить написание AddIn-ов. Пакет также регулярно обновляется.
12. Пожелания.
Удачи всем!
Александр Журенков (aka shoo)
© shoo
Как написать AddIn для WinAsm Studio
Дата публикации 5 фев 2005