Page 1 of 3

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

Posted: Fri Jun 07, 2019 6:54 pm
by AZJIO
Хочу попробовать написать плаг на PureBasic, но хотелось бы иметь шаблон с чего начать.
Я уже делал плаги используя шаблон, а с нуля это попытаться с моей стороны сравнить шаблоны плагов на С++, чтобы произвести такие же действия для PureBasic, с учётом что я слабо разбираюсь в С++.

Posted: Fri May 08, 2020 12:45 am
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


Но наверняка мне не нужны некоторые структуры по крайней мере на начальном этапе, или не нужны все её параметры, просто задать её нужным размером.

Posted: Fri May 08, 2020 6:20 am
by VladSh
А зачем для добавления пункта меню писать плагины? У меня как раз пункт "Плагины" добавлен:
Image

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
так как вы отвечаете, сразу вопрос, вот макро с версией.

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, сходится?

Posted: Fri May 08, 2020 10:00 am
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

Posted: Fri May 08, 2020 11:18 am
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;
}

Posted: Mon May 11, 2020 8:26 am
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 + исходник, теперь работает захват текста естественным образом, и структуры вроде выровнял.

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

Posted: Tue May 12, 2020 8:44 am
by AZJIO
DV
upload.ee
Может, стоит создать проект на GitHub, например?
я пока не понимаю как он работает. Я выложил там 1 раз, и не понял, правильно ли получилось.

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

Posted: Tue May 12, 2020 11:35 am
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

Posted: Thu May 14, 2020 9:53 am
by AZJIO
DV
В чём отличие между окнами, например у меня окно закрывается после создания, я вставляю цикл, но тогда команды плагов блокируются, например не могу сменить подсветку с HTML на XML, а команды AkelPad не блокируются.

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

Posted: Sat May 16, 2020 5:41 pm
by AZJIO
флаг bOldWindows это в какой юникод не поддерживается? Ниже XP?