понедельник, 19 апреля 2021 г.

Внешние компоненты в 1С 8.2

 Введение

Эта статья дает представление о работе внешних компонент в системе «1С: Предприятие».
Будет показан процесс разработки внешней компоненты для системы «1С: Предприятие» версии 8.2, работающей под управлением ОС семейства Windows с файловым вариантом работы. Такой вариант работы используется в большинстве решений, предназначенных для предприятий малого бизнеса. ВК будет реализована на языке программирования C++.

Внешние компоненты «1C: Предприятие»


«1С: Предприятие» является расширяемой системой. Для расширения функциональных возможностей системы используются внешние компоненты (ВК). С точки зрения разработчика ВК представляет собой некоторый внешний объект, который имеет свойства и методы, а также может генерировать события для обработки системой «1С: Предприятие».
Внешние компоненты можно использовать для решения класса задач, которые сложно или даже невозможно реализовать на встроенном в «1C: Предприятие» языке программирования. В частности, к такому классу можно отнести задачи, требующие низкоуровневого взаимодействия с операционной системой, например, для работы с специфичным оборудованием.
В системе «1С: Предприятие» используются две технологии создания внешних компонент:
  • с использованием Native API
  • с использованием технологии COM

При заданных ограничениях между двумя вышеозначенными технологиями разница незначительна, поэтому будем рассматривать разработку ВК с использованием Native API. При необходимости, реализованные наработки могут быть применены для разработки ВК с использованием технологии COM, а также, с незначительными доработками, применены для использования в системе «1С: Предприятие» с другими вариантами работы, отличными от файлового режима работы.

Структура ВК

Внешняя компонента системы «1С: Предприятие» представлена в виде DLL-библиотеки. В коде библиотеки описывается класс-наследник IComponentBase. В создаваемом классе должны быть определены методы, отвечающие за реализацию функций внешней компоненты. Более подробно переопределяемые методы будут описаны ниже по ходу изложения материала.

Запуск демонстрационной ВК


Задача:
  1. Выполнить сборку внешней компоненты, поставляемой с подпиской ИТС и предназначенной для демонстрации основных возможностей механизма внешних компонент в 1С
  2. Подключить демонстрационную компоненту к конфигурации 1С
  3. Убедиться в корректной работоспособности заявленных функций


Компиляция

Демонстрационная ВК расположена на диске подписки ИТС в каталоге «/VNCOMP82/example/NativeAPI».
Для сборки демонстрационной ВК будем использовать Microsoft Visual Studio 2008. Другие версии данного продукта не поддерживают используемый формат проекта Visual Studio.

Открываем проект AddInNative. В настройках проекта подключаем каталог с заголовочными файлами, необходимыми для сборки проекта. По умолчанию они располагаются на диске ИТС в каталоге /VNCOMP82/include.
Результатом сборки является файл /bind/AddInNative.dll. Это и есть скомпилированная библиотека для подключения к конфигурации 1С.

Подключение ВК к конфигурации 1С

Создадим пустую конфигурацию 1С.
Ниже приведен код модуля управляемого приложения.
перем ДемоКомп;
Процедура ПриНачалеРаботыСистемы()
	ПодключитьВнешнююКомпоненту("...\bind\AddInNative.dll", "DemoVK", ТипВнешнейКомпоненты.Native);
	ДемоКомп = Новый("AddIn.DemoVK.AddInNativeExtension");
КонецПроцедуры

Если при запуске конфигурации 1С не было сообщено об ошибке, то ВК была успешно подключена.
В результате выполнения приведенного кода в глобальной видимости конфигурации появляется объект ДемоКомп, имеющий свойства и методы, которые определены в коде внешней компоненты.

Демонстрация заложенного функционала

Проверим работоспособность демонстрационной ВК. Для этого попробуем установить и прочитать некоторые свойства, вызвать некоторые методы ВК, а также получить и обработать сообщение ВК.
В документации, поставляемой на диске ИТС заявлен следующий функционал демонстрационной ВК:
  1. Управление состоянием объекта компоненты
    Методы: ВключитьВыключить
    Свойства: Включен
  2. Управлением таймером
    Каждую секунду компонента посылает сообщение системе «1C: Предприятие» с параметрами ComponentTimer и строкой счетчика системных часов.
    Методы: СтартТаймерСтопТаймер
    Свойства: ЕстьТаймер
  3. Метод ПоказатьВСтрокеСтатуса, который отображает в строке статуса текст, переданный методу в качестве параметров
  4. Метод ЗагрузитьКартинку. Загружает изображение из указанного файла и передает его в систему «1C: Предприятие» в виде двоичных данных.

Убедимся в работоспособности этих функций. Для этого исполним следующий код:
перем ДемоКомп;
Процедура ПриНачалеРаботыСистемы()
	ПодключитьВнешнююКомпоненту(...);
	ДемоКомп = Новый("AddIn.DemoVK.AddInNativeExtension");
	ДемоКомп.Выключить();
	Сообщить(ДемоКомп.Включен);
	ДемоКомп.Включить();
	Сообщить(ДемоКомп.Включен);
	ДемоКомп.СтартТаймер();
КонецПроцедуры     
Процедура ОбработкаВнешнегоСобытия(Источник, Событие, Данные)
	Сообщить(Источник + " " + Событие + " " + Данные);
КонецПроцедуры

Результат запуска конфигурации приведен на изображении

На панель «Сообщения» выведены результаты вызовов методов ДемоКомп.Выключить() и Демо.Комп.Включить(). Последующие строки на той же панели содержат результаты обработки полученных от ВК сообщений — ИсточникСобытие и Данные соответственно.

Произвольное имя внешней компоненты


Задача: Изменить имя внешней компоненты на произвольное.
В предыдущем разделе использовался идентификатор AddInNativeExtension, смысл которого не был пояснен. В данном случае AddInNativeExtension — это наименование расширения.
В коде ВК определен метод RegisterExtensionAs, возвращающий системе «1С: Предприятие» имя, которое необходимо для последующей регистрации ВК в системе. Рекомендуется указывать идентификатор, который в известной мере раскрывает суть внешней компоненты.
Приведем полный код метода RegisterExtensionAs с измененным наименованием расширения:
bool CAddInNative::RegisterExtensionAs(WCHAR_T** wsExtensionName)
{ 
    wchar_t *wsExtension = L"SomeName";
    int iActualSize = ::wcslen(wsExtension) + 1;
    WCHAR_T* dest = 0;
    if (m_iMemory)
    {
        if(m_iMemory->AllocMemory((void**)wsExtensionName, iActualSize * sizeof(WCHAR_T)))
            ::convToShortWchar(wsExtensionName, wsExtension, iActualSize);
        return true;
    }
    return false; 
}

В приведенном примере имя ВК изменено на SomeName. Тогда при подключении ВК необходимо указывать новое имя:
ДемоКомп = Новый("AddIn.DemoVK.SomeName");


Расширение списка свойств ВК


Задача:
  1. Изучить реализацию свойств ВК
  2. Добавить свойство строкового типа, доступное для чтения и записи
  3. Добавить свойство строкового типа, доступное для чтения и записи, которое хранит тип данных последнего установленного свойства. При установке значения свойства никаких действий не производится
  4. Убедиться в работоспособности произведенных изменений

Для определения свойств создаваемой компоненты разработчику необходимо реализовать следующие методы в коде библиотеки AddInNative.cpp:
GetNProps
Возвращает количество свойств данного расширения, 0 – при отсутствии свойств
FindProp
Возвращает порядковый номер свойства, имя которого передается в параметрах
GetPropName
Возвращает имя свойства по его порядковому номеру и по переданному идентификатору языка
GetPropVal
Возвращает значение свойства с указанным порядковым номером
SetPropVal
Устанавливает значение свойства с указанным порядковым номером
IsPropReadable
Возвращает флаг флаг возможности чтения свойства с указанным порядковым номером
IsPropWritable
Возвращает флаг флаг возможности записи свойства с указанным порядковым номером

Полное описание методов, включая список параметров подробно описан в документации, поставляемой на диске ИТС.
Рассмотрим реализацию приведенных методов класса CAddInNative.
В демонстрационной ВК определены 2 свойства: Включен и ЕстьТаймер (IsEnabled и IsTimerPresent).
В глобальной области видимости кода библиотеки определено два массива:
static wchar_t *g_PropNames[] = {L"IsEnabled", L"IsTimerPresent"};
static wchar_t *g_PropNamesRu[] = {L"Включен", L"ЕстьТаймер"};

которые хранят русское и английское названия свойств. В заголовочном файле AddInNative.h определяется перечисление:
enum Props
    {
        ePropIsEnabled = 0,
        ePropIsTimerPresent,
        ePropLast      // Always last
    };

ePropIsEnabled и ePropIsTimerPresent, соответственно имеющие значения 0 и 1 используются для замены порядковых номеров свойств на осмысленные идентификаторы. ePropLast, имеющее значение 2, используется для получения количества свойств (методом GetNProps). Эти имена используются только внутри кода компоненты и недоступны извне.
Методы FindProp и GetPropName осужествляют поиск по массивам g_PropNames и g_PropNamesRu.
Для хранения значения полей в модуле библиотеки у класса CAddInNative определены свойства, которые хранят значение свойств компоненты. Методы GetPropVal и SetPropVal соответственно возвращают и устанавливают значение этих свойств.
Методы IsPropReadable и IsPropWritable и возвращают trure или false, в зависимости от переданного порядкового номера свойства в соответствии с логикой приложения.
Для того, чтобы добавить произвольное свойство необходимо:
  1. Добавить имя добавляемого свойства в массивы g_PropNames и g_PropNamesRu (файл AddInNative.cpp)
  2. В перечисление Props (файл AddInNative.h) перед ePropLast добавить имя, однозначно идентифицирующее добавляемое свойство
  3. Организовать память под хранение значений свойств (завести поля модуля компоненты, хранящие соответствующие значения)
  4. Внести изменения в методы GetPropVal и SetPropVal для взаимодействия с выделенной на предыдущем шаге памятью
  5. В соответствии с логикой приложения внести изменения в методы IsPropReadable и IsPropWritable

Пункты 1, 2, 5 не нуждаются в пояснении. С деталями реализации этих шагов можно ознакомиться, изучив приложение к статье.
Дадим названия тестовым свойствам Тест и ПроверкаТипа соответственно. Тогда в результате выполнения пункта 1 имеем:
static wchar_t *g_PropNames[] = {L"IsEnabled", L"IsTimerPresent", L"Test", L"TestType"};
static wchar_t *g_PropNamesRu[] = {L"Включен", L"ЕстьТаймер", L"Тест", L"ПроверкаТипа"};

Перечисление Props будет иметь вид:
 enum Props
    {
        ePropIsEnabled = 0,
        ePropIsTimerPresent,
		ePropTest1,
		ePropTest2,
        ePropLast      // Always last
    };

Для значительного упрощения кода будем использовать STL C++. В частности, для работы со строками WCHAR, подключим библиотеку wstring.
Для сохранения значения метода Тест, определим в классе CAddInNative в области видимости private поле:
string test1;

Для передачи строковых параметров между «1С: Предприятие» и внешней компонентов используется менеджер памяти «1С: Предприятие». Рассмотрим его работу подробнее. Для выделения и освобождения памяти соответственно используются функции AllocMemory и FreeMemory, определенные в файле ImemoryManager.h. При необходимости передать системе «1С: Предприятие» строковый параметр, внешняя компонента должна выделить под нее память вызовом функции AllocMemory. Ее прототип выглядит следующим образом:
virtual bool ADDIN_API AllocMemory (void** pMemory, unsigned long ulCountByte) = 0;

где pMemory — адрес указателя, в который будет помещен адрес выделенного участка памяти,
ulCountByte — размер выделяемого участка памяти.
Пример выделения памяти под строку:
WCHAR_T *t1 = NULL, *test = L"TEST_STRING";
int iActualSize = wcslen(test1)+1;
m_iMemory->AllocMemory((void**)&t1, iActualSize * sizeof(WCHAR_T));
::convToShortWchar(&t1, test1, iActualSize);

Для удобства работы с строковыми типами данными опишем функцию wstring_to_p. Она получает в качестве параметра wstring-строку. Результатом функции является заполненная структура tVariant. Код функции:
bool CAddInNative::wstring_to_p(std::wstring str, tVariant* val) {
	char* t1;
	TV_VT(val) = VTYPE_PWSTR;
	m_iMemory->AllocMemory((void**)&t1, (str.length()+1) * sizeof(WCHAR_T));
	memcpy(t1, str.c_str(), (str.length()+1) * sizeof(WCHAR_T));
	val -> pstrVal = t1;
	val -> strLen = str.length();
	return true;
}

Тогда соответствующая секция case оператора switch метода GetPropVal примет вид:
case ePropTest1:
	wstring_to_p(test1, pvarPropVal);
	break;

Метода SetPropVal:
 case ePropTest1:
	if (TV_VT(varPropVal) != VTYPE_PWSTR)
		 return false;
	test1 = std::wstring((wchar_t*)(varPropVal -> pstrVal));
	break;

Для реализации второго свойства определим поле класса CaddInNative
uint8_t				last_type;

в котором будем сохранять тип последнего переданного значения. Для этого в метод CaddInNative::SetPropVal добавим команду:
last_type = TV_VT(varPropVal);

Теперь при запросе чтения значения второго свойства будем возвращать значение last_type, чего требует обозначенное задание.
Проверим работоспособность произведенных изменений.
Для этого приведем внешний вид конфигурации 1С к виду:
перем ДемоКомп;
Процедура ПриНачалеРаботыСистемы()
	ПодключитьВнешнююКомпоненту("...", "DemoVK", ТипВнешнейКомпоненты.Native);
	ДемоКомп = Новый("AddIn.DemoVK.SomeName");
	ДемоКомп.ПроверкаТипа = 1;
	Сообщить(Строка(ДемоКомп.ПроверкаТипа));
	ДемоКомп.Тест = "Вася";
	Сообщить(Строка(ДемоКомп.Тест));
	ДемоКомп.Тест = "Петя";
	Сообщить(Строка(ДемоКомп.Тест));
	Сообщить(Строка(ДемоКомп.ПроверкаТипа));
КонецПроцедуры

В результате запуска получим последовательность сообщений:
3
Вася
Петя
22

Второе и третье сообщения являются результатом чтения свойства, установленного на предыдущем шаге. Первое и второе сообщения содержат код типа последнего установленного свойства. 3 соответствует целочисленному значению, 22 — строковому. Соответствие типов и их кодов устанавливается в файле types.h, который находится на диске ИТС.

Расширение списка методов


Задача:
  1. Расширить функционал внешней компоненты следующим функционалом:
  2. Изучить способы реализации методов внешней компоненты
  3. Добавить метод-функцию Функц1, которая в качестве параметра принимает две строки («Параметр1» и «Параметр2»). В качестве результата возвращается строка вида: «Проверка. Параметр1, Параметр2»
  4. Убедиться в работоспособности произведенных изменений

Для определения методов создаваемой компоненты разработчику необходимо реализовать следующие методы в коде библиотеки AddInNative:
GetNMethodsFindMethodGetMethodName
Предназначены для получения соответственно количества методов, поиска номера и имени метода. Аналогичны соответствующим методам для свойств
GetNParams
Возвращает количество параметров метода с указанным порядковым номером; если метод с таким номером отсутствует или не имеет параметров, возвращает 0
GetParamDefValue
Возвращает значение по умолчанию указанного параметра указанного метода
HasRetVal
Возвращает флаг наличия у метода с указанным порядковым номером возвращаемого значения: true для методов с возвращаемым значением и false в противном случае
CallAsProc
Выполняет метод с указанным порядковым номером. Если метод возвращает false, возникает ошибка времени выполнения и выполнение модуля 1С: Предприятия прекращается. Память для массива параметров выделяется и освобождается 1С: Предприятием.
CallAsFunc
Выполняет метод с указанным порядковым номером. Если метод возвращает false, возникает ошибка времени выполнения и выполнение модуля 1С: Предприятия прекращается. Память для массива параметров выделяется 1С: Предприятием. Если возвращаемое значение имеет тип строка или двоичные данные, компонента выделяет память функцией AllocMemory менеджера памяти, записывает туда данные и сохраняет этот адрес в соответствующем поле структуры. 1С: Предприятие освободит эту память вызовом FreeMemory.
Полное описание методов, включая список параметров подробно описан в документации, поставляемой на диске ИТС.
Рассмотрим реализацию описанных выше методов.
В в коде компоненты определены два массива:
static wchar_t *g_MethodNames[] = {L"Enable", L"Disable", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadPicture"};
static wchar_t *g_MethodNamesRu[] = {L"Включить", L"Выключить", L"ПоказатьВСтрокеСтатуса", L"СтартТаймер", L"СтопТаймер", L"ЗагрузитьКартинку"};

и перечисление:
enum Methods
    { 
        eMethEnable = 0,
        eMethDisable,
        eMethShowInStatusLine,
        eMethStartTimer,
        eMethStopTimer,
        eMethLoadPicture,
        eMethLast      // Always last
    };

Они используются в функциях GetNMethodsFindMethod и GetMethodName, по аналогии с описанием свойств.
Методы GetNParamsGetParamDefValueHasRetVal реализуют switch, в зависимости от переданных параметров и логики приложения возвращают требуемое значение. Метод HasRetVal в своем коде имеет список только методов, которые могут возвращать результат. Для них он возвращает true. Для всехо стальных методов возвращается false.
Методы CallAsProc и CallAsFunc содержат непосредственно исполняемый код метода.
Для добавления метода, который может вызываться только как функция необходимо произвести следующие изменения в исходном коде внешней компоненты:
  1. Добавить имя метода в массивы g_MethodNames и g_MethodNamesRu (файл AddInNative.cpp)
  2. Добавить осмысленный идентефикатор метода в перечисление Methods (файл AddInNative.h)
  3. Внести изменения в код функции GetNParams в соответствии с логикой программы
  4. При необходимости внести изменения в код метода GetParamDefValue, если требуется использовать значения по умолчанию параметров метода.
  5. Внести изменения в функцию HasRetVal
  6. Внести изменения в логику работы функций CallAsProc или CallAsFunc, поместив туда непосредственно исполняемый код метода

Приведем массивы g_MethodNames и g_MethodNamesRu, а также перечисление Methods к виду:
static wchar_t *g_MethodNames[] = {L"Enable", L"Disable", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadPicture", L"Test"};
static wchar_t *g_MethodNamesRu[] = {L"Включить", L"Выключить", L"ПоказатьВСтрокеСтатуса", L"СтартТаймер", L"СтопТаймер", L"ЗагрузитьКартинку", L"Тест"};


 enum Methods
    {
        eMethEnable = 0,
        eMethDisable,
        eMethShowInStatusLine,
        eMethStartTimer,
        eMethStopTimer,
        eMethLoadPicture,
		eMethTest,
        eMethLast      // Always last
    };

Отредактируем функцию GetNProps, чтобы она возвращала количество параметров метода «Тест»:
long CAddInNative::GetNParams(const long lMethodNum)
{ 
    switch(lMethodNum)
    { 
    case eMethShowInStatusLine:
        return 1;
    case eMethLoadPicture:
        return 1;
    case eMethTest:
        return 2;
	default:
        return 0;
    }
return 0;
}

Внесем изменения в функцию CAddInNative::GetParamDefValue:
bool CAddInNative::GetParamDefValue(const long lMethodNum, const long lParamNum, tVariant *pvarParamDefValue)
{ 
    TV_VT(pvarParamDefValue)= VTYPE_EMPTY;
    switch(lMethodNum)
    { 
    case eMethEnable:
    case eMethDisable:
    case eMethShowInStatusLine:
    case eMethStartTimer:
    case eMethStopTimer:
	case eMethTest:
        // There are no parameter values by default 
        break;
    default:
        return false;
    }
    return false;
} 

Благодаря добавленной строке
case eMethTest:

в случае отсутствия одного или нескольких аргументов соответствующие параметры будут иметь пустое значение (VTYPE_EMPTY). Если необходимо наличие значения по умолчанию для параметра, следует задать его в секции eMethTest оператора switch функции CAddInNative::GetParamDefValue.
Так как метод «Тест» может возвращать значение, необходимо внести изменения в код функции HasRetVal:
bool CAddInNative::HasRetVal(const long lMethodNum)
{ 
    switch(lMethodNum)
    { 
    case eMethLoadPicture:
	case eMethTest:
        return true;
    default:
        return false;
    }
    return false;
}

И добавим исполняемый код метода в функцию CallAsFunc:
bool CAddInNative::CallAsFunc(const long lMethodNum,
                tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray)
{ 
...
std::wstring s1, s2;
    switch(lMethodNum)
    {
    case eMethLoadPicture:
	   ...
        break;
	case eMethTest:
        if (!lSizeArray || !paParams)
            return false;
	    s1 = (paParams) -> pwstrVal;
	    s2 = (paParams+1) -> pwstrVal;
	    wstring_to_p(std::wstring(s1+s2), pvarRetValue);
	    ret = true;
	    break;
    }
    return ret; 
}

Скомпилируем компоненту и приведем код конфигурации к виду:
перем ДемоКомп;
Процедура ПриНачалеРаботыСистемы()
	ПодключитьВнешнююКомпоненту("...", "DemoVK", ТипВнешнейКомпоненты.Native);
	ДемоКомп = Новый("AddIn.DemoVK.SomeName");
	пер = ДемоКомп.Тест("Привет, ", "Мир!");
	Сообщить(пер);
КонецПроцедуры 

После запуска конфигурации получим сообщение: «Привет, Мир!», что говорит о том, что метод отработал успешно.

Таймер


Задача:
  1. Изучить реализацию таймера в демонстрационной ВК
  2. Модифицировать метод «СтартТаймер», добавив возможность передавать в параметрах интервал срабатывания таймера (в миллисекундах)
  3. Убедиться в работоспособности произведенных изменений

В WinAPI для работы со временем можно воспользоваться сообщением WM_TIMER. Данное сообщение будет посылаться вашей программе через интервал времени, который вы зададите при создании таймера.
Для создания таймера используется функция SetTimer:
UINT SetTimer(HWND hWnd,              // описатель окна
              UINT nIDevent,          // идентификатор (номер) таймера
              UINT nElapse,           // задержка
              TIMERPROC lpTimerFunc); // указатель на функцию

Операционная система будет посылать сообщение WM_TIMER в программу с интервалом указанным в аргументе nElapse (в миллисекундах). В последнем параметре можно указать функцию, которая будет выполняться при каждом срабатывании таймера. Заголовок этой функции должен выглядеть так (имя может быть любым):
void __stdcall TimerProc (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)

Рассмотрим реализацию таймера в демонстрационной ВК.
Так как мы рассматриваем процесс разработки внешней компоненты для ОС семейства Windows, не будем рассматривать реализацию таймера в других операционных системах. Для ОС GNU/Linux, в частности, реализация будет отличаться синтаксисом функции SetTimer и TimerProc.
В исполняемом коде вызывается метод SetTimer, в который передается функция MyTimerProc:
m_uiTimer = ::SetTimer(NULL,0,100,(TIMERPROC)MyTimerProc);

Идентефикатор созданного таймера помещается в переменную m_uiTimer, чтобы в последствии его можно было отключить.
Функция MyTimerProc выглядит следующим образом:
VOID CALLBACK MyTimerProc(
  HWND hwnd,    // handle of window for timer messages
  UINT uMsg,    // WM_TIMER message
  UINT idEvent, // timer identifier
  DWORD dwTime  // current system time
)
{
    if (!pAsyncEvent)
        return;
    wchar_t *who = L"ComponentNative", *what = L"Timer";
    wchar_t *wstime = new wchar_t[TIME_LEN];
    if (wstime)
    {
        wmemset(wstime, 0, TIME_LEN);
        ::_ultow(dwTime, wstime, 10);
        pAsyncEvent->ExternalEvent(who, what, wstime);
        delete[] wstime;
    }
}

Суть функции сводится к тому, что вызывается метод ExternalEvent, который посылает сообщение системе «1С: Предприятие».
Для расширения функционала метода СтартТаймер произведем следующие действия:
Модифицируем код метода GetNParams так, чтобы он для метода eMethStartTimer возвращал значение 1:
case eMethStartTimer:
	return 1;

Приведем код метода CallAsProc к виду:
case eMethStartTimer:
	if (!lSizeArray || TV_VT(paParams) != VTYPE_I4 || TV_I4(paParams) <= 0)
		return false;
	pAsyncEvent = m_iConnect;
#ifndef __linux__
        m_uiTimer = ::SetTimer(NULL,0,TV_I4(paParams),(TIMERPROC)MyTimerProc);
#else
// код для GNU/Linux
#endif
	break;

Теперь проверим работоспособность. Для этого в модуле управляемого приложения конфигурации напишем код:
перем ДемоКомп;
Процедура ПриНачалеРаботыСистемы()
	ПодключитьВнешнююКомпоненту("...", "DemoVK", ТипВнешнейКомпоненты.Native);
	ДемоКомп = Новый("AddIn.DemoVK.SomeName");
	ДемоКомп.СтартТаймер(2000);
КонецПроцедуры  

После запуска конфигурации в программу будут поступать сообщения с интервалом в 2 секунды, что говорит о корректной работе таймера.

Взаимодействие с системой «1С: Предприятие»


Для взаимодействия между внешней компонентой и системой «1С: Предприятие» используются методы класса IAddInDefBase, описанного в файле AddInDefBase.h. Перечислим наиболее часто используемые:
Генерация сообщения об ошибке
virtual bool ADDIN_API AddError(unsigned short wcode, const WCHAR_T* source, const WCHAR_T* descr, long scode)

wcodescode — коды ошибки (список кодов ошибок с описанием можно найти на диске ИТС)
source — источник ошибки
descr — описание ошибки
Отправка сообщения системе «1С: Предприятие»
virtual bool ADDIN_API ExternalEvent(WCHAR_T* wszSource, WCHAR_T* wszMessage, WCHAR_T* wszData) = 0;

wszSource — источник сообщения
wszMessage — текст сообщения
wszData — передаваемые данные
Перехват сообщения осуществляется процедурой ОбработкаВнешнегоСобытия
Регистрация внешней компоненты в системе «1С: Предприятие»
virtual bool ADDIN_API RegisterProfileAs(WCHAR_T* wszProfileName)

wszProfileName — имя компоненты.
Этих методов достаточно для полноценного взаимодействия ВК и 1С. Для получения данных внешней компонентой от системы «1С: Предприятие» и наоборот внешняя компонента отправляет специальное сообщение, которое в свою очередь перехватывается системой «1С» и, при необходимости вызывает методы внешней компоненты для обратной передачи данных.

Тип данных tVariant


При обмене данными между внешней компонентой и системой «1С: Предприятие» используется тип данных tVariant. Он описан в файле types.h, который можно найти на диске с ИТС:
struct _tVariant 
{ 
    _ANONYMOUS_UNION union 
    { 
        int8_t         i8Val; 
        int16_t        shortVal; 
        int32_t        lVal; 
        int            intVal; 
        unsigned int   uintVal; 
        int64_t        llVal; 
        uint8_t        ui8Val; 
        uint16_t       ushortVal; 
        uint32_t       ulVal; 
        uint64_t       ullVal; 
        int32_t        errCode; 
        long           hRes; 
        float          fltVal; 
        double         dblVal; 
        bool           bVal; 
        char           chVal; 
        wchar_t        wchVal; 
        DATE           date; 
        IID            IDVal; 
        struct _tVariant *pvarVal; 
        struct tm      tmVal; 
        _ANONYMOUS_STRUCT struct 
        { 
            void*  pInterfaceVal; 
            IID        InterfaceID; 
        } __VARIANT_NAME_2/*iface*/; 
        _ANONYMOUS_STRUCT struct 
        { 
            char*        pstrVal; 
            uint32_t     strLen; //count of bytes 
        } __VARIANT_NAME_3/*str*/; 
        _ANONYMOUS_STRUCT struct 
        { 
            WCHAR_T*    pwstrVal; 
            uint32_t    wstrLen; //count of symbol 
        } __VARIANT_NAME_4/*wstr*/; 
    } __VARIANT_NAME_1; 
    uint32_t      cbElements;    //Dimension for an one-dimensional array in pvarVal 
    TYPEVAR       vt; 
}; 

Тип tVariant представляет из себя структру, которая включает себя:
  • смесь (union), предназначенную непосредственно для хранения данных
  • идентификатор типа данных

В общем случае работа с переменными типа tVariant происходит по следующему алгоритму:
  1. Определение типа данных, которые в данный момент хранятся в переменной
  2. Обращение к соответствующему полю смеси, для непосредственного доступа к данным

Использование типа tVariant значительно упрощает взаимодействие системы «1С: Предприятие» и внешней компоненты

Приложение


Каталог «examples» содержит примеры к статье
examples/1 — запуск демонстрационной компоненты
examples/2 — демонстрация расширения списка свойств
examples/3 — демонстрация расширения списка методов
Каждый каталог содержит проект VS 2008 и готовую конфигурацию 1C.
Скачать приложение

Комментариев нет:

Отправить комментарий