//////////////////////////
// 1CNativeLib //
// Alexander Solomatin //
// qxlreport@mail.ru //
//////////////////////////
Для тех, кто уже почитал документацию по технологии создания внешних компонент Native API и понял, что на Delphi написать такую ВК довольно сложно.
1CNativeLib - библиотека Delphi для создания внешних компонент (ВК) 1С по технологии Native API. Библиотека легка в использовании и позволяет заниматься непосредственно функционалом ВК, не отвлекаясь на соблюдение инструкций, предоставленных фирмой 1С.
Для того, чтобы создать ВК с помощью 1CNativeLib, достаточно создать проект DLL, написать класс с реализацией функционала вашей ВК и зарегистрировать этот класс при загрузке dll. Минимальный проект DLL с реализацией класса с одним методом выглядит так:
//------------------------------------------------------------------------
library MiniVK;
uses
SysUtils, v8napi;
type
TMyClass = class(TV8UserObject)
function Hello(RetValue: PV8Variant; Params: PV8ParamArray;
const ParamCount: integer): boolean;
end;
{ TMyClass }
function TMyClass.Hello(RetValue: PV8Variant; Params: PV8ParamArray;
const ParamCount: integer): boolean;
begin
V8SetWString(RetValue,'Привет из Delphi!'); //устанавливаем результат функции
result := True;
end;
begin
with ClassRegList.RegisterClass(TMyClass, 'MySuperExtention', 'TMyClass') do
begin
AddFunc('Hello', 'Привет', @TMyClass.Hello, 0); //регистрируем функцию
end;
end.
//----------------------------------------------------------------
после компиляции данного примера создается MiniVK.dll, которую можно использовать в 1С:
//------------------------------------------------------------------------
Процедура Команда1(Команда)
ПодключитьВнешнююКомпоненту("c:\temp\MiniVK.dll","MyVK",AddInType.Native);
Об = Новый("AddIn.MyVK.MySuperExtention");
П = Об.Привет();
Сообщить(П);
КонецПроцедуры
//----------------------------------------------------------------
ЧТО НУЖНО СОБЛЮСТИ ПРИ СОЗДАНИИ КЛАССА ВК
1. Ваш класс должен быть наследован от TV8UserObject.(находится в модуле v8napi)
2. Конструктор Create класса TV8UserObject виртуальный и должен переопределяться с директивой overload. Иначе конструктор не будет вызван при создании объекта ВК.
3. Методы класса, которые будут использоваться в 1С должны быть статическими.
4. Методы класса, которые будут использоваться в 1С должны иметь вид:
для процедур
PV8CallAsProc = function(Params: PV8ParamArray; const ParamCount: integer): boolean;
для функций
PV8CallAsFunc = function(RetValue: PV8Variant; Params: PV8ParamArray; const ParamCount: integer): boolean;
RetValue: PV8Variant - адрес вариантной переменной 1С для результата функции (только для функций);
Params: PV8ParamArray - адрес массива параметров, массив вариантных переменных 1С;
ParamCount: integer - количество переданных параметров;
Тип вариантной переменной 1С V8Variant определен в модуле v8types как record.
Массив параметров начинается с 1 и обратиться к нужному параметру можно так:
Params[N] - обратиться непосредственно к записи V8Variant;
@Params[N] - получить адрес параметра (тип PV8Variant);
N - номер параметра.
для установки/чтения свойств объекта ВК
TV8PropertyGetSet = function(propValue: PV8Variant; Get: boolean): boolean;
propValue - указатель на V8Variant, куда нужно поместить значение при чтении свойства или получить значение при установке свойства;
Get - флаг, указывающий на вид операции со свойством. True - чтение значения свойства. False - установка значения свойства.
Вышеперечисленные функции должны возвращать True, при удачном выполнении.
РЕГИСТРАЦИЯ КЛАССА ВК
Код регистрации класса ВК пишется в основном блоке dll. Классы ВК регистрируются с помощью методов объекта ClassRegList, который определен в модуле v8napi и создается сам, при загрузке модуля.
Метод RegisterClass возвращает объект типа TClassReg, который имеет методы для регистрации свойств, функций и процедур объекта ВК.
ЗАРЕГИСТРИРОВАТЬ ПРОЦЕДУРУ:
function AddProc(MethName, MethNameLoc: WideString;ExecuteProc: PV8CallAsProc; ParamCount: integer = 0): TMethReg;
MethName - английское имя процедуры;
MethNameLoc - русское имя процедуры;
ExecuteProc - адрес метода класса ВК, который реализует данную процедуру. Тип PV8CallAsProc.
ParamCount - количество параметров процедуры;
ЗАРЕГИСТРИРОВАТЬ ФУНКЦИЮ:
function AddFunc(MethName, MethNameLoc: WideString;ExecuteProc: PV8CallAsFunc; ParamCount: integer = 0): TMethReg;
параметры имеют тоже значение, за исключением ExecuteProc, который должен иметь тип PV8CallAsFunc.
ЗАРЕГИСТРИРОВАТЬ СВОЙСТВА:
procedure AddProp(PropName, PropNameLoc: WideString;
IsReadable: boolean;
IsWritable: boolean; PropGetSet: PV8PropertyGetSet);
PropName - английское имя свойства;
PropNameLoc - русское имя свойства;
IsReadable - свойство будет читаться
IsWritable - в свойство можно писать
PropGetSet - адрес метода класса ВК, который реализует установку и получение значения свойства.
ЗАРЕГИСТРИРОВАТЬ ПАРАМЕТРЫ ПО УМОЛЧАНИЮ:
Методы AddProc и AddFunc возвращают объект класса TMethReg, который имеет переменную DefParams класса TDefParamList, который имеет методы для добавления значений параметров по умолчанию для каждого типа.
procedure AddInt(V: integer; ParamNum: integer);
procedure AddDouble(V: double; ParamNum: integer);
procedure AddBool(V: boolean; ParamNum: integer);
procedure AddWString(const V: PWideChar; ParamNum: integer);
procedure AddAString(const V: PAnsiChar; ParamNum: integer);
procedure AddDate(V: TDateTime; ParamNum: integer);
V - значение параметра;
ParamNum - номер параметра;
ПРИМЕР РЕГИСТРАЦИИ КЛАССА
регистрация двух классов TMyClass1 и TMyClass2.
//-----------------------------------------------------------------------------
begin
with ClassRegList.RegisterClass(TMyClass1, 'MySuperExtention1', 'TMyClass1') do
begin
AddFunc('GetValueType', 'ДайТипЗначения', @TMyClass1.GetValueType, 1);
AddProc('Hello', 'Привет', @TMyClass1.Hello, 0);
AddProp('Property1', 'Свойство1',True,True,@TMyClass1.GetSetProp1);
AddProp('PropertyVersion',
'Версия',True,False,
@TMyClass1.GetSetVersion); //свойство только для чтения
end;
with ClassRegList.RegisterClass(TMyClass2, 'MySuperExtention2', 'TMyClass2') do
begin
AddFunc('Function1', 'Функция1', @TMyClass2.Function1, 1);
with AddProc('Procedure1', 'Процедура1', @TMyClass2.Procedure1, 2) do //с параметрами по умолчанию
begin
DefParams.AddInt(1000000,1);
DefParams.AddWString('Параметр по умолчанию номер два',2);
end;
end;
end.
//-----------------------------------------------------------------------------
РАБОТА С ПЕРЕМЕННЫМИ 1С
Для работы с параметрами, передаваемыми в ВК из 1С и для возврата значений функций используется функции из модуля v8napi и специальные методы класса TV8UserObject. Методы класса TV8UserObject используются для работы с переменными 1С с использованием менеджера памяти 1С. Сам менеджер памяти 1С доступен как
свойство класса с именем V8MM.
Методы класса TV8UserObject для работы с типом V8Variant:
procedure V8ClearVar(V: PV8Variant);
Очищает переменную V8Variant. Если переменная V8Variant имела длинное значение (строка или blob), то память, занятая этой переменной освобождается менеджером памяти 1С
procedure V8SetBool(V: PV8Variant; Value: boolean);
procedure V8SetDate(V: PV8Variant; Value: TDateTime);
procedure V8SetInt(V: PV8Variant; Value: integer);
procedure V8SetDouble(V: PV8Variant; Value: double);
Функции устанавливают значения коротких типов переменной V8Variant. Перед установкой значения, переменная V8Variant очищается. Если переменная V8Variant имела длинное значение (строка или blob), то память, занятая этой переменной освобождается менеджером памяти 1С.
function V8SetWString(V: PV8Variant; Value: WideString): boolean;
function V8SetString(V: PV8Variant; Value: AnsiString): boolean;
function V8SetPChar(V: PV8Variant; Value: PAnsiChar): boolean;
function V8SetBlob(V: PV8Variant; Value: PByte; Length: integer): boolean;
Функции станавливают значение переменной V8Variant строкового типа или blob. Перед установкой значения, переменная V8Variant очищается. Если переменная V8Variant имела длинное значение (строка или blob), то память, занятая этой переменной, освобождается менеджером памяти 1С. Память под значение переменной выделяется менеджером памяти 1С.
V - указатель на переменную типа V8Variant;
Value - значение соответствующего типа;
Функции модуля для работы с типом V8Variant:
Функции группы V8is* проверяют переменную типа V8Variant на соответствие определенному типу.
function V8isEmpty(V: PV8Variant): boolean; - Переменная пуста
function V8isNULL(V: PV8Variant): boolean; - Переменная NULL
function V8isNumber(V: PV8Variant): boolean; - целое или вещественное число, код ошибки
function V8isString(V: PV8Variant): boolean;
function V8isWString(V: PV8Variant): boolean; - WideString
function V8isAString(V: PV8Variant): boolean; - AnsiString
function V8isBlob(V: PV8Variant): boolean;
function V8isDate(V: PV8Variant): boolean; - ДатаВремя (VTYPE_DATE или VTYPE_TM)
function V8isBool(V: PV8Variant): boolean; - boolean
Функции V8AsInt и V8AsDouble возвращают значение из V8Variant. Если значение не соответствует типу, возвращается ноль.
function V8AsInt(V: PV8Variant): integer; - тип Double возвращается как целая часть
function V8AsDouble(V: PV8Variant): double; - тип integer возвращается как Double
function V8AsDate(V: PV8Variant): TDateTime;
Возвращает значение типа ДатаВремя из V8Variant типа дата (VTYPE_DATE,VTYPE_TM) или Double. Если значение не соответствует типу, возвращает ноль.
function V8AsBool(V: PV8Variant): boolean;
Возвращает значение типа boolean из V8Variant. Если значение не соответствует типу, возвращает False
function V8AsPWideChar(V: PV8Variant): PWideChar;
function V8AsPChar(V: PV8Variant): PAnsiChar;
function V8AsBlob(V: PV8Variant): Pbyte;
Функции возвращают указатели на значения типов Blob, AnsiChar, WideChar из V8Variant. Если значение не соответствует типу, возвращается nil.
function V8AsAString(V: PV8Variant): AnsiString;
Возвращает значения типов AnsiString и WideString из V8Variant в виде строки AnsiString.
function V8StrLen(V: PV8Variant): integer;
Длина переменной для типов Blob, AnsiString, WideString.
function V8VarTypeStr(V: PV8Variant): AnsiString;
Возвращает, в виде строки, тип значения переменной V8Variant.
ПРИМЕР РАБОТЫ С ПЕРЕМЕННЫМИ 1С
//------------------------------------------------------------
function MyFunction(RetValue: PV8Variant; Params: PV8ParamArray; const ParamCount: integer): boolean;
//берем значения из четырех параметров функции, изменяем из и помещаем значения //обратно.
//В результат функции пишем True
var
i : integer;
d : double;
dt : TDateTime;
w : WideString;
begin
i := V8AsInt(@Params[1]);
i := I * 2; //умножаем на 2
V8SetInt(@Params[1],i); //сохраняем обратно в переменную 1С
d := V8AsDouble(@Params[2]);
d := d * 2; //умножаем на 2
V8SetDouble(@Params[2],d); //сохраняем обратно в переменную 1С
dt := V8AsDate(@Params[3]);
dt := dt + 1; //добавляем день к дате
V8SetDate(@Params[3],dt);//сохраняем обратно в переменную 1С
w := V8AsPWideChar(@Params[4]);
w := w + ' здесь была наша ВК'; //добавляем к параметру свою строку
V8AsSetWString(@Params[4],w);//сохраняем обратно в переменную 1С
V8SetBool(RetValue,True); //Результат функции
result := True;
end;
//------------------------------------------------------------
код в 1С
//------------------------------------------------------------
Процедура Команда1(Команда)
ПодключитьВнешнююКомпоненту("c:\temp\MiniVK.dll","MyVK",AddInType.Native);
Об = Новый("AddIn.MyVK.MySuperExtention");
П_Целое = 100;
П_Число = 100.01;
П_Дата = ТекущаяДата();
П_Строка = "Измени меня, ВК.";
Об.MyFunction(П_Целое,П_Число,П_Дата,П_Строка);
Сообщить(П_Целое);
Сообщить(П_Число);
Сообщить(П_Дата);
Сообщить(П_Строка);
КонецПроцедуры
//----------------------------------------------------------------
ПРИМЕР РАБОТЫ СО СВОЙСТВАМИ
//------------------------------------------------------------
//Объявление класса ВК
TMyClass = class(TV8UserObject)
private
Property1 : integer; //здесь хранится значение нашего свойства
public
function Property1GetSet(propValue: PV8Variant; Get: boolean): boolean;
end;
...
//код реализации установки/чтения свойства
function TMyClass.Property1GetSet(propValue: PV8Variant; Get: boolean): boolean;
begin
if Get then //если 1С хочет получить значение нашего свойства
begin
V8SetInt(propValue,Property1);
end else
begin
Property1 := V8AsInt(propValue);
end;
end;
...
//код регистрации класса ВК
with ClassRegList.RegisterClass(TMyClass, 'MySuperExtention', 'TMyClass') do
begin
AddProp('Property1', 'Свойство1',True,True,@TMyClass.Property1GetSet);
end;
//----------------------------------------------------------------
СВОЙСТВА КЛАССА TV8UserObject
ClassReg: TClassReg; - регистрация класса.
V8MM: TV8MemoryManager; - менеджер памяти 1С
V8: TV8AddInDefBase; - ссылка на объект IAddInDefBase, интерфейс 1С:Предприятия который передается 1С после создания объекта. (см. документацию от 1С)
locale: WideString; - строка с названием локали, переданная 1С после создания объекта (см. документацию от 1С)
//----------------------------------------------------------------
Менеджер памяти 1С (см. документацию от 1С)
TV8MemoryManager = class
procedure Destroy1; virtual; abstract;
function AllocMemory(pMemory: PPointer; ulCountByte: longword): boolean; virtual; stdcall; abstract;
procedure FreeMemory(pMemory: PPointer); virtual; stdcall;abstract;
end;
Интерфейс 1С:Предприятия (см. документацию от 1С)
TV8AddInDefBase = class
procedure Destroy1; virtual; abstract;
function AddError(wcode: word; const source: PWideChar;
const descr: PWideChar;
scode: integer): boolean; virtual; stdcall; abstract;
function Read(wszPropName: PWideChar;pVal: PV8Variant;pErrCode: PInteger;errDescriptor: PPWideChar): boolean; virtual; stdcall; abstract;
function Write(wszPropName: PWideChar;pVar: PV8Variant): boolean; virtual; stdcall; abstract;
function RegisterProfileAs(wszProfileName: PWideChar): boolean; virtual; stdcall; abstract;
function SetEventBufferDepth(lDepth: integer): boolean; virtual; stdcall; abstract;
function GetEventBufferDepth: integer; virtual; stdcall; abstract;
function ExternalEvent(wszSource, wszMessage, wszData: PWideChar): boolean; virtual; stdcall; abstract;
procedure CleanEventBuffer; virtual; stdcall; abstract;
function SetStatusLine(wszStatusLine: PWideChar): boolean; virtual; stdcall; abstract;
procedure ResetStatusLine; virtual; stdcall; abstract;
end;