Вы здесь

Передача параметров в функции динамических библиотек

При создании системы «Умный дом» я столкнулся с двумя проблемами:

  1. Если вы когда-нибудь вели разработку программы состоящей из исполняемого файла и динамической библиотеки вы должны быть в курсе, что существуют некоторые проблемы при передачи параметров — есть ограничения передаваемых типов.

  2. Pascal не поддерживает создание функций с переменным количеством параметров. А такие функции были нужны хотя бы потому что драйвер устройства может возвращать несколько значений (устройство ведь может быть сложным)

Для решения этих двух проблем было решено множественные параметры возвращать в виде массива. Были описаны такие типы данных:

  1. type
  2. TDLParam = record // Описание параметра
  3. Name: string; // Имя параметра
  4. typename: string; // Тип параметра
  5. Value: string; // Значение параметра
  6. pValue: Pointer; // Значение параметра типа Pointer
  7. end;
  8.  
  9. TDLParamArray = array of TDLParam; // Массив параметров
  10. TDLParamForSend = PChar; // Формат для передачи в DLL

Для удобства работы с этим массивом был описан класс:

  1. TDLParams = class
  2. // Набор параметров для передачи в библиотечную функцию
  3. private
  4. ParamArray: TDLParamArray;
  5. function GetParams: TDLParamForSend;
  6. // Возвращает массив параметров
  7. procedure SetParams(Value: TDLParamForSend);
  8. // Устанавливает массив параметров
  9. function GetParamIndex(Name: string): integer;
  10. public
  11. property Params: TDLParamForSend Read GetParams Write SetParams;
  12. property ParamsArray: TDLParamArray Read ParamArray Write ParamArray;
  13. constructor Create;
  14. procedure AddParamAsInteger(Name: string; Value: integer);
  15. procedure AddParamAsReal(Name: string; Value: real);
  16. procedure AddParamAsString(Name: string; Value: string);
  17. procedure AddParamAsBoolean(Name: string; Value: boolean);
  18. procedure AddParamAsPointer(Name: string; Value: Pointer);
  19. function GetParamAsInteger(Name: string): integer;
  20. function GetParamAsReal(Name: string): real;
  21. function GetParamAsString(Name: string): string;
  22. function GetParamAsBoolean(Name: string): boolean;
  23. function GetParamAsPointer(Name: string): Pointer;
  24. procedure DeleteParam(Name: string);
  25. destructor Free;
  26. end;

AddParamAsInteger(Name: string; Value: integer) Добавляет в массив параметров параметр типа Integer, Name имя параметра, Value значение параметра.

AddParamAsReal(Name: string; Value: real) Добавляет в массив параметров параметр типа Real, Name имя параметра, Value значение параметра.

AddParamAsString(Name: string; Value: string) Добавляет в массив параметров параметр типа String, Name имя параметра, Value — значение параметра.

AddParamAsBoolean(Name: string; Value: boolean) Добавляет в массив параметров параметр типа Boolean, Name имя параметра, Value значение параметра.

AddParamAsPointer(Name: string; Value: Pointer) Добавляет в массив параметров параметр типа Pointer, Name имя параметра, Value значение параметра.

GetParamAsInteger(Name: string): integer; Возвращает из массива параметров параметр типа Integer. Name имя параметра.

GetParamAsReal(Name: string): real; Возвращает из массива параметров параметр типа Real. Name имя параметра.

GetParamAsString(Name: string): string; Возвращает из массива параметров параметр типа String. Name имя параметра.

GetParamAsBoolean(Name: string): boolean; Возвращает из массива параметров параметр типа Boolean. Name имя параметра.

GetParamAsPointer(Name: string): Pointer; &‐ Возвращает из массива параметров параметр типа Pointer. Name имя параметра.

DeleteParam(Name: string) – Удаляет параметр из массива. Nameимя параметра.

Params – Параметры проебразованные в строку для передачи в динамическую библиотеку.

ParamsArray – Массив со значениями.

Предполагается что использоваться это будет так:

Если необходимо передать в функцию массив параметров, то перед её вызовом создается экземпляр класса TDLParams. Далее с использование специальных функций в массив параметров добавляются параметры и далее в функцию передается ни сам класс, а массив. Вот пример:

  1. var
  2. TestParams: TDLParams;
  3. begin
  4. TestParams := TDLParams.Create;
  5. TestParams.AddParamAsInteger('PARAM1', 1);
  6. WriteParams(TestParams.Params);
  7. TestParams.Free;
  8. TestParams := nil;
  9. end;

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

  1. Procedure WriteParams(pparam: TDLParamForSend);
  2. var
  3. ParamsInFunct: TDLParams;
  4. begin
  5. ParamsInFunct := TDLParams.Create;
  6. ParamsInFunct.Params := pparam; // После записи pparam в ParamsInFunct.Params
  7. // в pparam будет пусто и память будет
  8. // освобождена
  9. WriteLn(IntToStr(ParamsInFunct.GetParamAsInteger('PARAM1')));
  10. ParamsInFunct.Free;
  11. ParamsInFunct := nil;
  12. end;

Если необходимо из функции вернуть несколько параметров, то можно сделать так:

  1. Function GetParams: TDLParamForSend;
  2. var
  3. TestParams: TDLParams;
  4. begin
  5. TestParams := TDLParams.Create;
  6. TestParams.AddParamAsInteger('PARAM1', 1);
  7. result := TestParams.Params;
  8. TestParams.Free;
  9. TestParams := nil;
  10. end;

А при вызове сделать как-то так:

  1. var
  2. ParamsResult: TDLParams;
  3. begin
  4. ParamsResult := TDLParams.Create;
  5. ParamsResult.Params := GetParams();
  6. WriteLn(IntToStr(ParamsResult.GetParamAsInteger('PARAM1')));
  7. ParamsResult.Free;
  8. ParamsResult := nil;
  9. end;

Стоит обратить внимание на то, что при считывании из свойства Params класса TDLParams в памяти создается блок данных типа PChar и возвращается указатель на него. При записи в Params блок памяти освобождается.

Рекомендую для передачи пользоваться классом TDLParams. Использование этого класса исключит необходимость переписывать код программы, если структура параметров будет изменена. Нужно будет просто скачать новую версию модуля и перекомпилировать программу.

Не утверждаю, что такая структура оптимальна, возможно она будет меняться в будущем.