Работа с динамически подключаемыми библиотеками
Макроязык K3 имеет возможность вызова функций из динамически-подключаемых библиотек (dll). Динамически-подключаемая библиотека получает на вход набор исходных данных, обрабатывает из и возвращает в K3 набор выходных величин. В данном разделе описан механизм работы с такими библиотеками.
Запуск функции из динамически-подключаемой библиотеки
INT DllFuncExec(STRING <PathDll>, STRING <NameFunc>, variant ARRAY <In>, variant ARRAY <Out>)
Функция DllFuncExec инициирует исполнение функции с именем <NameFunc> из динамически подключаемой библиотеки с полным именем файла <PathDll>. Массив <In> является массивом входных параметров, массив <Out> – массивом выходных параметров. Значением функции является число, возвращенное из функции динамической библиотеки. Это число может представлять собой количество заполненных элементов в выходном массиве, а может иметь и любое другое значение по выбору создателя функции.
Пример использования:
defarr In[3];
defarr Out[10];
In [1]=1;
In [2]="Hello";
In [3]=123.45;
Res=DllFuncExec("D:\\Geos\\ExtDll.dll","MyXXXFunc",In,Out);
=Res;
6
Если библиотека ExtDll.dll находится в той же папке, что и запускающая ее задача (например, mebel.exe), то можно не указывать в параметре запуска полный путь.
Res=DllFuncExec("ExtDll.dll","MyXXXFunc",In,Out);
| Имя функции в пользовательской dll нужно задавать с учётом регистра. |
Создание динамической библиотеки средствами Microsoft VisualC
Пользователь создаёт динамическую библиотеку стандартными средствами. (DLL не должна быть расширением MFC). В библиотеке описывается прототип экспортируемой функции. Например:
#define CDDCALLINGFORMAT __cdecl #define CDDEXPORT extern "C" __declspec(dllexport) CDDEXPORT int CDDCALLINGFORMAT MyXXXFunc ( const int nIn, const VARIANT* vtIn, const int nOut, VARIANT* vtOut);
Параметры:
- int nIn – число, указывающее размерность входного массива vtIn
- VARIANT* vtIn – указатель на входной массив
- int nOut – число, указывающее размерность выходного массива vtOut, который можно заполнить результатами работы функции.
- VARIANT* vtOut – указатель на выходной массив
Возвращаемое значение:
В данном примере – это число заполненных элементов в выходном массиве.
Затем пишется ее реализация. Например:
CDDEXPORT int CDDCALLINGFORMAT MyXXXFunc(
const int nIn,
const VARIANT* vtIn,
const int nOut,
VARIANT* vtOut)
{
CString sz;
_variant_t vt;
// Получим входной массив
sz.Format("Я получил число входных: %d. А именно:",nIn);
AfxMessageBox(sz);
if(vtIn)
{
for(int i = 0; i < nIn; i++)
{
vt = vtIn[i];
vt.ChangeType(VT_BSTR);
sz = vt.bstrVal;
AfxMessageBox(sz);
}
}
// Заполним массив данными для возврата их в K3
// Для примера разными типами
vtOut[0].vt = VT_UI1;
vtOut[0].bVal = 12;
vtOut[1].vt = VT_I2;
vtOut[1].iVal = 123;
vtOut[2].vt = VT_I4;
vtOut[2].lVal = 1235678;
vtOut[3].vt = VT_R4;
vtOut[3].fltVal = 77.88;
vtOut[4].vt = VT_R8;
vtOut[4].dblVal = 1235678.99;
vtOut[5].vt = VT_BSTR;
sz = "Привет всем от строки из Dll!";
vtOut[5].bstrVal = sz.AllocSysString();
return 6; // Должны вернуть число заполненных элементов
// Или число <= 0 в случае ошибки
}
Всю захваченную внутри функции память нужно освободить перед выходом из функции. Все операции по освобождению памяти, используемой при передаче данных, осуществляются внутри K3.
Создание динамической библиотеки средствами CBuilder
В принципе, создание динамических библиотек в различных средах мало, чем отличается.
Пример экспортируемой функции, созданной в CBuilder:
#define CDDCALLINGFORMAT __cdecl
#define CDDEXPORT extern "C" __declspec(dllexport)
CDDEXPORT int CDDCALLINGFORMAT MyXXXFunc(int n1,
VARIANT* v1,
int n2,
VARIANT* v2)
{
return 5;
}
Прототип экспортируемой функции тот же самый. Тем не менее, есть небольшое отличие при вызове этой функции из макрокоманды.
Отличие связано с разным представлением имён функций внутри DLL для Microsoft и Borland. Если динамическая библиотека создана посредством продуктов Borland, то в макрокоманде при указании имени функции надо добавить “_”.
Например:
defarr In[3];
defarr Out[10];
In [1]=1;
In [2]="Hello";
In [3]=123.45;
Res=DllFuncExec("D:\\K32_Tlx\\TestExtDll\\Debug\\ExtDll.dll","_MyXXXFunc",In,Out);
=Res;
5
Создание динамической библиотеки средствами Delphi
Для тех, кто использует Delphi можно рекомендовать следующий образец создания экспортируемой в Dll функции:
library YouNameLibrary;
uses
SysUtils,
Classes,Dialogs;
type
TDynArr=array of Variant;
function Pan( nIn:Integer; const vtIn:TDynArr;
nOut:Integer; vtOut: TDynArr):Integer; cdecl;
var
sz:String;
vt:Variant;
vto:OleVariant;
begin
sz:='Я получил число входных: '+Inttostr(nIn);
showmessage(sz);
sz := InttoStr(vtIn[0])+'& '+InttoStr(vtIn[1]);
showmessage(sz);
vtOut[0]:=123;
vto := 'Hello User!;
vtOut[1] := vto;
Pan := 2;
end;
exports
Pan;
begin
end.
Тогда макрокоманда, представленная ниже, передаст в Dll два числа (1, 7) – покажет сообщение о числе параметров (2) и их значение. Функция же вернёт в dirt[1] число 123, а в dirt[2] будет находиться строка “Hello User!”.
defarrays second[2] dirt[2];
second[1]=1;
second[2]=7;
=DllFuncExec("D:\\SAMPLE\\YouNameLibrary.dll","Pan",second,dirt);
2
=dirt[1];
123
=dirt[2];
Hello User!
Пример функции вычисления минимума и максимума, динамически вызываемой из К3
Пример макропрограммы:
defarr a[20], b[2];
// ...
// Здесь заполнение массива a
// ...
=DllFuncExec("ExtDll.dll","MinMaxFunc",a,b);
2
min=b[1]
max=b[2]
Пример функции в С:
#include "stdafx.h"
#include "ExtDll.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
CDDEXPORT int CDDCALLINGFORMAT MinMaxFunc(
const int nIn,
const VARIANT* vtIn,
const int nOut,
VARIANT* vtOut)
{
CString sz;
_variant_t vt;
double dmin, dmax, ddd;
dmin = 10000000;
dmax = -dmin;
if(vtIn)
{
for(int i = 0; i < nIn; i++)
{
// Берем значение элемента входного массива в виде double
ddd = vtIn[i].dblVal;
if ( ddd > dmax) dmax = ddd;
if ( ddd < dmin) dmin = ddd;
}
}
// Заполним массив данными для возврата их в K3
vtOut[0].vt = VT_R8; // Тип double
vtOut[0].dblVal = dmin; // Значение
vtOut[1].vt = VT_R8;
vtOut[1].dblVal = dmax;
return 2;
}
Дополнительные функции для работы с внешними динамическими библиотеками
Работа функции DllFuncExec заключается в загрузке динамической библиотеки, нахождении нужной функции, выполнении её и выгрузке библиотеки. То есть, при необходимости выполнить другую функцию из той же библиотеки, последовательность действии повторится. Для исключения накладных расходов, связанных с загрузкой/выгрузкой динамической библиотеки можно использовать набор из трёх функций: DllLoad, DllFunc и DllFree.
INT DllLoad(STRING <PathDll>)
Функции DllLoad загружает динамически подключаемую библиотеку с полным именем файла <PathDll> в память и возвращает уникальный идентификатор.
INT DllFunc(INT <idDll>, STRING <NameFunc>, variant ARRAY <In>, variant ARRAY <Out>)
Функция DllFunc инициирует исполнение функции с именем <NameFunc> в динамически подключаемой библиотеке с идентификатором <idDll>, загруженной функцией DllLoad, передает ей в качестве входного параметра массив <In> и заполняет выходной массив <Out>.
Logical DllFree(INT <idDll>)
Функция DllFree выгружает из памяти динамически подключаемую библиотеку с идентификатором <idDll>. Функция возвращает:
- 1 — библиотека успешно выгружена.
- 0 — ошибка
Работа функциями должна выполняться в следующей последовательности:
- 1. Загрузить библиотеку с помощью функции
DllLoad. - 2. Используя идентификатор, возвращенный из функции загрузки, выполнить один или несколько вызовов функций из библиотеки с помощью
DllFunc - 3. После того, как библиотека станет ненужной, выгрузить eё с помощью
DllFree
Пример:
defarr In[3];
defarr Out[10];
In[1]=1;
In[2]="Hello";
In[3]=123.45;
IDLib=DllLoad("D:\\ExtDll.dll");
Res=DllFunc(IDLib,"MyFunc1",In,Out);
Res=DllFunc(IDLib,"MFunc2",In,Out);
Res=DllFunc(IDLib,"MyFunc3",In,Out);
=DllFree(IDLib);