s_mike@rambler.ru написал: Здравствуйте.Как выгрузить библиотеку при завершении скрипта?
Добрый день. Насколько мне известно, в Lua нет функционала принудительной выгрузки модулей, но возможность есть, вот пример (sqlite3.dll под рукой не было, проверил на odbc.dll):
Код
function main()
assert(package.loadlib(getScriptPath() .. "\\lib\\luasql\\odbc.dll", "luaopen_luasql_odbc")) ()
env = luasql.odbc()
local db = env:connect("LuaTest","login","pass")
db:close()
env:close()
print_loaded_modules()
unrequire('luasql')
message('**********')
print_loaded_modules()
end
function print_loaded_modules()
for k,v in pairs(package.loaded) do
message(string.format("%s=%s", k, tostring(v)))
end
end
function unrequire(m)
package.loaded[m] = nil
_G[m] = nil
end
Перед тем как задать вопрос, убедитесь, что решение Вашей задачи не описано в официальном мануале - 'Использование Lua в Рабочем месте QUIK.pdf' https://arqatech.com/upload/Public/quik_lua.zip
И еще один момент, при завершении скрипта модули автоматически выгружаются.
Перед тем как задать вопрос, убедитесь, что решение Вашей задачи не описано в официальном мануале - 'Использование Lua в Рабочем месте QUIK.pdf' https://arqatech.com/upload/Public/quik_lua.zip
т.к. ссылка на модуль уничтожается, в этом случае сборщик мусора VM lua в теории должен подчистить память при очередном забеге.
Перед тем как задать вопрос, убедитесь, что решение Вашей задачи не описано в официальном мануале - 'Использование Lua в Рабочем месте QUIK.pdf' https://arqatech.com/upload/Public/quik_lua.zip
s_mike@rambler.ru написал: И еще один вопрос по теме.в случае индикатора этот способ будет работать? Eстройство терминала в этом вопросе для меня туманная загадка...при старте loadlib() и connect() в КАЖДОМ экземпляре индикатора и в ondestroy() вычищение его из _Gпроблемы ожидаются?
Добрый день. Проблем не должно быть, ведь каждый Lua скрипт, и каждый Lua индикатор крутятся в своем собственном экземпляре виртуальной машине Lua, соответственно в каждой из этой виртуальной машине совой экземпляр таблицы _G, package.loaded и т.д.
Перед тем как задать вопрос, убедитесь, что решение Вашей задачи не описано в официальном мануале - 'Использование Lua в Рабочем месте QUIK.pdf' https://arqatech.com/upload/Public/quik_lua.zip
Указанным способом модуль после использования вроде бы и выгружается (в списке package.loaded его нет), но фактически же dll-файл Квиком не освобождается. Если нужно перекомпилировать и перезапустить dll-модуль, то только через перезапуск Квика.
Может есть какой-то способ высвобождать dll (используемую в скрипте через require) без перезапуска всего терминала?
Latrop написал: Указанным способом модуль после использования вроде бы и выгружается (в списке package.loaded его нет), но фактически же dll-файл Квиком не освобождается.
При нормальном завершении скрипта - освобождается. Обычно ж перекомпилировать начинают, если скрипт вывалился с ошибкой, а тут есть косячок-с.
М-да, странности. Если dll исходно на С++, то норм высвобождается, а если на C#, то нет
Код
[DllExport("luaopen_MyModule", CallingConvention = CallingConvention.Cdecl)]
public static int luaopen_MyModule(IntPtr L) {
return 0;
}
Ошибок не выдает, и даже такая пустышка не хочет выгружаться, хотя норм загружается, отладчик норм в метод заходит и выходит, lua тоже норм все отрабатывает, по OnStop корректно завершается. OnInit не используется.
Видимо DllExport что-то там не того. Тут мои компетенции как бы все.
Latrop написал: Ошибок не выдает, и даже такая пустышка не хочет выгружаться, хотя норм загружается
Забавно, что пишущие под дотнет обычно гораздо хуже представляют себе, как этот самый дотнет работает, нежели те, кто его обходит стороной. Почему, интересно.
Вы же знаете, что квик - нативное приложение? Это значит, что перед загрузкой вашей дотнетной длл нужно инициализировать дотнет. Из сей это делается как? Правильно, путем инициализации соответствующего COM-объекта. Вопрос раз: кто и когда дернет ему Release()? Вопрос два: кто и когда дернет DllCanUnloadNow() и выгрузит длл с дотнетным рантаймом? Боюсь оба ответа - никто, никогда. Так чего и ждать-то.
Попробую поднять данную тему вновь. Вобщем всё что выше написано - не работает Библиотека не выгружается!. Библиотека на C++. Выше писали, что типа если на чистом C++ то выгружается нормально. Не выгружается. Есть ли ещё какой способ выгрузить свою же DLL, которую Lua выгружать почему-то не хочет. Странное вообще какое-то поведение Lua. Скрипт отработал, библиотека - осталась. Функций в Lua для выгрузки не нашёл, что тоже довольно таки странно. Загружать значит можно, выгружать нет. Приходится постоянно перезагружать QUIK, чтобы удалить файл DLL, поскольку система не даёт его удалить, пока библиотека используется Lua. А удалять файл надо, чтобы после перекомпиляции DLL, новый DLL-файл записать на место того, что надо удалить. Это сильно напрягает поскольку сам QUIK грузится не быстро. Сталкивался кто с таким? Как ещё можно решить пролему? Ведь библиотека же чисто моя, зачем она вообще удерживается Lua - не понятно. Закончился скрипт без ошибок - освободи, но нет. Уж не знаю как решить, думаю уже как-то искать ID библиотеки, ну или там хэндл какой и выгружать отдельно программой через API винды.
Alexander написал: Попробую поднять данную тему вновь. Вобщем всё что выше написано - не работает Библиотека не выгружается!. Библиотека на C++. Выше писали, что типа если на чистом C++ то выгружается нормально. Не выгружается. Есть ли ещё какой способ выгрузить свою же DLL, которую Lua выгружать почему-то не хочет. Странное вообще какое-то поведение Lua. Скрипт отработал, библиотека - осталась. Функций в Lua для выгрузки не нашёл, что тоже довольно таки странно. Загружать значит можно, выгружать нет. Приходится постоянно перезагружать QUIK, чтобы удалить файл DLL, поскольку система не даёт его удалить, пока библиотека используется Lua. А удалять файл надо, чтобы после перекомпиляции DLL, новый DLL-файл записать на место того, что надо удалить. Это сильно напрягает поскольку сам QUIK грузится не быстро. Сталкивался кто с таким? Как ещё можно решить пролему? Ведь библиотека же чисто моя, зачем она вообще удерживается Lua - не понятно. Закончился скрипт без ошибок - освободи, но нет. Уж не знаю как решить, думаю уже как-то искать ID библиотеки, ну или там хэндл какой и выгружать отдельно программой через API винды.
А причем здесь скрипт? Скрипт - это даже не программа, а все лишь байткод для луа машины. Вспомните, когда dll выгружается.
Alexander написал: Попробую поднять данную тему вновь. Вобщем всё что выше написано - не работает Библиотека не выгружается!.
Явный вызов FreeLibrary для твоей либы (которую ты загрузил в скрипте, не так ли?) - не работает?
Или когда ты ожидаешь что либа будет отгружена? Автоматически, по завершении/отмене скрипта? Такого нет. Нужно явно выгружать либу. Ну и конечно, если либа выгружена, а ты к ней будешь обращаться - все попадает :)
Alexander написал: Попробую поднять данную тему вновь. Вобщем всё что выше написано - не работает Библиотека не выгружается!.
Явный вызов FreeLibrary для твоей либы (которую ты загрузил в скрипте, не так ли?) - не работает?
Или когда ты ожидаешь что либа будет отгружена? Автоматически, по завершении/отмене скрипта? Такого нет. Нужно явно выгружать либу. Ну и конечно, если либа выгружена, а ты к ней будешь обращаться - все попадает :)
Вообще-то в исходниках lua есть вызов FreeLibrary. Вроде бы gc должен ее вызывать, когда библиотека больше не используется (как в начале предлагали package.loaded[] = nil)
К тому же, я незнаю как работает вся эту луашная магия:
Цитата
function unrequire(m) package.loaded[m] = nil _G[m] = nil end
Я вижу тут зануление таблиц. Приводит ли это к выгрузке модулей - неизвестно.
Для этого не надо знать лушную магию . Достаточно знать Сишную и назначение функции DllMain. ---------------------------------- Но, боже упаси Вас, подумать что я Вас заставляю что-то изучать или проводить эксперименты. Не утруждайте себя. Пишите на форум жалобы. Время потратите столько-же, но зато не будете себя утруждать.
Alexander написал: Попробую поднять данную тему вновь. Вобщем всё что выше написано - не работает Библиотека не выгружается!. Библиотека на C++. Выше писали, что типа если на чистом C++ то выгружается нормально. Не выгружается. Есть ли ещё какой способ выгрузить свою же DLL, которую Lua выгружать почему-то не хочет. Странное вообще какое-то поведение Lua. Скрипт отработал, библиотека - осталась. Функций в Lua для выгрузки не нашёл, что тоже довольно таки странно. Загружать значит можно, выгружать нет. Приходится постоянно перезагружать QUIK, чтобы удалить файл DLL, поскольку система не даёт его удалить, пока библиотека используется Lua. А удалять файл надо, чтобы после перекомпиляции DLL, новый DLL-файл записать на место того, что надо удалить. Это сильно напрягает поскольку сам QUIK грузится не быстро. Сталкивался кто с таким? Как ещё можно решить пролему? Ведь библиотека же чисто моя, зачем она вообще удерживается Lua - не понятно. Закончился скрипт без ошибок - освободи, но нет. Уж не знаю как решить, думаю уже как-то искать ID библиотеки, ну или там хэндл какой и выгружать отдельно программой через API винды.
А причем здесь скрипт? Скрипт - это даже не программа, а все лишь байткод для луа машины. Вспомните, когда dll выгружается.
Ну в случае квика загрузка происходит явно через LoadLibrary. Поэтому могли бы и предусмотреть и аналог FreeLibrary. После его вызова там конечно как система соизволит, так и выгрузится. Библиотека же твоя - захотел загрузил, захотел - выгрузил.
Alexander написал: Попробую поднять данную тему вновь. Вобщем всё что выше написано - не работает Библиотека не выгружается!.
Явный вызов FreeLibrary для твоей либы (которую ты загрузил в скрипте, не так ли?) - не работает?
Или когда ты ожидаешь что либа будет отгружена? Автоматически, по завершении/отмене скрипта? Такого нет. Нужно явно выгружать либу. Ну и конечно, если либа выгружена, а ты к ней будешь обращаться - все попадает :)
Как же я могу сделать явный вызов FreeLibrary для своей библиотеки из своего скрипта, который её загрузил? Я могу вызвать функцию из скрипта свою из этой своей библиотеки и там сделать вызов FreeLibrary этой библиотеки, но что это будет за абсурд? Из тела библиотеки вызывать выгрузку самой этой библиотеки? Библиотека в своём коде будет пытаться попросить систему выгрузить саму себя?
К тому же, я незнаю как работает вся эту луашная магия:
Цитата
function unrequire(m) package.loaded[m] = nil _G[m] = nil end
Я вижу тут зануление таблиц. Приводит ли это к выгрузке модулей - неизвестно.
Я это пробовал - никакого толку. Выгрузка не происходит. И способ действительно извращённый. Внаглую занулить таблицу. И что там когда какой сборщик вычистит - не понятно. Вызова самого сборщика я тут не вижу.
Nikolay Pavlov написал: И еще один момент, при завершении скрипта модули автоматически выгружаются.
Я вот за это зацепился. Раз человек такое писал, значит выгрузка автоматом была. На данный момент моё видение по этому вопросу следующее. При отладке я вижу, что после загрузки моей библиотеки помимо того, что я запускаю свои отдельные потоки, которые присоединяются к этой DLL, так же время от времени к моей библиотеки присоединяются потоки самого квика, которые он запускает. Я вижу как при завершении потока происходит его отсоединение от моей DLL, и как отсоединяются от неё потоки квика, которые ранее к ней присоединились. Но проблема в том, что свои потоки я могу завершить, а вот потоки квика я не контролирую. В любой момент времени постоянно есть какой-то из потоков квика, который присоединён к моей библиотеке. Это делает сама система. Таковы её правила видимо - все вновь созданные потоки из пространства процесса присоединять к DLL, работающей в пространстве этого процесса. Они постоянно подключаются, отключаются, я это контролирую по их ID, но всегда есть потоки присоединённые и никак не возникает ситуация когда все потоки отсоединились. Вот думаю можно ли програмно в самой DLL как-то не разрешать "чужим" подключаться к ней или как-то принудительно их отсоединять. Возможно, когда писалось это, то в тот момент не было ни одного потока присоединённого к DLL и по завершению скрипта квик выгружал DLL, но то было раньше и квик был другой версии и потоки были там по другому организованы чем сейчас. Вот если добиться отсоединения всех потоков, то возможно квик и выгрузит DLL. Но я же не могу запретить квику запускать потоки. А может это и не квика потоки, а Lua, - не понятно. И поэтому может даже сторонняя выгрузка явно не поможет пока есть хотя бы один поток, присоединённый к DLL. Может можно как-то права доступа организовать к воей DLL только своим потокам, но так глубоко я не вникал, да и не хотелось бы, но видимо придётся. Поэтому пока будем думать дальше, уж больно напрягает это всё - перезагрузки для каждой повторной компиляции.
Сегодня сделал несколько попыток выгрузить мою DLL загруженную моим скриптом. 1) Написал отдельную программу(скомпилял с hModule = const, - взял из отладочного вывода, где DLL выводит свой hModule при загрузке), которая делает FreeLibrary DLL, результат не успешный, GetlastError = 126 (Не удалось найти указанный модуль), из другого пространства процесса видимо не катит. 2) Написал отдельную DLL для выгрузки DLL, в которой скрипт вызывает так же FreeLibrary(hModule скрипт получает вызовом функции из удаляемой DLL и передаёт его DLL, которая должна удалить удаляемую DLL), - результат не успешный, GetlastError = 126 (Не удалось найти указанный модуль). Тут DLL вроде в пространстве того же процесса, но видимо нельзя удалить не свой модуль. 3) Вызов скриптом функции, которая в самой DLL и она вызывает FreeLibrary(т.е. по сути DLL выгружает сама себя), результат выполнения функции как ни странно - Успешное выполнение( ! = 0), но удалить фай DLL из каталога так и не удаётся, только после выхода из QUIK. 4) То же самое, что и п.3, плюс вызов UnmapViewOfFile, результат - QUIK вылетает(слышно как шуршит hdd, идёт полная выгрузка), но вылет понятен - после UnmapViewOfFile по сути управление команде после UnmapViewOfFile уже не передать - кода в памяти уже нет. 5) То же самое, что и п.3, но вызов UnmapViewOfFile вызовом функции из другой DLL(hModule скрипт получает вызовом функции из удаляемой DLL и передаёт его DLL, которая должна удалить удаляемую DLL), - результат GetlastError = 487 (Попытка обращения к неверному адресу). 7) То же самое, что и п.3, плюс collectgarbage(), результат такой же как у п.3 8) То же самое, что и п.3, плюс: package.loaded["luacdllopt"] = nil _G["luacdllopt"] = nil , результат такой же как у п.3 9) То же самое, что и п.8, плюс collectgarbage(), результат такой же как у п.3
На этом сделал вывод, что нужен только либо явный вызов FreeLibrary оттуда, откуда был LoadLibrary, а этого нет, либо UnmapViewOfFile нужна оттуда же, чего тоже не можем иметь(хотя если бы и имели, то просто воспользовались бы FreeLibrary). Либо должен быть какой-то способ заставить Lua всё нормально почистить - ведь п.3 имел таки успешный результат, поэтому я полагаю, что выгрузка произошла, но отвязка файла не сделана, что и не даёт нам его удалить. Есть у кого ещё какие мысли что ещё можно попробовать? Должен же быть какой-то выход - скрипт загрузил, скрипт отработал, по сути DLL никому не нужна. Решение должно быть.
PS. Пока писал ещё мысль такая пришла - пишем одну DLL, загружаем её в скрипте, эта DLL ничего не делает кроме как через LoadLibrary грузит основную DLL(раз сама загрузила, то сама должна и выгрузить), в которой все нужные нам функции. Но как вызывать функции из основной потом? Если найдётся решение, то может и получится потом выгружать основную. Главное выгружать основную, так как её постоянно надо перекомпилировать и менять файл на новый, а первая путь себе и не удаляется. Наспех что-то на ум ничего не идёт как вызывать, надо собраться с мыслями.
PSS. А не вроде можно вызывать. В первой DLL пишем ещё одну функцию через которую будем вызывать любую функцию из первой DLL, блин ну как это заморочено всё получается. Но придётся видимо и так попробовать.
PSSS. Или после загрузки основной DLL, адреса всех функций возвращать в скрипт и потом использовать адреса для вызова, но тоже заморочено. Лучше бы конечно сам Lua заставить выполнить свою работу.
Alexander, Добрый день, Объясняю как это сделать. ---------------------- 1) Вам надо написать отдельную dll, в которой одна функция - выгрузить dll по ее имени ------------------------- 2) в этой функции вам надо найти хендл выгружаемой dll и выгрузить ее столько раз, сколько ее вызывали до этого. --------------------- 3) При сборке dll, которую Вы будете в дальнейшем выгружать, вам надо отключить оповещение о вызове этой dll ---------------- И будет вам счастье.
nikolz написал: Alexander , Добрый день, Объясняю как это сделать. ---------------------- 1) Вам надо написать отдельную dll, в которой одна функция - выгрузить dll по ее имени ------------------------- 2) в этой функции вам надо найти хендл выгружаемой dll и выгрузить ее столько раз, сколько ее вызывали до этого. --------------------- 3) При сборке dll, которую Вы будете в дальнейшем выгружать, вам надо отключить оповещение о вызове этой dll ---------------- И будет вам счастье.
Постом выше я описал, что уже было предпринято. В п.2 я как раз делал тоже самое. Отдельная DLL написана, там одна функция на выгрузку. Хэндл DLL есть - получаю запросом из первой. Не выгружается. По поводу - выгрузить столько раз, сколько её вызывали до этого. Библиотека загружается один раз и висит не выгружается. Наверное имелось ввиду столько раз сколько запускался скрипт, который её грузит в начале через require? По п.3 - как отключить оповещение о вызове этой DLL? И ещё. Вспомнил, что ранее как только первый раз написал свою DLL для QUIK, она ВРОДЕ как сама выгружалась после отработки скрипта. Но потом выгрузок не было, когда писал DLL, где запускались потоки. Точно ответить не мог точно выгружалась или нет. Но вот когда писал DLL, которая выгружает основную DLL, то при завершении скрипта, я мог удалить файл этой DLL, а вот удалить файл основной DLL, которую надо удалить так и не получалось. Сделал выводы, что какие-то ресурсы не освобожены, поэтому DLL и висит. Но я уже всё предусмотрел, выгружаю и закрываю всё что открывал и использовал. У меня запускаются потоки, они выделяют память, потоки перед завершением память освобождают, сами завершаются, так же использовал TLS, тоже освобождаю, дескрипторы потоков закрываю. Инициализацию делал критической секции, - удаляется. Что ещё может быть не знаю. Короче что использовал, то освободил или закрыл. Что мешает Lua выгрузить не пойму пока.
Добавлю ещё. Попробовал такой вариант. Написана ещё одна DLL для загрузки основной. Скрипт через require грузит её, та в DLL_PROCESS_ATTACH через LoadLibrary загружает основную DLL. Есть функция в DLL, которая загрузила основную, которая возвращает мне адрес любой функции из основной DLL по имени функции, этот адрес она передаёт через lua_pushcfunction, а скрипт сохраняет в переменную и потом я через эту переменную вызываю функцию из основной DLL. Всё работает. Проверено. Точно так же как если бы я вызывал функции напрямую из первой. Далее вызываю из скрипта функцию, которая выгружает основную через FreeLibrary. Вот тут самое интересное. Выгрузка происходит нормально, ошибок нет, НО... результат такой же как в п.3, что пару постов выше - файл DLL так и не удалить!!! Удалить можно только после выхода из QUIK-а. Получается, что так, что выгружать DLL из самой себя - результат одинаковый - положительный, но на самом деле нихрена не положительный раз файл удалить нельзя.
попробуйте в dll ,которую надо выгрузить в функции WINAPI DllMain(HINSTANCE hinstDLL ... после case DLL_PROCESS_ATTACH: вставить DisableThreadLibraryCalls(hinstDLL);
Такой прокси вызов функций из основной DLL, как описал выше так же можно организовать и по другому, т.е. не возвращая адрес нужной функции в скрипт с последующим вызовом функции через переменную, получившую адрес этой функции. А можно вызывать функции как обычно через имя модуля.имя функции, но эти функции надо сделать прокси-функциями, т.е они должны принимать все нужные параметры для вызова, а потом через переменную указатель вызывать нужную функцию из основной DLL, передавая ей эти параметры обычной функции C. Но это просто к слову. Так не стал, хотя разницы нет. Всё равно же результат никакой.
Alexander написал: Такой прокси вызов функций из основной DLL, как описал выше так же можно организовать и по другому, т.е. не возвращая адрес нужной функции в скрипт с последующим вызовом функции через переменную, получившую адрес этой функции. А можно вызывать функции как обычно через имя модуля.имя функции, но эти функции надо сделать прокси-функциями, т.е они должны принимать все нужные параметры для вызова, а потом через переменную указатель вызывать нужную функцию из основной DLL, передавая ей эти параметры обычной функции C. Но это просто к слову. Так не стал, хотя разницы нет. Всё равно же результат никакой.
Я Вроде вам написал, что сделать. Вы сделали? Какой результат? Если не выгрузилась, то скажу что делать дальше.
nikolz написал: попробуйте в dll ,которую надо выгрузить в функции WINAPI DllMain(HINSTANCE hinstDLL ... после case DLL_PROCESS_ATTACH: вставить DisableThreadLibraryCalls(hinstDLL);G
Посмотрел, что это за функция. Это мне не пойдёт, т.к. у меня в DLL_THREAD_DETACH происходит удаление дескрипторов потоков из массива потоков, которые закрылись. Если сделаю её вызов, то DLL_THREAD_DETACH мне поступать не будут.
Кстати, а есть ли такая функция, которая бы запрещала бы вообще присоединяться к моей библиотеке другим потокам? А то кроме потоков, которые я сам запускаю к ней присоединяются все кому не поподя - какие-то потоки квика или луа, которые мне не нужны. Это было бы полезно для меня, хотя проблему не решает, просто чужие потоки забивают мне отладочный вывод. Можно конечно каждый раз сравнивать свой-чужой, но это лишний код.
Alexander написал: Такой прокси вызов функций из основной DLL, как описал выше так же можно организовать и по другому, т.е. не возвращая адрес нужной функции в скрипт с последующим вызовом функции через переменную, получившую адрес этой функции. А можно вызывать функции как обычно через имя модуля.имя функции, но эти функции надо сделать прокси-функциями, т.е они должны принимать все нужные параметры для вызова, а потом через переменную указатель вызывать нужную функцию из основной DLL, передавая ей эти параметры обычной функции C. Но это просто к слову. Так не стал, хотя разницы нет. Всё равно же результат никакой.
Я Вроде вам написал, что сделать. Вы сделали? Какой результат? Если не выгрузилась, то скажу что делать дальше.
Рад бы вобщем такое попробовать, но не могу, мне нужен DLL_THREAD_DETACH. Ещё есть подозрение, что вызов DisableThreadLibraryCalls cделает так, что DLL_THREAD_ATTACH и DLL_THREAD_DETACH просто не будут приходить в DLL, но сами потоки будут и присоединяться и отсоединяться, просто уведомлений не будет.
Я сейчас попробую сделать вызов DisableThreadLibraryCalls не в в функции WINAPI DllMain(HINSTANCE hinstDLL ... после case DLL_PROCESS_ATTACH: как Вы писали, а в другом месте - в вызове функции, которая закрывает все работающие потоки если таковые есть.
nikolz написал: Alexander , У меня все это работает. Но если Вы будете сами выдумавать а не делать ровно то что я написал, то не вижу смысла Вам помогать.
Хорошо, сейчас попробую в точности. Может я не правильно понял как эта функция работает.
nikolz, попробовал вставил вызов DisableThreadLibraryCalls после case DLL_PROCESS_ATTACH: как Вы писали - результат отрицательный. Потоки перестали подключаться и отключаться к моей DLL, ну вернее перестали приходить сообщения из DLL_THREAD_ATTACH и DLL_THREAD_DETACH, что я оттуда делал в debug, ну и деактивировался мой код в DLL_THREAD_DETACH, который закрывал дескрипторы завершённых потоков. Файл библиотеки удалить не могу.