Взаимодействие Lua и библиотеки DLL, написанной на C/C++

Автор записи: Дмитрий (Admin)
1 звезда2 звезды3 звезды4 звезды5 звезд (Голосов 6, среднее: 5,00 из 5)
Загрузка...

Qlua-csharp-connector-dll
Взаимодействие происходит по стандартному алгоритму: в скрипте QLua(Lua) вызываются функции из подключенной при помощи директивы "require" библиотеки DLL, написанной на языке C/C++ (о том как ее создать, можно прочитать в данной статье). В функцию можно передавать данные, в виде параметров. Обрабатывать их внутри функции и получать в скрипт результат(ы) выполнения функции.

Для этого служит Lua-стек, который представляет из себя массив разнотипных данных (таблицу Lua). Первое значение, помещенное в стек, получает индекс 1, второе - 2 и т.д.

Для каждого отдельного вызова функции автоматически используется отдельный стек!

Получить результаты можно обычным способом, и, если их несколько, используя параллельное присваивание.
Пример, если бы функция из приведенного примера DLL возвращала 4 значения:

require("QluaCSharpConnector");
-- Получение значений в QLua
a,b,c,d = QluaCSharpConnector.TestFunc();
 
-- Передать значения в функцию на C/C++ можно обычным способом
QluaCSharpConnector.TestFunc(true, 1, 2.5, "Текст");

Для того, чтобы получить значения внутри функции DLL, переданные из скрипта, в Lua существует несколько функций, приведу основные:

lua_gettop(L); //Вернет индекс последнего элемента в стеке. Т.к. индексы начинаются с 1, то это значение, так же, означает количество элементов в стеке. По-умолчанию, максимально возможный индекс установлен в 8000, но может быть изменен в файле luaconf.h
 
lua_settop(L, i); //Меняет размер стека, устанавливая количество элементов равным i, если i больше, чем на данный момент элементов в стеке, то функция добавляет в стек недостающие элементы, устанавливая их значения как nil. Если i меньше, чем на данный момент элементов в стеке, то функция удаляет из стека лишние элементы. Если передать i равное 0, то функция очистит стек. 
 
lua_toboolean(L, i); //Возьмет из стека значение по индексу i (индексы, как и всегда в Lua, начинаются с 1) и преобразует его к типу boolean, вернет 1 для всех значений, кроме false и nil, иначе вернет 0. Так же, вернет 0, если указан несуществующий индекс.
   lua_isboolean(L, i); //Проверяет, является-ли значение в стеке по данному индексу типом boolean.
 
lua_tointeger(L, i); //Возьмет из стека значение по индексу i, и попытается преобразовать его к типу int. Вернет полученное число, либо 0. Результат преобразования не целого числа непредсказуем.
 
lua_tonumber(L, i); //Возьмет из стека значение по индексу i, и попытается преобразовать его к типу double. Вернет полученное число, либо 0.
 
   lua_isnumber(L, i); //Проверяет, является-ли значение в стеке по данному индексу числом.
 
lua_tostring(L, i); //Возьмет из стека значение по индексу i, и попытается преобразовать его к типу string. Вернет полученную строку, либо NULL, если элемент стека не являлся ни строкой, ни числом. Значение в стеке, так же, изменится на тип string!!!
   lua_isstring(L, i); //Проверяет, является-ли значение в стеке по данному индексу типом string.

Пример получения значений внутри функции C/C++ с предварительной проверкой на тип, переданных в примере выше:

static int forLua_TestFunc(lua_State *L)
{
	boolean B;
	int I;
	double D;
	const char *S;
	// Получает из стека переданные скриптом данные
	if (lua_isboolean(L, 1)) B = lua_toboolean(L, 1);
	if (lua_isnumber(L, 2))  I = lua_tointeger(L, 2);
	if (lua_isnumber(L, 3))  D = lua_tonumber(L, 3);
	if (lua_isstring(L, 4))  S = lua_tostring(L, 4);
 
	return(0);
}

Основные функции для добавления элементов в стек:

lua_pushboolean(L, B); //Добавляет в стек элемент типа boolean
 
lua_pushinteger(L, B); //Добавляет в стек элемент типа int
 
lua_pushnumber(L, B); //Добавляет в стек элемент типа double
 
lua_pushstring(L, B); //Добавляет в стек элемент типа const char*

Пример с добавлением отправки полученных значений обратно в скрипт:

static int forLua_TestFunc(lua_State *L)
{
	boolean B;
	int I;
	double D;
	const char *S;
	// Получает из стека переданные скриптом данные
	if (lua_isboolean(L, 1)) B = lua_toboolean(L, 1);
	if (lua_isnumber(L, 2)) I = lua_tointeger(L, 2);
	if (lua_isnumber(L, 3)) D = lua_tonumber(L, 3);
	if (lua_isstring(L, 4)) S = lua_tostring(L, 4);
 
	// Очищает стек Lua
	lua_settop(L, 0);
 
	// Добавляет в стек полученные ранее значения
	lua_pushboolean(L, B);
	lua_pushinteger(L, I);
	lua_pushnumber(L, D);
	lua_pushstring(L, S);
 
	// Возвращает значения в скрипт
	return(1,2,3,4);
}

Скрипт, демонстрирующий работу данной функции:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
require("QluaCSharpConnector");
 
IsRun = true;
 
function main()   
   while IsRun do
      sleep(1000);
   end;   
end;
 
function OnStop()
   -- Отправляет в DLL и получает обратно значения
   b,i,d,s = QluaCSharpConnector.TestFunc(true, 1, 2.5, "Текст");
 
   -- Выводит полученные значения в сообщении
   message(tostring(b).." "..tostring(i).." "..tostring(d).." "..s);
 
   IsRun = false;
end;

Так же, из QLua(Lua) в DLL(C/C++) можно передавать МАССИВЫ (таблицы Lua).
Ниже приведен пример DLL с функцией forLua_SendArray, которая получает из QLua массив чисел, вычисляет сумму значений и отправляет результат в C#:

Код DLL(С/С++), обрабатывающей массив
Если данную DLL назвать QluaCSharpConnector.dll и запустить нижеприведенный скрипт, то в именованной памяти "MyMemory" будет строка "15", которую можно обработать в приложении C#.

1
2
3
4
5
6
require("QluaCSharpConnector");
 
function main()   
   Array = {1,2,3,4,5};
   QluaCSharpConnector.SendArray(Array);   
end;

Разные функции:

lua_getglobal(L, name);     //Ищет глобальную переменную с заданным именем и помещает на вершину стека

Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!

Добавить комментарий

Взаимодействие Lua и библиотеки DLL, написанной на C/C++: 58 комментариев

  1. Подскажите пожалуйста пример, как на C++ получить строку записанную в именованную память, и что немаловажно синхронизировать это.
    Мне необходимо в стороннем приложении на С++ получать данные о сделках которые поступают в функцию OnTrade в Quik LUA.
    DLL для получения и записи данных из OnTrade в именованную память по примеру в данной статье я сделал, а как эти данные получать на C++ и получать именно тогда когда они поступили (синхронно) не знаю 🙁 Помогите пожалуйста. Я без примера не разберусь так как новичок в этом деле.

  2. Кто-нибудь использовал CLR или иные способы вывода форм из DLL? На данный момент пробую использовать CLR, но похоже, что из-за этого DLL после остановке LUA скрипта (где подключен DLL) - не выгружается.