Хочу попробовать написать плаг на PureBasic, но хотелось бы иметь шаблон с чего начать.
Я уже делал плаги используя шаблон, а с нуля это попытаться с моей стороны сравнить шаблоны плагов на С++, чтобы произвести такие же действия для PureBasic, с учётом что я слабо разбираюсь в С++.
Posted: Fri May 08, 2020 12:45 am
by AZJIO
Может кто сделать плагин шаблон минимального типа, например для начала вывести мессагу "привет мир!". Собственно интересует встраивание пункта меню в интерфейс AkelPad, то есть в меню плагинов.
#MAX_PATH = 260
; AkelDLL.h строка 1112
Structure PLUGINVERSION Align #PB_Structure_AlignC
cb.l ; размер структуры
hMainWnd.l ; дескриптор главного окна
dwAkelDllVersion.l ; Текущая версия AkelPad
dwExeMinVersion3x.l ; Минимальная поддерживаемая версия AkelPad_3. Неподдерживаемый вариант MAKE_IDENTIFIER(-1, -1, -1, -1)
dwExeMinVersion4x.l ; Минимальная поддерживаемая версия AkelPad_4
*pPluginName ; Имя плагина
EndStructure
; AkelDLL.h строка 1150
Structure PLUGINCALLSENDW Align #PB_Structure_AlignC
*pFunction ; Имя функции в формате L"Plugin::Function"
lParam.l ; Входные данные
dwSupport.l ; Смотрите значения #define PDS_*
nResult.i ; Может использоваться для связи между плагинов
EndStructure
; AkelDLL.h строка 1169
Structure PLUGINFUNCTION Align #PB_Structure_AlignC
*next._PLUGINFUNCTION
*prev._PLUGINFUNCTION
*pFunction ; Имя функции в формате L"Plugin::Function" (в юникоде, если bOldWindows = False)
szFunction.a[#MAX_PATH] ; имя функции (Ansi)
wszFunction.c[#MAX_PATH] ; имя функции (Unicode)
nFunctionLen.i ; Длина имени функции
wHotkey.i ; Горячая клавиша. См. HKM_GETHOTKEY возвращаемое значение сообщения (MSDN)
bAutoLoad.c ; TRUE, если функция имеет флаг автозагрузки, иначе FALSE
bRunning.c ; Функция выполняется
; PluginProc.PLUGINPROC ; Функциональная процедура (?????)
*lpParameter ; Параметр процедуры
nRefCount.i ; Внутренняя
EndStructure
; AkelDLL.h строка 1187
Structure STACKPLUGINFUNCTION Align #PB_Structure_AlignC
*first.PLUGINFUNCTION
*last.PLUGINFUNCTION
EndStructure
; конвертирование типов
; Логический тип данных
; bool 1 байт
; Символьный тип данных
; char 1 байт
; wchar_t 1 байт
; char16_t 2 байта
; char32_t 4 байта
; Целочисленный тип данных short
; 2 байта
; int 2 байта
; long 4 байта
; long long 8 байт
; Тип данных с плавающей запятой
; float 4 байта
; double 8 байт
; long double 8 байт
; DWORD = Long .l 4 байта
; размер указателей задаётся автоматически от битности компилируемого
; AkelDLL.h строка 1192
Structure PLUGINDATA Align #PB_Structure_AlignC
cb.l ; размер структуры
*pcs ; указатель на структуру PLUGINCALLSENDW (AkelDLL.h строка 1150)
dwSupport.l ; когда запрашивающий хочет получить флаги PDS_* без выполнения функции
*pFunction ; Вызываемое имя функции, в формате "Plugin::Function" (в юникоде, если bOldWindows = False)
*szFunction ; Вызываемое имя функции (Ansi)
*wszFunction ; Вызываемое имя функции (Unicode)
lParam.l ; Входные данные (тип указатель или long?)
hInstanceDLL.l ; Экземпляр DLL
*lpPluginFunction ; указатель на структуру PLUGINFUNCTION (AkelDLL.h строка 1169)
nUnload.i ; смотрите значения #define UD_*
bInMemory.c ; Плагин уже загружен
bOnStart.c ; Указывает, когда функция была вызвана: TRUE - на страрте, FALSE - вручную.
*pAkelDir ; папка AkelPad (в юникоде, если bOldWindows = False)
*szAkelDir ; папка AkelPad (Ansi)
*wszAkelDir ; папка AkelPad (Unicode)
hInstanceEXE.l ; Экземпляр EXE
*hPluginsStack ; Стек плагинов, указатель на STACKPLUGINFUNCTION (AkelDLL.h строка 1187)
nSaveSettings.i ; смотрите значения #define SS_*
hMainWnd.l ; Главное окно
*lpFrameData ; указатель на структуру FRAMEDATA (AkelDLL.h строка 1010)
hWndEdit.l ; окно редактирования
hDocEdit.l ; окно редактирование документа
hStatus.l ; строка состояния
hMdiClient.l ; Окно MDI, если nMDI = WMD_MDI
hTab.l ; Окно Tab, если nMDI = WMD_MDI или WMD_PMDI
hMainMenu.l ; Главное меню
hMenuRecentFiles.l ; Меню последних файлов
hMenuLanguage.l ; Языковое меню
hPopupMenu.l ; Контекстное меню правой кнопкой мыши
hMainIcon.l ; дескриптор иконки основного окна
hGlobalAccel.l ; Глобальная таблица быстрых клавиш (высокий приоритет).
hMainAccel.l ; Основная таблица быстрых клавиш (низкий приоритет).
bOldWindows.c ; Флаг Windows без юникода
bOldRichEdit.c ; Riched20.dll ниже, чем 5.30 (v3.0). Всегда FALSE
dwVerComctl32.l ; Версия Comctl32.dll установлена как MAKELONG(major,minor).
bAkelEdit.c ; Используется элемент AkelEdit. Всегда TRUE
nMDI.i ; Режим окна, смотрите значения #define WMD_*
*pLangModule ; Имя файла языкового модуля (в юникоде, если bOldWindows = False)
*szLangModule ; Имя файла языкового модуля (Ansi)
*wszLangModule ; Имя файла языкового модуля (Unicode)
Hlangmodule.l ; Дескриптор языкового модуля
wLangSystem.l ; Идентификатор языка системы
wLangModule.l ; Идентификатор языка языкового модуля
EndStructure
EnableExplicit
#WM_COMMAND = $0111
#IDM_FILE_OPEN = 2
#PDS_NOAUTOLOAD = $00000001 ; Function doesn't support autoload.
#PDS_GETSUPPORT = $10000000 ; Flag is set if caller wants to get PDS_* flags without function execution
XIncludeFile "for.pb"
; Structure MAKELONG
; StructureUnion
; Num.l
; byte.c[4]
; EndStructureUnion
; EndStructure
; Procedure MAKE_IDENTIFIER(a,b,c,d)
; Protected NumVer.MAKELONG
; NumVer\byte[0] = a
; NumVer\byte[1] = b
; NumVer\byte[2] = c
; NumVer\byte[3] = d
; ProcedureReturn NumVer\Num
; EndProcedure
Procedure MAKE_IDENTIFIER(d,c,b,a)
Protected x, y, z
x = ((a << 8) | (b & $FF))
y = ((c << 8) | (d & $FF))
z = ((x << 16) | (y & $FFFF))
ProcedureReturn z
EndProcedure
; конвертирование типов
; Логический тип данных
; bool 1 байт
; Символьный тип данных
; char 1 байт
; wchar_t 1 байт
; char16_t 2 байта
; char32_t 4 байта
; Целочисленный тип данных short
; 2 байта
; int 2 байта
; long 4 байта
; long long 8 байт
; Тип данных с плавающей запятой
; float 4 байта
; double 8 байт
; long double 8 байт
; DWORD = Long .l 4 байта
; идентификация
ProcedureCDLL DllAkelPadID(*pv.PLUGINVERSION)
*pv\dwAkelDllVersion = MAKE_IDENTIFIER(2, 2, 2, 0)
*pv\dwExeMinVersion3x = MAKE_IDENTIFIER(-1, -1, -1, -1)
*pv\dwExeMinVersion4x = MAKE_IDENTIFIER(4, 9, 6, 0)
*pv\pPluginName = @"MyPlugin"
EndProcedure
; Внешняя функция плагина
ProcedureCDLL OpenDialogTest(*pd.PLUGINDATA)
; Функция не поддерживает автозагрузку
*pd\dwSupport | #PDS_NOAUTOLOAD
If *pd\dwSupport & #PDS_GETSUPPORT
ProcedureReturn
EndIf
; SendMessage_(*pd\hMainWnd, #WM_COMMAND, #IDM_FILE_OPEN, 0)
MessageRequester("ура", "ура")
EndProcedure
; Точка входа
ProcedureDLL DllMain(hinstDLL, fdwReason, lpvReserved)
If lpvReserved = #DLL_PROCESS_ATTACH
ElseIf fdwReason = #DLL_THREAD_ATTACH
ElseIf fdwReason = #DLL_THREAD_DETACH
ElseIf fdwReason = #DLL_PROCESS_DETACH
EndIf
ProcedureReturn 1
EndProcedure
Но наверняка мне не нужны некоторые структуры по крайней мере на начальном этапе, или не нужны все её параметры, просто задать её нужным размером.
Posted: Fri May 08, 2020 6:20 am
by VladSh
А зачем для добавления пункта меню писать плагины? У меня как раз пункт "Плагины" добавлен:
Posted: Fri May 08, 2020 7:31 am
by AZJIO
VladSh
Ну если взять вызов "Format::LineFixWrap", здесь "Format" это имя DLL или имя импортированное из DLL. А также LineFixWrap это функция или Format это класс/модуль, а LineFixWrap функция внутри модуля. Я пока не понимаю связи. В исходнике нет описания или его мало или написано для знающих язык С++. Я смотрю плаг для Notepad++ на PureBasic, вижу функции в формате С++, там мне всё понятно, пытаюсь делать аналогию.
Если удастся разобраться в минималистическом коде, то в точку вызова мессаги я допустим вставлю вызов функции GUI, которая уже будет на понятном для меня языке PureBasic. Ну хотя придётся изучить язык вызова внутренних AkelPad-функций, чтобы получать позиции курсора и т.д. Но хотя бы начало получить, чтобы интерес был. Сразу я не смогу переписать заголовочный файл AkelDLL.h (и надо ли), могу пойти по неправильному пути. Я хоть и переписал пару структур, но по идее надо гуглить размер в байтах каждого типа данных для x86, а в будущем и для x64 или сразу, чтобы указать изменяющийся тип взависимости от битности.
Я скачал "Templates", думая что это пример, потом полегче взял "Exit", сейчас нашёл "AkelDLL", хотя я про него просто забыл, ранее пытался его ковырять. Ну да, он просто мессага, буду опять пробовать, вижу там функцию GetSelTextTest, которая экспортируется в окно плагов, а "AkelDLL" стало именем. Так что это имя файла и в ней функция, остаётся конвертировать жизненно необходимые структуры, в самой структуре PLUGINDATA несколько вложенных, так что не знаю, окажется ли во вложенных ещё вложенные, но цель уже понятна.
Posted: Fri May 08, 2020 8:15 am
by VladSh
Непонятна задача.
Пункт меню элементарно добавляется. Позиция курсора элементарно получается в скрипте. Из скрипта можно вызывать любые функции плагинов. В скрипте же можно напилить свой UI...
Зачем???
Posted: Fri May 08, 2020 8:17 am
by DV
Сам AkelPad и подавляющее большинство (если не все) его плагинов написаны на чистом C. Так что никаких классов.
В ContextMenu-Rus.txt читаем:
Call("Плагин::Функция"[, дополнительные параметры])
Call("Scripts::Main")
метод вызывает плагин Scripts и функцию Main.
Здесь `Scripts` - это имя плагина (файла с расширенем .dll), а `Main` - имя экспортируемой из .dll функции. Поскольку речь о чистом C, это имя функции as is (как в исходном коде), безо всяких mangled function name, могущих встретиться в C++ при экспорте функции без `extern "C"`.
Описанный подход для вызова функции из плагина не уникален для плагина ContextMenu или Scripts. Это универсальный подход, с помощью которого и AkelPad, и его плагины вызывают функции других плагинов.
Posted: Fri May 08, 2020 8:40 am
by AZJIO
так как вы отвечаете, сразу вопрос, вот макро с версией.
то есть просто создать 2 структуры. потом структуру MAKELONG и присвоить адрес структуры в *pv.dwExeMinVersion4x ?
А проще сразу 4-х байтовую структуру.
Это параметр pv->dwAkelDllVersion, где pv - указатель на структуру PLUGINVERSION.
То есть нужно просто установить pv->dwAkelDllVersion = MAKE_IDENTIFIER(2, 2, 2, 0)
Минимальный код плагина должен выглядеть примерно так:
// Identification
void __declspec(dllexport) DllAkelPadID(PLUGINVERSION *pv)
{
pv->dwAkelDllVersion = AKEL_DLL_VERSION;
pv->dwExeMinVersion3x = EXE_MIN_VERSION_3X;
pv->dwExeMinVersion4x = EXE_MIN_VERSION_4X;
pv->pPluginName = PLUGIN_NAME;
}
// Plugin extern function
void __declspec(dllexport) QSearch(PLUGINDATA* pd)
{
pd->dwSupport |= PDS_SUPPORTALL;
if ( pd->dwSupport & PDS_GETSUPPORT )
return;
// do something useful here...
// If this function did everything and currently there is no visible
// or invisible (not destroyed) dialog created by it -->
// Stay in memory, and show as non-active
return UD_NONUNLOAD_NONACTIVE;
// If this function created a dialog it is not destroyed -->
// Stay in memory, and show as active
return UD_NONUNLOAD_ACTIVE;
}
Posted: Mon May 11, 2020 8:26 am
by AZJIO
DV
Я уже получил нужный результат, ввёл 2.3.4.5 и в мессаге в тех же позициях получил нужное, потом поставил то что просит прога 2.2.2.0 и в итоге получил мессагу
так что нельзя сказать что я всё исключил в своём понимании. Если ранее я идентифицировал этот синтаксис как указатель на структуру, то структуры такой нет, а есть закомментированная функция:
Добавил пару тысяч констант, конвертируя их с помощью регулярных выражений. Жаль со структурами так не получится, хотя можно попробовать потом сравнить 2 файла на правильность конвертирования.
Может кто дать ширину полей в байтах (SizeOf) для x86 для структуры PLUGINDATA? Я задал nSaveSettings в 8 байт вместо 4-х, чтобы нормально возвращались дескриптор окна, но теперь у меня сдвинулся указатель на имя файла языкового модуля. Я уже замучился подгонять поля, чтобы угадать и получить валидные значения.
Для дельфи (в исходнике в комплекте) структура по другому иногда порядок полей.
Обновил DLL + исходник, теперь работает захват текста естественным образом, и структуры вроде выровнял.
INT, UINT, DWORD, LONG, BOOL - целое число, всегда 4 байта
INT_PTR, UINT_PTR, DWORD_PTR, LONG_PTR, WPARAM, LPARAM - целое число размера указателя: 4 байта для x86, 8 байт для x64
void*, int*, char*, HANDLE, HWND - указатель: 4 байта для x86, 8 байт для x64
Posted: Thu May 14, 2020 9:53 am
by AZJIO
DV
В чём отличие между окнами, например у меня окно закрывается после создания, я вставляю цикл, но тогда команды плагов блокируются, например не могу сменить подсветку с HTML на XML, а команды AkelPad не блокируются.