Инструменты пользователя

Инструменты сайта


atf:dll

Вызов функций DLL (ATF 1.14)

Общее описание

Работа с внешними DLL происходит через объект-обертку dll_wrapper. После создания объекта необходимо вызвать метод load с именем модуля DLL, который необходимо загрузить. В системе одновременно может быть загружен лишь один экземпляр любой DLL. Если загрузить DLL повторно, то будет использоваться библиотека загруженная в прошлый раз. Таким образом, можно загружать DLL в каждом из скриптов, и все они будут разделять доступ к одной DLL.

После того, как DLL загружена, ее функции вызываются с помощью вызова call(funcname, format, args). Он имеет следующие аргументы:

  • funcname - имя функции
  • format - описание аргументов
  • args - сами аргументы

Пример использования:

static dll;
 
function init() {
  dll = new_object("dll_wrapper");
  dll.load("test.dll");
}
 
function calc() {
  line[0] = dll.call("multiplication", "i8: i8, i8", 2, 3);
}

Предположим, что содержимое файла test.cpp следующее:

// Пример кода на C++
__declspec(dllexport) int multiplication(int a, int b)
{
	return a * b;
}

И функция multiplication экспортируется.

Тогда линия line[0] будет иметь всюду значение 6.

Формат

Формат вызова функции (атрибут format) имеет следующий вид:

«соглашение_вызова возвращаемый_тип: аргумент1, аргумент2, …»

Соглашение вызова чаще всего не указывается и используется только для dll скомпилированных компиляторами Borland либо для соглашения вызова cdecl. Для компиляторов Borland требуется поставить префикс b, а для cdecl нижнее подчеркивание _ (при наличии обоих параметров они должны идти именно в указанном порядке). Пример:

dll_1.call("borland_function", "bi4: i4, i4");
dll_2.call("cdecl_function", "_i4: i4, i4");
dll_3.call("borland_cdecl_function", "b_i4: i4, i4");
dll_4.call("microsoft_stdcall_function", "i4: i4, i4");

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

  • i4 - 4-байтовое целое
  • i8 - 8-байтовое целое
  • f4 - 4-байтовое с плавающей точкой
  • f8 - 8-байтовое с плавающей точкой
  • s - строка 1-байтовых символов в кодировке Windows-1251
  • p - указатель (доступно начиная с ATF 1.16, подробнее смотрите ниже раздел raw_data)

На данный момент ATF не поддерживает указатели, а так же имеет сложную структуру внутренних типов и памяти, поэтому передать что-то по указателю на данный момент невозможно, это будет следующий этап развития объекта dll_wrapper (на данный момент он имеет больше тестовый вид).

Если функция не возвращает никакого значения, просто ничего не указывайте перед символом : (кроме соглашений вызова, если они требуются):

// Функция принимает строку в качестве
// аргумента и ничего не возвращает
dll.call("f1", ":s");
 
// Функция не возвращающая значение,
// с соглашением вызова cdecl
dll.call("f2", "_:i4, i4")

Если функция не возвращает значения и не принимает аргументов, то формат и аргументы не указываются вообще:

dll.call("hello");

raw_data

ATF не имеет встроенного типа «указатель», а так же любые его данные не являются типизированными. Таким образом, например, вызвать следующую функцию С++ не удастся стандартными методами:

void f(int *p)
{
  // некоторый код, пишущий по адресу p
}

По этой же причине из ATF не удастся напрямую передать данные из массива в функцию dll.

Для того, чтобы решить эту проблему, в ATF 1.16 был введен объект нового типа raw_data, который имеет низкоуровневое внутреннее представление, совместимое со стандартными машинными типами. На данный момент типы данных поддерживаются следующие:

  • char - 1-байтный символ
  • wchar - 2-байтный символ
  • int2 - 2-байтное целое
  • int4 - 4-байтное целое
  • int8 - 8-байтное целое
  • float - число с плавающей точкой одинарной точности (4 байта)
  • double - число с плавающей точкой двойной точности (8 байт)

Объект типа raw_data поддерживает три функции:

  • raw_data.define(type, number)

Задает внутреннее представление объекта. В качестве типа указывается строка содержащее одно их значений выше. В качестве number указывается для скольки именно экземпляров такого типа необходимо выделить память. Например, если вы ожидаете, что функция dll fn запишет по передаваемому ей указателю массив из 100 чисел двойной точности, вы должны предварительно инициализировать объект raw_data следующим образом:

var data = new_object("raw_data");
data.define("double", 100);
dll.call("fn", ":p", data);
  • raw_data.get()

Функция get() возвращает либо массив данных, содержащихся в raw_data, либо одно значение, если оно было выделено лишь одно. Таким образом можно получить данные, которые были записаны функцией dll по указателю. Пример выше можно дополнить, например, следующим образом:

var data = new_object("raw_data");
data.define("double", 100);
dll.call("fn", ":p", data);
var result = data.get();
var i = 0;
while (i < 100) {
  line[0][i] = result[i];
  i += 1;
}
  • raw_data.set(type, data)

Функция set() инициализирует объект данными из переменной data. Если data - скалярная переменная, то в raw_data будет выделена память под один элемент. Если data - массив, то будет выделено памяти ровно под количество элементов, содержащихся в массиве. Используется функция set() аналогично

var data = new_object("raw_data");
var array = new_object("array");
fillArraySomehow(array);
data.set("int4", array);
dll.call("fn", ":p", data);

Синхронизация

Скрипты выполняются последовательно, так что проблем с синхронизацией функций DLL возникать не должно. В тоже время обработка событий окружения или таймера может исполняться в отдельном потоке, поэтому можно быть уверенным, что никакая функция DLL не будет вызвана одновременно из разных потоков Transaq, однако нельзя закладываться на последовательность вызова функций:

function onEnvEvent(var string)
{
  dll.call("onEnvEventFunc");
}
 
function onNewCandle()
{
  dll.call("onNewCandleFunc1")
  dll.call("onNewCandleFunc2");
}

В этом примере последовательность вызовов функций DLL может быть следующей: onNewCandleFunc1, onEnvEventFunc, onNewCandleFunc2.

Функции

  • new_object("dll_wrapper") - создание обертки для доступа к dll
  • new_object("raw_data") - обертка для низкоуровневых данных, передаваемых по указателю
  • dll_wrapper.call(funcname, format, args…) - вызвать функцию dll funcname с аргуметами, описынными в строке format.
  • dll_wrapper.load(name) - загрузить dll с заданным именем
  • raw_data.define(type, number) - инициализировать number значений типа type
  • raw_data.get() - получить значение, содержащееся в raw_data (массив или скаляр)
  • raw_data.set(type, data) - инициализировать raw_data элементами массива data, проинтерпретированными как тип type
atf/dll.txt · Последние изменения: 2012/06/15 21:07 (внешнее изменение)