Работа с динамически подключаемыми библиотеками

Материал из GEOS_WIKI
Перейти к: навигация, поиск

Макроязык 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);