Принципы построения, создания и использования Dynamic Link Library (DLL)
Среды разработки и языки: Borland С++ Builder 6, lua 5, PtokaX 3.6.*.*
Немного теории.
DLL - это один или несолько участков кода, хранимых в файле с расширением .dll. Код, содержащийся в dll, может быть вызван из испольняемой программы, но сама dll не является программой. DLL может содержать функции, классы и ресурсы. Прежде чем использовать dll, её нужно загрузить в память. Существуют два вида загрузки dll: статическая и динамическая. Статическая загрузка означает, что dll автоматически загружается при запуске приложения. DLL содержит экспортируемые функции, а описания этих функций находятся в библиотеке импорта (import library). Библиотеки импорта имеют имя, совпадающее с именем соответствующей dll, и расширение .lib. Для использования статической загрузки необходимо на этапе компоновки к проекту подключить lib-файл для dll. DLL загружается при загрузке приложения и можно вызывать экспортируемые функции так же, как и любые другие функции. Это самый лёгкий подход. Недостаток его в том, что при отсутствие необходимой dll программа не будет загружаться ваобще. Второй тип загрузки - Динамическая загрузка. Означает, что DLL загружается при необходимости и выгружается по окончанию использования. Достоинством является то, что dll находится в памяти до тех пор, пока она нужна, что приводит к более эффективному использованию памяти. В lua мы используем динамический вид загрузки.
Ещё немного о содержании dll библиотек. Как же прсмотреть содержимо dll библиотеки? Ведь dll файл нельзя ничем открыть. Для просмотра содержимого dll существуют специальные утилиты. Я использую Борнандовскую утилиту tdump (находится в папке bin вместе с bcb.exe).
Далее писать теорию влом. Если что, то спрашивайте попробую объяснить)))
Приступим к написанию.
1. Открываем Builder C++ 6 2. Закрываем автоматически созданную форму (file -> close all) 3. Создаём новый проект (file -> new -> Other -> dll wizard) указываем тип "C" (в противном случае будут необходимы некоторые манипуляции с extern "C") я уще обычно ставлю галку vc++ style dll (но это не принципиально, просто будет различаться имя основной функции dll). 4. Далее в созданном проекте видим огромный коммент -> стираем его нах (в кратце он говорит о том что для того чтобы использовать библиотеку srting необходим дополнительный гемор). 5. функцию DllMain не трогаем, а пишем выше (или ниже) следующий код (данный код является самым простейшим):
6. сохраняем проект (file -> save project). вам будет сначало предложено сохранить юнит, а потом уже проект. 7. для компиляции нужны заголовочные файлы (из исходников),а именно файлы: "lua.h", "lualib.h", "lauxlib.h" и "luaconf.h". 8. все эти файлы можно найти скачав исходники lua. 9. эти файлы надо поместить в директорию проекта. 10. также нужна библиотека статической загрузки "PXLua.lib" 11. создаем dll (project -> build all) 12. копируем dll из папки проекта в папку с PtokaX (dll обычно имеет имя проекта. по умолчанию "project1.dll") 13. пишем скрипт
тут функция содержит впереди символ подчеркивания - это "украшения" компилятора))) (хотя существуют способы убрать этот символ, но я их тут рассматривать не буду). Просмотреть названия функций в библиотеке можно опять же воспользовавшись утилитой tdump.exe 14. заходим на хаб и перезагружаем скрипт. должно появиться "Hello World!".
p.s. для написания dll для PtokaX 0.4.*.* необходима библиотека статической загрузки "PXLua.lib" для данной версии и соответственно изменить SendToAll на Core.SendToAll
p.p.s. чуть позже выложу более сложные функции (ща влом писать)
Отличная работа. просто замечательно что ты уделил время и занялся этим.
Код
DLL может содержать функции, классы и ресурсы.
по-поводу ресурсов. что кроме чистого кода может содержать dll билеотека?
Автор: Setuper 1.7.2008, 22:49
это не относится к lua. Ресурсы в lua использовать можно но бесполезно. Ресурсами могут быть например картинки в бинарном коде или иконки или например загнать в длл в качестве ресурсов некоторую таблицу переменных и использовать эти переменные в программе (переменным присваиваются некие строковые значения) - таким образом можно осуществять перевод программы на другие языки не переписывая всю программу, а переписав только длл файл. Ресурсы обычно используются прикладными программами (программами, написанными на с++), а не lua скриптами. Ресурсы при создании хранятся в специальном файле с расширением .rc или .res
Автор: Svyat 1.7.2008, 23:03
ну про ресурсы я чисто из интереса спросил. понятное дело что они не применимы в данном случае.
Автор: Nickolya 2.7.2008, 12:46
^_^ Ееее! Начало положено, спасибо Setuper'у!
Вот короче результат:
Цитата
[Linker Error] Unresolved external '_lua_pushstring' referenced from E:\BORLAND CBUILDER6\PROJECTS\2ND\UNIT1.OBJ
Даже получившийся код приведу, вдруг тут где-то накосячил:
Исходники для 0.4, те что выложены сейчас на луаборде, а именно PXLua-5.1.3-src.7z. Всё по инструкции, расположенной выше. Кстати 0.3.6 уже не актуальна, так что думаю лучше будет сразу рассматривать это дело для 0.4.*.*
Автор: Setuper 10.7.2008, 20:00
данная ошибка возникает из-за того, что в новой библиотеке PXLua.dll убрали символ "_" перед функцией (так называемый name mangling). библиотека ищет в библиотеке PXLua.dll функцию "_lua_pushstring" и не находит, поскольку там функция "lua_pushstring".
Автор: Setuper 11.7.2008, 23:46
Более сложный пример использования dll. Пример вызова api-функции windows: "MessageBoxA" из системной библиотеки "user32.dll".
msg = package.loadlib("PXDLL.dll", "_msg") Message=msg() Message(nil,"Приступаю к форматированию диска!","format c:",2)
P.S. PXDLL.dll - вами созданная dll библиотека
Автор: Nickolya 13.7.2008, 23:18
Цитата(Setuper @ 10.7.2008, 20:00)
данная ошибка возникает из-за того, что в новой библиотеке PXLua.dll убрали символ "_" перед функцией (так называемый name mangling). библиотека ищет в библиотеке PXLua.dll функцию "_lua_pushstring" и не находит, поскольку там функция "lua_pushstring".
И как добиться того чтобы небыло этого злощастного подчеркивания при компиляции??
lauxlib.h - это очепятка или так и должно быть? напрашивается luaxlib.h
Автор: Setuper 31.8.2008, 17:31
нет! это не опечатка! но разницы нет! в исходниках lua есть как файл lauxlib.h так и файл luaxlib.h - они полностью идентичны для того чтобы народ не перепутал вдруг))))))
а исходное имя заголовочного файла конечно же было: lauxlib.h имя файла расшифровывается как: Lua Auxiliary for libraries переводится как: Lua помошник для построения библиотек
Автор: Wariner 13.10.2008, 16:50
Цитата(Nickolya @ 2.7.2008, 13:46)
^_^ Ееее! Начало положено, спасибо Setuper'у!
Вот короче результат:
Даже получившийся код приведу, вдруг тут где-то накосячил:
Исходники для 0.4, те что выложены сейчас на луаборде, а именно PXLua-5.1.3-src.7z. Всё по инструкции, расположенной выше. Кстати 0.3.6 уже не актуальна, так что думаю лучше будет сразу рассматривать это дело для 0.4.*.*
mangling name убирается, если использовать calling convention передачи параметров через стек: __stdcall
Автор: Setuper 16.11.2008, 19:29
Построение dll библиотек под API2. Чего я добился.
mangling name (украшение имени функции, дополнительные символы в имени функции) убирается, если использовать calling convention передачи параметров через стек, а именно, установкой параметра __stdcall во всех экспортируемых функциях. Этот параметр указывает на то, что все параметры функций будут передаваться через стек, и при этом стек будет очищаться в конце выполнения той функции, которая вызывается. Этот параметр применим только для функций, которые содержать конечное число параметров. Можем убедиться, что при использовании __stdcall, mangling name не убирается у функций lua_pushfstring и luaL_error, это объясняется тем, что эти функции содержат переменное число параметров. Однако, во всех других функциях mangling name убрался.
Итак, как проверить имена функции в dll библиотеке? Я буду рассматривать всё в Borland C++ Builder. Эта среда разработки выбрана потому, что именно инструментами компании Borland пользуются разработчики PtokaX.
Для проверки содержимого dll библиотеке будем использовать утилиту tdump.exe. Её можно найти в папке bin установленного Builder C++, или просто скачать: tdump.rar ( 108.4 килобайт )
: 38
Копируем эту утилиту, например, на рабочий стол, и сюда же копируем dll библиотеку, содержимое которой хотим просмотреть. Далее, идём Пуск-Выполнить, набираем cmd, нажимаем enter, вводим: cd рабочий стол, жмём enter, вводим: tdump имя_библиотеке.dll 1.txt. После всего этого на рабочем столе должен появиться файл 1.TXT, в котором будут описаны все функции, которые содержатся в нашей dll.
Для выявления отличий в dll библиотеках разных версий api просматриваем библиотеки PXLua.dll из разных версий api и видим, что в старом апи перед всеми функциями находится символ _ - это так называемый mangling name, или его ещё называют украшением имени функций dll. В библиотеке нового апи этот символ перед функциями отсутствует везде, кроме двух функций, о которых я писал выше.
Как же избавиться от этого mangling name? Создадим свою библиотеку, в которой будет единственная функция: hello_world.
Открываем Borland C++ Builder (в дальнейшем я буду писать сокращённо по первым буквам BCB). У нас появилась какая-то форма. Лезем в меню File -> Close All. Далее, File -> New -> Other -> DLL Wizard. Устанавливаем переключатель в С++ и снимаем все галки, жмём ОК. Далее заходим: View -> Project Manager, щёлкаем на плюсик рядом с надписью Project1.dll, выделяем Project1.res и нажимаем Remove, выделяем Unit1.cpp и нажимаем Remove, после этого закрываем окно Project Manager, создаём на рабочем столе папку test, входим File -> Save Project As... и сохраняем в созданную папку проект с именем test.bpr, далее, в этой папке надо создать 2 папки с именами src и obj. Качаем с сайта PtokaX (http://mydc.ru/r/?http://www.ptokax.ch) PtokaX 0.4.x.x Libs Package. Скачается архив: PtokaX_API2_Extensions.rar, распаковываем его, далее, используя утилиту 7Zip (http://mydc.ru/r/?http://www.7-zip.org), распаковываем архив: PXLua-5.1.3-src.7z, копируем в нашу папку src из их папки src следующие файлы: lauxlib.h, lua.h, luaconf.h, lualib.h. Сразу же открываем файл luaconf.h (например, с помощью NotePad++), находим строчки:
Строчка #pragma comment(lib,"PXLua.lib") подключает статическую библиотеку, которая, в принципе, есть в скаченных исходниках архива PXLua-5.1.3-src.7z, копируем эту PXLua.lib в нашу папку obj. Далее, в BCB заходим в Project -> Options -> Directories/Conditionals, устанавливаем следующие параметры: Include path: obj;src;$(BCB)\include;$(BCB)\include\vcl, Library path: obj;src;$(BCB)\lib\obj;$(BCB)\lib, Debug source path: $(BCB)\source\vcl, Intermediate output: путь_до_вашей_папки_obj, Final output: путь_до_вашей_папки_obj, далее, нажимаем на три точки напротив Conditional defines и добавляем макросы: LUA_BUILD_AS_DLL и BUILD_AS_DLL. Жмём ОК. В закладке Linker убираем галку с Use dynamic RTL, в закладке Packages убираем галку с Build with runtime packages. На этом настройка завершена. Далее, заходим в View -> Project Manager и добавляем к проекту файлы: main.h и main.cpp Строим проект: Project -> Build test.
При построении проекта вылезает ошибка: [Linker Error] Unresolved external '_lua_pushstring' referenced from... Возможно, линковщиком ищется функция _lua_pushstring, но в либе PXLua.lib находится ссылка на функцию lua_pushstring. Как быть? Как убрать mangling name? Mangling name убирается следующим кодом: main.h:
Строим проект, и видим ошибку: [Linker Error] Unresolved external 'lua_pushstring' referenced from... Это уже не поддаётся объяснению. Вскрываем библиотеку PXLua.lib с помощью утилиты tdump и видим, что ссылки на функцию lua_pushstring в ней есть! Возможно либа PXLua.lib - битая, но если она битая, то из исходников мы можем построить свою либу с этими же функциями.
Строим свою либу! Точно так же создам проект, но с названием PXLua, всё точно также делаем, но только в файле luaconf.h мы ничего не меняем (берём исходный файл из PXLua-5.1.3-src.7z), файлы main.h и main.cpp мы не создаём. Копируем в нашу папку src всё содержимое папки src архива PXLua-5.1.3-src.7z. Точно также стоим проект и получаем в папке obj файлы PXLua.lib и PXLua.dll. Но если открыть эти файлы утилитой tdump, то мы увидим там mangling name. Избавиться от него можно таким же способом, который я предлагал. Но чтобы вам не переписывать все файлы, я выложу уже переписанные файлы, вам надо только скопировать всё в папку src: src.rar ( 133.98 килобайт )
: 8 Строим проект и получаем опять же PXLua.lib и PXLua.dll. Помещаем в папку obj нашего первого проекта файл PXLua.lib и строим наш первый проект. DLL успешно создалась. Ура!
Копируем нашу DLL в папку с PtokaX и пишем скрипт:
И тут, при запуске скрипта, выскакивает ошибка (ну надо же, на последней стадии). Из-за чего? Я понять до сих пор не могу! Ведь вроде убрали mangling name, построили (создали) свою dll библиотеку, но ничего не пашет!!! Что за несовместимости? Из-за чего они возникают? Почему наша библиотека не находит в библиотеке PXLua.dll нужную функцию? Кто-нибудь может ответить на эти вопросы?
Такая тупиковая ситуация называется DLL hell !
Автор: alex82 25.1.2009, 7:39
Цитата
Как быть? Как убрать mangling name?
А почему бы не использовать Fastcall? Тогда библиотека соберётся (во всяком случае у меня собралась).
Автор: Setuper 25.1.2009, 12:57
А возможно ли её подключить из скриптов? Собраться то она соберётся, но не подключиться.
Автор: alex82 25.1.2009, 14:00
Подключится, если поплясать с бубном. .
Во-первых:
Цитата(PPK)
Initializing in lua 5.1.1 like
Код
require "lalalalib"
И если в LUA 5.1.1 старый способ ещё работал, то в 5.1.3 - уже нет. А отсюда следует что имя библиотеки(без расширения) и имя вызываемой функции (а точнее то, что следует после luaopen_) должны совпадать. А иначе как - аргумент-то один.
PS. Я тоже долго бился головой ап стену пытаясь понять, почему библиотека подгружается нормально, а функция вызываться никак не хочет.
Автор: Setuper 25.1.2009, 18:56
ок. Как время будет покопаюсь, посмотрю.
Может скомпилишь тогда либу для работы с бд, раз у тебя получилось?
Автор: alex82 26.1.2009, 0:18
Цитата(Setuper @ 25.1.2009, 17:56)
Может скомпилишь тогда либу для работы с бд, раз у тебя получилось?
Сомневаюсь что у меня получится. Я си практически не знаю.
Автор: alex82 22.2.2009, 16:51
Setuper
У тебя есть исходники PXSqlite (той самой, которая нормально работает под птокой 0.3.6.0)? Если да - выложи плиз.
Автор: Setuper 24.2.2009, 17:05
Где-то тут на форуме уже выкладывал, ну да ладно, видимо затерялись где-то. Выложу ещё раз, не сложно)))
CodeGear (читай Borland) C++ Builder. Но это не суть важно - C++ Builder прекрасно хавает проекты созданные в BDS, и, я так думаю, BDS схавает проект C++ Builder'а.
Автор: Setuper 24.2.2009, 23:42
Просто у меня BDS нету, есть только Builder C++ 6 и Visual studio 6, Visual studio 2005, Visual studio 2008. C++ Builder 6 не открывает ни проект cbproj, ни bdsproj
Я пытался компилить только на том, что у меня есть, а вообще я приверженец VS.
Автор: alex82 24.2.2009, 23:54
Если не лень качать 800 с лишним метров, тогда вот - http://mydc.ru/r/?http://torrents.ru/forum/viewtopic.php?t=365896
Автор: Setuper 24.2.2009, 23:58
Спасибо. Скачаю и попробую скомпилить.
Автор: Setuper 7.3.2009, 14:00
Создание простейшей либы под API 2 (by Builder C++ 6).
Создаём dll проект (обязательно язык С). Пишем код:
Код
#include "lua.h" #include "lauxlib.h" #pragma link "PXLua.lib"
int lua_hello(lua_State *L) { lua_pushstring(L, "Hello World!"); return 1; }
Создаём в папке проекта def файл (Define.def) В созданный файл записываем:
Сохраняем и добавляем в проект этот самый файл Define.def
Сохраняем проект. Залезаем в настройки компилятора, и ставим calling convention: Register (J)
Строи проект.
После этого пишем lua скрипт:
Код
local Msg = package.loadlib("Project1", "lua_hello")
if Msg then Core.SendToAll(Msg()) end
Все необходимые заголовочные файлы прикладываю:files.rar ( 13.27 килобайт )
: 18
Автор: alex82 7.3.2009, 16:08
Если библиотека не создаёт ни одной функции, которую можно вызвать из скрипта, то при чём здесть вообще API2? Это обычная DLL-библиотека.
Автор: Setuper 7.3.2009, 16:13
А смысл тогда создавать библиотеку? Библиотеку нужно создавать для того, чтобы взаимодействовали функции СИ с функциями LUA (в контексте данного форума).
Автор: alex82 7.3.2009, 18:18
Ну ладно.
Тады продолжим в том же духе.
Создание библиотеки под API2 Урок второй: Вызов WinAPI функции из Lua-скрипта. Необходимые инструменты: * PtokaX 0.4.x.x * Borland C++ Builder * Текстовый редактор с подсветкой кода C (в принципе, можно использовать редактор встроенный в C++ Builder, но лично мне он не очень нравится) * А также клавиатура, мышь, и немного мозга
1. Создаём проект DLL-библиотеки (на языке C), и сразу же лезем в его настройки (Project -> Options). Здесь выбираем Build configuration - Base, переходим на вкладку C++ Compiler -> General compilation, и меняем опцию Calling convention на Fastcall (Register). Далее переходим на на вкладку Linker -> Linking, и отключаем пункт Dynamic RTL. Ну вот, с настройками вроде разобрались. Теперь переименуем проект. Назовём его, ну, скажем, PXHello.
2. Пишем код:
Код
#include <windows.h> //Это необходимо для использования функций WinAPI #include "lua.h" #include "lualib.h" #include "lauxlib.h" #pragma comment(lib, "PXLua.lib")
//Собственно, это и есть функция, которую мы будем вызывать из Lua-скрипта: static int msgbox (lua_State *L) { //Функциям, вызываемым из Lua-скриптов, всегда передаётся только один аргумент - адрес массива lua_State, который содержит информацию о состоянии Lua //При вызове функции из скрипта, её аргументы помещаются в стек. const char *header = luaL_checkstring (L, 1); //Забираем из стека первый аргумент функции const char *text = luaL_checkstring (L, 2); //Забираем из стека второй аргумент функции MessageBox(0,text,header,0); //Вызываем функцию WinAPI MessageBox return 0; }
//Массив luaL_reg, содержащий список функций, которые можно будет вызвать из скрипта. В нашем случае функция всего одна. static const struct luaL_reg functions[] = { {"MessageBox", msgbox}, {NULL, NULL}, //Так всегда должен выглядеть последний элемент массива luaL_reg };
//Функция, вызываемая при инициализации библиотеки: int libinit (lua_State *L) { luaL_register (L, "Win", functions); //Второй аргумент - имя таблицы, в которую будут помещены функции, доступные из скриптов, третий аргумент - адрес массива luaL_reg (См. выше) return 1; }
Далее создаём в папке проекта файл с расширением .def (имя файла может быть любым), и добавляем в него следующее:
function OnStartup() Win.MessageBox("Hello World!!!", "Фигасе!!! Это работает!") --Первый аргумент - заголовок окна сообщения, второй - текст, отображаемый в окне end
Если Вы всё сделали правильно, то при запуске скрипта появится сообщение:
function luaL_checklstring(L: Plua_State; numArg: Integer; s: Psize_t): PChar; cdecl external pxlib name '@luaL_checklstring'; procedure luaL_register(L: Plua_State; libname: PChar; const R: PluaL_reg); cdecl external pxlib name '@luaL_register';
function luaL_checkstring(L: Plua_State; N: Integer): PChar; begin Result := luaL_checklstring(L, N, nil); end;
//Собственно, это и есть функция, которую мы будем вызывать из Lua-скрипта: function msgbox (L: Plua_State): Integer; cdecl; //Функциям, вызываемым из Lua-скриптов, всегда передаётся только один аргумент - адрес массива lua_State, который содержит информацию о состоянии Lua var header, text: PChar; begin //При вызове функции из скрипта, её аргументы помещаются в стек. header := luaL_checkstring (L, 1); //Забираем из стека первый аргумент функции text := luaL_checkstring (L, 2); //Забираем из стека второй аргумент функции MessageBox(0, text, header, 0); //Вызываем функцию WinAPI MessageBox result := 0; end;
//Массив luaL_reg, содержащий список функций, которые можно будет вызвать из скрипта. В нашем случае функция всего одна. const functions: array [0..1] of TluaL_reg = ( (Name: 'MessageBox'; Func: @msgbox), (Name: nil; Func: nil) //Так всегда должен выглядеть последний элемент массива luaL_reg );
//Функция, вызываемая при инициализации библиотеки: function luaopen_pxhello2(L: Plua_State): Integer; cdecl; begin luaL_register(L, 'Win', @functions); //Второй аргумент - имя таблицы, в которую будут помещены функции, доступные из скриптов, третий аргумент - адрес массива luaL_reg (См. выше) result := 1; end;
exports luaopen_pxhello2;
begin end.
PS: и вообще существуют ли в природе хидеры под новый луа? (под delphi естественно)
Автор: Setuper 17.2.2011, 9:53
Ну и откуда у тебя компилятор узнает о том, что такое, например, TLuaFunc ?
Автор: Jonathan 17.2.2011, 10:16
Цитата(Setuper @ 17.2.2011, 15:53)
Ну и откуда у тебя компилятор узнает о том, что такое, например, TLuaFunc ?
так есть же
Код
PLuaFunc = ^TLuaFunc; TLuaFunc = function (L: Plua_State): Integer; cdecl;
проблем компиляции нет проблема при попытке запустить скрипт
Код
require "pxhello2"
function OnStartup() Win.MessageBox("Hello World!!!", "Фигасе!!! Это работает!") --Первый аргумент - заголовок окна сообщения, второй - текст, отображаемый в окне end
я так думаю, что птока спотыкается здесь
Код
function luaopen_pxhello2(L: Plua_State): Integer; cdecl; begin luaL_register(L, 'Win', @functions); //Второй аргумент - имя таблицы, в которую будут помещены функции, доступные из скриптов, третий аргумент - адрес массива luaL_reg (См. выше) result := 1; end;