Работа с динамически подключаемыми библиотеками
Макроязык 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 заключается в загрузке динамической библиотеки, нахождении нужной функции, выполнении её и выгрузке библиотеки. То есть, при необходимости выполнить другую функцию из той же библиотеки, последовательность действии повторится. Для исключения накладных расходов, связанных с загрузкой/выгрузкой динамической библиотеки можно использовать набор из трёх функций:
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);