При создании системы «Умный дом» я столкнулся с двумя проблемами:
Если вы когда-нибудь вели разработку программы состоящей из исполняемого файла и динамической библиотеки вы должны быть в курсе, что существуют некоторые проблемы при передачи параметров — есть ограничения передаваемых типов.
Pascal не поддерживает создание функций с переменным количеством параметров. А такие функции были нужны хотя бы потому что драйвер устройства может возвращать несколько значений (устройство ведь может быть сложным)
Для решения этих двух проблем было решено множественные параметры возвращать в виде массива. Были описаны такие типы данных:
type TDLParam = record // Описание параметра Name: string; // Имя параметра typename: string; // Тип параметра Value: string; // Значение параметра pValue: Pointer; // Значение параметра типа Pointer end; TDLParamArray = array of TDLParam; // Массив параметров TDLParamForSend = PChar; // Формат для передачи в DLL
Для удобства работы с этим массивом был описан класс:
TDLParams = class // Набор параметров для передачи в библиотечную функцию private ParamArray: TDLParamArray; function GetParams: TDLParamForSend; // Возвращает массив параметров procedure SetParams(Value: TDLParamForSend); // Устанавливает массив параметров function GetParamIndex(Name: string): integer; public property Params: TDLParamForSend Read GetParams Write SetParams; property ParamsArray: TDLParamArray Read ParamArray Write ParamArray; constructor Create; procedure AddParamAsInteger(Name: string; Value: integer); procedure AddParamAsReal(Name: string; Value: real); procedure AddParamAsString(Name: string; Value: string); procedure AddParamAsBoolean(Name: string; Value: boolean); procedure AddParamAsPointer(Name: string; Value: Pointer); function GetParamAsInteger(Name: string): integer; function GetParamAsReal(Name: string): real; function GetParamAsString(Name: string): string; function GetParamAsBoolean(Name: string): boolean; function GetParamAsPointer(Name: string): Pointer; procedure DeleteParam(Name: string); destructor Free; 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. Далее с использование специальных функций в массив параметров добавляются параметры и далее в функцию передается ни сам класс, а массив. Вот пример:
var TestParams: TDLParams; begin TestParams := TDLParams.Create; TestParams.AddParamAsInteger('PARAM1', 1); WriteParams(TestParams.Params); TestParams.Free; TestParams := nil; end;
Внутри функции необходимо создать свой экземпляр класса и передать ему полученный параметр. Пример:
Procedure WriteParams(pparam: TDLParamForSend); var ParamsInFunct: TDLParams; begin ParamsInFunct := TDLParams.Create; ParamsInFunct.Params := pparam; // После записи pparam в ParamsInFunct.Params // в pparam будет пусто и память будет // освобождена WriteLn(IntToStr(ParamsInFunct.GetParamAsInteger('PARAM1'))); ParamsInFunct.Free; ParamsInFunct := nil; end;
Если необходимо из функции вернуть несколько параметров, то можно сделать так:
Function GetParams: TDLParamForSend; var TestParams: TDLParams; begin TestParams := TDLParams.Create; TestParams.AddParamAsInteger('PARAM1', 1); result := TestParams.Params; TestParams.Free; TestParams := nil; end;
А при вызове сделать как-то так:
var ParamsResult: TDLParams; begin ParamsResult := TDLParams.Create; ParamsResult.Params := GetParams(); WriteLn(IntToStr(ParamsResult.GetParamAsInteger('PARAM1'))); ParamsResult.Free; ParamsResult := nil; end;
Стоит обратить внимание на то, что при считывании из свойства Params класса TDLParams в памяти создается блок данных типа PChar и возвращается указатель на него. При записи в Params блок памяти освобождается.
Рекомендую для передачи пользоваться классом TDLParams. Использование этого класса исключит необходимость переписывать код программы, если структура параметров будет изменена. Нужно будет просто скачать новую версию модуля и перекомпилировать программу.
Не утверждаю, что такая структура оптимальна, возможно она будет меняться в будущем.