Для этого нужна обычная библиотека DLL, подключаемая к QLua, о том, как ее создать можете посмотреть здесь.
Пусть созданная Вами DLL называется "LuaCallback.dll", которая находится в корневом каталоге терминала QUIK.
Следующий пример 10 раз, с периодичностью в 1 секунду выведет сообщение с текстом "Привет из DLL" посредством вызова из DLL функции из Qlua скрипта MyLuaCallback():
Код скрипта QLua
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | require("LuaCallback"); -- Подключает библиотеку "LuaCallback.dll" Run = true; -- Флаг поддержания работы скрипта -- Основная функция скрипта, пока работает она, работает скрипт function main() LuaCallback.StartCallback(); -- Запускает механизм вызова функции MyLuaCallback из DLL while Run do sleep(1000); end; -- Поддерживает работу скрипта end; -- Функция, вызываемая из DLL function MyLuaCallback(Text) message(Text); -- Выводит сообщение, переданное в функцию из DLL end; -- Функция завершения работы скрипта function OnStop() Run = false; -- Завершает цикл while, следовательно завершается main() и сам скрипт останавливается end; |
Код DLL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | #include <Windows.h> //Подключает все необходимое #include <thread> //Подключает возможность работы с потоками //=== Необходимые для Lua константы ============================================================================// #define LUA_LIB #define LUA_BUILD_AS_DLL //=== Заголовочные файлы LUA ===================================================================================// extern "C" { #include "Lua\lauxlib.h" #include "Lua\lua.h" } //=== Стандартная точка входа для DLL ==========================================================================// BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { return TRUE; } //Функция вызывает функцию из QLua с периодичностью в 1 секунду static int MyCallback(lua_State *L) { lua_getglobal(L, "MyLuaCallback"); //Находит в стеке Lua функцию по ее названию и помещает ее наверх стека int callbackRef = luaL_ref(L, LUA_REGISTRYINDEX); //Получает индекс функции в специальной внутренней таблице(реестре) Lua for (int i = 0; i < 10;i++) { lua_rawgeti(L, LUA_REGISTRYINDEX, callbackRef); //Достает функцию из реестра Lua по индексу и помещает в стек lua_pushstring(L, "Привет из DLL"); //Добавляет в стек параметр, который будет передан функции lua_call(L, 1, 1); //Вызывает выбранную функцию в скрипте QLua, передавая в нее 1 параметр (L - стек, 1 - количество передаваемых параметров, 1 - количество возвращаемых значений(нужно, чтобы обновлять стек)) Sleep(1000); //Пауза в 1 секунду } return 0; //Выход из функции } //=== Реализация функций, вызываемых из LUA ====================================================================// static int forLua_StartCallback(lua_State *L) { std::thread thr(MyCallback, L); //Запускает выполнение функции MyCallback() в отдельном потоке thr.detach(); //Отсоединяет созданный поток от основного, делая его "фоновым" return (0); //Завершает работу функции forLua_StartCallback, при этом функция MyCallback продолжает работать в отдельном потоке } //=== Регистрация реализованных в dll функций, чтобы они стали "видимы" для Lua ================================// static struct luaL_reg ls_lib[] = { { "StartCallback", forLua_StartCallback } }; //=== Регистрация названия библиотеки, видимого в скрипте Lua ==================================================// extern "C" LUALIB_API int luaopen_LuaCallback(lua_State *L) { luaL_openlib(L, "LuaCallback", ls_lib, 0); return 0; } |
Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!
На Lua 5,4 - ничего из вышеописанного не работает. Lua_pcall - Всегда завершается ошибкой - "error in error handling"
Используйте Lua 5.3 и решение отсюда (красным цветом) https://quikluacsharp.ru/qlua-c-cpp-csharp/konnektor-dll-quik-qlua-lua-c/
Здравствуйте.
Не могу вызвать LUA функции из DLL
Есть такой код:
В LUA просто вызов этой функции TEST()
При выполнении в LUA 5.3.5 все работает, появляется сообщение, где прописан путь.
При выполнении в LUA 5.4.1 терминал QUIK закрывается.
Естественно собираю две разные DLL, под LUA 5.3 и под LUA 5.4.
Для сборки DLL под 5.3 использую lua-5.3.5_Win64_vc16_lib
Для сборки DLL под 5.4 использую lua-5.4.2_Win64_vc16_lib
Падение происходит при вызове функции lua_call(L, 0, 1); Если ее убрать, ну и MessageBox тоже убрать, то терминал не падает, конечно ничего и не делает, это понятно, но не падает.
На функцию getWorkingFolder не смотрите, была взята для примера, для упрощения, так как не имеет параметров. Другие так же не могу вызвать.
Есть варианты, в чем может быть дело?
Никогда не пользуйтесь этим решением, так делать нельзя. LUA-машина не threadsafe.
Стандартная Lua-машина - да
Но в QUIK вроде как должна быть threadsafe же?
Это пожалуй маленькая революция получилась!!! Спасибо. Надо будет обдумать новые возможности. В своё время смотрел в данную сторону, но так как используется конструкция GLOBALSINDEX, которая вроде упразднилась в Lua 5.2 и выше. Решил данное направление не развивать, а вдруг quik перейдет на другую версию Lua... Если кто знает больше , поправьте меня 🙂 Ещё раз Спасибо. Прояснили окончательно технологию.
Согласен с Вами по поводу революции 🙂
Вроде бы нашел решение:
вместо lua_setfield(L, LUA_GLOBALSINDEX, "OnAllTrade");
использовать: lua_setglobal(L, "OnAllTrade");
говорят это должно и на 5.1 и на 5.2 работать, на 5.2 не проверял, но на 5.1 работает 🙂
Хорошее решение! Спасибо.
Дмитрий, огромное спасибо, очень помогли!! 🙂 Буду пользовать.
Всегда пожалуйста!) И Вам спасибо!)
Дмитрий, извините за навязчивость, Вы сможете показать рабочий код для DLL чтобы там отловить OnQuote(class, sec) или OnTrade(trade) ?
Буду признателен, если так же подскажете как там получить данные из переменных class, sec и trade?
Я вчера начал писать, но не успел закончить, проверил пример с main, все работает, как ожидалось, почти дописал вчера пример для OnAllTrade, но ночью сервера не работают, сегодня надеюсь сделаю и сразу отпишусь по результатам.
Спасибо, буду ждать! 🙂
Реализовал OnAllTrade, если main и OnStop еще впихнуть в DLL, то вообще скрипт из одного require будет 🙂
Код Lua
Код DLL
Здравствуйте!
Подскажите, пожалуйста:
1. к встроенным функциям QLua обращаться по такому же принципу, как Вы описали выше? Как передавать в них больше 1 параметра и как получать возвращаемые значения?
2. как в DLL обращаться к функциям обратного вызова? При этом не указывать её (функцию обратного вызова) в коде Lua.
Простите за задержку, очень занят, отвечу Вам чуть позже!
А зачем Вам из DLL обращаться к встроенным функциям QLua? Они же созданы для того, чтобы терминал их вызывал когда происходит то, или иное событие!
Дмитрий, чтобы понять механизм как QScalp, ATAS и StockSharp так делают, что у них в скрипте Lua, только require("...") и настройки. А вообще хочу прописать все функции в DLL на C++, а потом програмить только на C# не думая больше о Lua.
Понимаю Вас, у меня тоже есть такая задача. Но недавние мои изыскания не дали результата, хотя не сказать, что я очень усердно пытался найти решение. Здесь задача не вызвать функцию обратного вызова из DLL, а отловить в DLL вызов этой функции терминалом и я на данный момент не представляю как это сделать, хотя, как это обычно бывает, решение, возможно, очень простым окажется)
Вот тут, в самом низу человеку удалось сделать это с main: http://quik2dde.ru/viewtopic.php?id=143
Он так сделал:
Но как сделать это например с OnQuote()? Тут, косвенно подтверждается что сделать можно: http://quik2dde.ru/viewtopic.php?id=90
Эти ссылки могут как то помочь? Куда "копать"?
Дак это готовое решение, копать уже ничего не нужно!!!)
Спасибо Вам!
Я честно говоря не врубился как 🙂 Напишите, пожалуйста рабочий код для DLL чтобы там отловить OnQuote(class, sec) или OnTrade(trade) . И как там получить данные из переменных class, sec и trade?
Здравствуйте.
Написал Вам в воскресный день письмо по безопасности и сделал пару (по незнанию интерфейса сайса) заявок на обратный звонок, пишу на всякий случай, не знаю где прочитаете/откликнитесь первым.
Теперь, -- "по теме",
а вопрос мой продиктован следующим:
Спасибо за идеи/коды/контент, уже и не помню, откуда брал примеры реализации функций LUA на нативной стороне, но меня ГЛОЖЕТ другой вопрос:
Как (есть ли пример) "безболезненно" ПОТОМ отключать "это дело" от машины LUA.
Т.е. корректно выгрузить и библиотеку и скрипт.
Сейчас, (уже несколько лет) ничего лучше не придумал, чем использовать другую "шлюзовую" dll, через которую подключаю, ту, которую постоянно переделываю.
Наконец добился нужного результата (библиотеки при этом всё-таки также две), но не БЕЗ WIN API . . .
Вопрос:
Как выглядит решение, которое позволяет:
1. без перезагрузки QUIK;
2. не останавливая скрипт в интерфейсе QUIK, а закрывая окно dll;
3. корректно выгрузить и библиотеку (dll) и скрипт;
4. чтобы после изменения кода и перекомпиляции dll снова запустить скрипт.
"корректно выгрузить" означает, что QUIK при соответствующей процедуре завершения dll "НЕ стреляется", dll из адресного пространства QUIK действительно выгружена, что позволяет ее замену в папке QUIK без его перезагрузки.
Здравствуйте, не понял проблему. Когда Вы останавливаете скрипт с подключенной DLL, она и так выгружается сама, пересобрали DLL, запустили скрипт, и он уже новую версию DLL подгрузил. Если Вы хотите какое-то окно закрывать и чтобы при этом скрипт останавливался автоматически, то вызывайте функцию OnStop(). Или я что-то не так понял?
Правильно, но я хочу "разобраться в деталях" . . .
чтобы, например, так:
. . . я закрываю окно dll . . . , -- и скрипт останавливается.
Неужели у коннектора LUA надо обязательно именно останавливать скрипт?
И про безопасность сайта, . . . как написать модератору Лично ("в личку")?
по сайту напишите мне на эту почту, я сам его делал reply@quikluacsharp.ru, а по поводу окна dll не понял, dll это библиотека функций, у Вас в библиотеке окно создается?
если вы запустите свой скрипт из статьи и завершите скрипт до того, как полностью отработает ваш тред внутри dll, то все упадет со страшным грохотом.
Здесь да, правда ваша. Это одна из проблем как корректно дождаться завершения всех дочерних тредов. Но ведь можно, вынеся вызов спец. метода в OnStop (и еще куда-то) 🙂
Я когда придумывал свою библиотеку доп. тредов в QLua - тоже задумывался как дожидаться завершения дочерних тредов.
Сейчас проверю
Подскажите, вот я прочитал, что "Lua вызывающий C/C ++ должен скомпилировать C/C ++ в динамическую библиотеку."
У меня такой вопрос - правильно ли я понимаю, что весь код реализации того, что я хочу сделать с тем, что вызвано из Lua - я должен реализовать именно в DLL и никаким образом то, что вызвано из Lua не получится получить именно скажем так в стандартный проект С/С++ - тот который с точкой входа в int main ?
И тот же вопрос про вызовы Lua функций из С/C++, из самого проекта main() - такое нельзя будет реализовать ?