Plugin на PureBasic, нужна помощь

Discuss and announce AkelPad plugins
  • Author
  • Message
Offline
Posts: 330
Joined: Mon Jun 03, 2019 2:33 am

Plugin на PureBasic, нужна помощь

Post by AZJIO »

Хочу попробовать написать плаг на PureBasic, но хотелось бы иметь шаблон с чего начать.
Я уже делал плаги используя шаблон, а с нуля это попытаться с моей стороны сравнить шаблоны плагов на С++, чтобы произвести такие же действия для PureBasic, с учётом что я слабо разбираюсь в С++.

Offline
Posts: 330
Joined: Mon Jun 03, 2019 2:33 am

Post by AZJIO »

Может кто сделать плагин шаблон минимального типа, например для начала вывести мессагу "привет мир!". Собственно интересует встраивание пункта меню в интерфейс AkelPad, то есть в меню плагинов.

Я немного попытался
инклуд

Code: Select all

#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


Code: Select all

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


Но наверняка мне не нужны некоторые структуры по крайней мере на начальном этапе, или не нужны все её параметры, просто задать её нужным размером.
Last edited by AZJIO on Fri May 08, 2020 12:42 pm, edited 5 times in total.

Offline
Posts: 3217
Joined: Wed Nov 29, 2006 1:19 pm
Location: Киев, Русь
Contact:

Post by VladSh »

А зачем для добавления пункта меню писать плагины? У меня как раз пункт "Плагины" добавлен:
Image

Offline
Posts: 330
Joined: Mon Jun 03, 2019 2:33 am

Post 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 несколько вложенных, так что не знаю, окажется ли во вложенных ещё вложенные, но цель уже понятна.

Offline
Posts: 3217
Joined: Wed Nov 29, 2006 1:19 pm
Location: Киев, Русь
Contact:

Post by VladSh »

Непонятна задача.
Пункт меню элементарно добавляется. Позиция курсора элементарно получается в скрипте. Из скрипта можно вызывать любые функции плагинов. В скрипте же можно напилить свой UI...
Зачем???

DV
Offline
Posts: 1250
Joined: Thu Nov 16, 2006 11:53 am
Location: Kyiv, Ukraine

Post 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, и его плагины вызывают функции других плагинов.

Offline
Posts: 330
Joined: Mon Jun 03, 2019 2:33 am

Post by AZJIO »

так как вы отвечаете, сразу вопрос, вот макро с версией.

Code: Select all

((DWORD)MAKELONG(MAKEWORD(a, b), MAKEWORD(c, d)))
я не нашёл MAKEWORD как макро или функцию в заголовочных файлах, но гугл дал структуру

Code: Select all

WORD MAKEWORD(
   BYTE bLow,
   BYTE bHigh
);
то есть просто создать 2 структуры. потом структуру MAKELONG и присвоить адрес структуры в *pv.dwExeMinVersion4x ?
А проще сразу 4-х байтовую структуру.

Как-то так

Code: Select all

Structure MAKELONG
    StructureUnion
        Num.l
        byte.c[4]
    EndStructureUnion
EndStructure

NumVer.MAKELONG
NumVer\byte[0] = 4
NumVer\byte[1] = 9
NumVer\byte[2] = 6
NumVer\byte[3] = 0
Debug Str(NumVer\Num) ; возвращает 589828, сходится?

Offline
Posts: 330
Joined: Mon Jun 03, 2019 2:33 am

Post by AZJIO »

В общем скомпилировал я плаг, он видит его в плагах, при клике выдаёт
---------------------------
AkelPad
---------------------------
Обновите 1test плагин.

Архитектура AkelPad'а: 2.2.2.0
Архитектура 1test плагина: 2.0.2.0
---------------------------
ОК
---------------------------
Код плага выше я обновил пост №2

Такая функция выдала то что надо... Мессага вылетела, но AkelPad упал.

Code: Select all

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

DV
Offline
Posts: 1250
Joined: Thu Nov 16, 2006 11:53 am
Location: Kyiv, Ukraine

Post by DV »

AZJIO wrote:Архитектура AkelPad'а: 2.2.2.0
Архитектура 1test плагина: 2.0.2.0
Это параметр pv->dwAkelDllVersion, где pv - указатель на структуру PLUGINVERSION.
То есть нужно просто установить pv->dwAkelDllVersion = MAKE_IDENTIFIER(2, 2, 2, 0)

Минимальный код плагина должен выглядеть примерно так:

Code: Select all

// 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;
}
Last edited by DV on Fri May 08, 2020 11:48 am, edited 1 time in total.

Offline
Posts: 330
Joined: Mon Jun 03, 2019 2:33 am

Post by AZJIO »


DV
Я уже получил нужный результат, ввёл 2.3.4.5 и в мессаге в тех же позициях получил нужное, потом поставил то что просит прога 2.2.2.0 и в итоге получил мессагу

Code: Select all

MessageRequester("ура", "ура")
но AkelPad упал после её закрытия. Сейчас проблема устранить падение. Я дописал необходимые вложенные структуры, но не помогло, пока падает.

Только не смог понять эту запись в структуре, в строке 1182

Code: Select all

PLUGINPROC PluginProc;          //Function procedure
так что нельзя сказать что я всё исключил в своём понимании. Если ранее я идентифицировал этот синтаксис как указатель на структуру, то структуры такой нет, а есть закомментированная функция:

Code: Select all

BOOL CALLBACK PluginProc(void *lpParameter, LPARAM lParam, DWORD dwSupport)
и что с этим делать? Какой там размер, я бы просто игнорировал бы, пускай там указатель 0, может оно не используется, главное ширину сохранить.



Всё, теперь не падает, помогла строка

Code: Select all

ProcedureCDLL OpenDialogTest(*pd.PLUGINDATA)
тут получил ещё помощь

Добавил пару тысяч констант, конвертируя их с помощью регулярных выражений. Жаль со структурами так не получится, хотя можно попробовать потом сравнить 2 файла на правильность конвертирования.



Может кто дать ширину полей в байтах (SizeOf) для x86 для структуры PLUGINDATA? Я задал nSaveSettings в 8 байт вместо 4-х, чтобы нормально возвращались дескриптор окна, но теперь у меня сдвинулся указатель на имя файла языкового модуля. Я уже замучился подгонять поля, чтобы угадать и получить валидные значения.

Для дельфи (в исходнике в комплекте) структура по другому иногда порядок полей.


Обновил DLL + исходник, теперь работает захват текста естественным образом, и структуры вроде выровнял.

DV
Offline
Posts: 1250
Joined: Thu Nov 16, 2006 11:53 am
Location: Kyiv, Ukraine

Post by DV »

AZJIO wrote:Обновил DLL + исходник
Может, стоит создать проект на GitHub, например?
Мне с yadi.sk не удаётся скачать, браузер сообщает, что "We can’t connect to the server at yadi.sk".

Offline
Posts: 330
Joined: Mon Jun 03, 2019 2:33 am

Post by AZJIO »

DV
upload.ee
Может, стоит создать проект на GitHub, например?
я пока не понимаю как он работает. Я выложил там 1 раз, и не понял, правильно ли получилось.

Кодер для подсветки кода PureBasic, если что...

DV
Offline
Posts: 1250
Joined: Thu Nov 16, 2006 11:53 am
Location: Kyiv, Ukraine

Post by DV »

Поигрался с версией x86 (32-bit), работает!
На всякий случай, небольшая памятка по часто используемым в WinAPI типам:

Code: Select all

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

Offline
Posts: 330
Joined: Mon Jun 03, 2019 2:33 am

Post by AZJIO »

DV
В чём отличие между окнами, например у меня окно закрывается после создания, я вставляю цикл, но тогда команды плагов блокируются, например не могу сменить подсветку с HTML на XML, а команды AkelPad не блокируются.

Обновлённый, все функции на кнопках GUI.

Offline
Posts: 330
Joined: Mon Jun 03, 2019 2:33 am

Post by AZJIO »

флаг bOldWindows это в какой юникод не поддерживается? Ниже XP?
Post Reply