Технология создания внешних компонент 1с. Технология внешних компонентов (). Тип данных tVariant

Эта статья посвящена работе с внешними компонентами, а именно: их подключению. На данный момент для расширения возможностей 1С Предприятия используются две технологии внешних компонент:

  • 1 С использованием Native API
  • 2 С использованием технологии COM
В этой статье я решил осветить работу с компонентами Native API.
Итак, приступим, от простого к сложному:
Выдержка из ИТС

1. Допустим ВК у нас расположена в определенном каталоге на диске:

Возможно использование в "Толстом Клиенте (обычное приложение)";

Это самый простой пример работы с компонентой Native. Следует обратить внимание на то, что компонент этого типа не требует регистрацию в системе, что значительно упрощает администрирование.

2. Рассмотренный выше пример совсем не жизненный. Чаще всего компонент располагают в макете. Макет должен содержать zip архив c файлами компонента и файлом MANIFEST.xml
Пример файла манифеста:

3. При работе в тонком и web клиенте обязательно использования метода .
Цитата с ИТС:

пояснение:
%APPDATA%\1C\1Cv82\ExtCompT - каталог установки компонентов для Толстого, Тонкого клиентов.
%APPDATA%\Roaming\Mozilla\Extensions - каталог (в моем случае) расширений для Mozilla FF/
При использовании метода УстановитьВнешнююКомпоненту() , в зависимости от используемого клиента, в соответствующий каталог будут распакованы расширения.

Пример процедуры установки внешнего компонента:

УстановитьВнешнююКомпоненту - метод должен вызываться лишь при первичной установке компонента и в случае, когда необходимо обновить установленную версию компонента.

В случае тонкого и толстого клиента:
Достаточно повторно выполнить операцию установки внешней компоненты с помощью метода УстановитьВнешнююКомпоненту ().

В случае web клиента для обновления компонента:

  • Необходимо удалить плагин через механизм работы с дополнениями веб-браузера (Mozilla FF)
  • Воспользоваться методом УстановитьВнешнююКомпоненту
Для подключения ВК можно использовать следующую процедуру:

Если компонент не был установлен, то будет вызвано исключение.

2. бывают случаи, когда компонент необходимо установить из временного хранилища (файл получен со стороннего источника, внешняя обработка), в этом случае необходимо первым параметров в методы ПодключитьВнешнююКомпоненту и УстановитьВнешнююКомпоненту адрес архива во временном хранилище. Ниже приведу возможный пример работы:

&НаКлиенте Перем АдресАрхиваКомпонента; &НаКлиенте Перем Компонент; &НаКлиенте Процедура ПриОткрытии(Отказ) // адрес, содержит строку (навигационную ссылку на двоичные данные zip архива во // временном хранилище) АдресАрхиваКомпонента = ПолучитьАдресАрхиваВоВременномХранилище(); КонецПроцедуры // ПриОткрытии() &НаСервере // методы ПодключитьВнешнююКомпоненту,УстановитьВнешнююКомпоненту, могут принимать в // качестве первого параметра строку в формате "навигационная ссылка" // (URL к внешней компоненте, упакованной в ZIP-архив, в формате, аналогичном // ПолучитьНавигационнуюСсылку). Функция ПолучитьАдресАрхиваВоВременномХранилище() ОбъектОбработки = РеквизитФормыВЗначение("ОбработкаОбъект"); СсылкаНаАрхив = ПоместитьВоВременноеХранилище(ОбъектОбработки.ПолучитьМакет("MIKO_phone_IP"), Новый УникальныйИдентификатор); Возврат СсылкаНаАрхив; КонецФункции // ПолучитьАдресАрхиваВоВременномХранилище() &НаКлиенте // Процедура должна вызываться лишь один раз, в случае, если компонент еще не установлен // или нуждается в обновлении Процедура УстановитьКомпонент(Команда) Попытка УстановитьВнешнююКомпоненту(АдресАрхиваКомпонента); Исключение Сообщить("Не удалось устанвить внешнюю компоненту."); КонецПопытки; КонецПроцедуры // УстановитьКомпонент() &НаКлиенте // основная процедура инициализации комопнента Процедура Инициализаровать(Команда) Попытка ПодключитьВнешнююКомпоненту(АдресАрхиваКомпонента,"Comp" ,ТипВнешнейКомпоненты.Native); Компонент = Новый ("AddIn.Comp.MIKO_phone_IP"); Исключение Сообщить("Исключение при инициализации. Возможно компонент еще не был установлен."); КонецПопытки; КонецПроцедуры

ansmirnov 22 августа 2013 в 14:12

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

  • Программирование ,
  • C++
  • Tutorial

Введение

Эта статья дает представление о работе внешних компонент в системе «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: Предприятие» с параметрами Component , Timer и строкой счетчика системных часов.
    Методы: СтартТаймер , СтопТаймер
    Свойства: ЕстьТаймер
  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. Добавить свойство строкового типа, доступное для чтения и записи, которое хранит тип данных последнего установленного свойства. При установке значения свойства никаких действий не производится

Для определения свойств создаваемой компоненты разработчику необходимо реализовать следующие методы в коде библиотеки 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:
GetNMethods , FindMethod , GetMethodName
Предназначены для получения соответственно количества методов, поиска номера и имени метода. Аналогичны соответствующим методам для свойств
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 };
Они используются в функциях GetNMethods , FindMethod и GetMethodName , по аналогии с описанием свойств.
Методы GetNParams , GetParamDefValue , HasRetVal реализуют 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; }
Внесем изменения в функцию :
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; 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)
wcode , scode - коды ошибки (список кодов ошибок с описанием можно найти на диске ИТС)
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.
  • Tutorial

Введение

Эта статья дает представление о работе внешних компонент в системе «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: Предприятие» с параметрами Component , Timer и строкой счетчика системных часов.
    Методы: СтартТаймер , СтопТаймер
    Свойства: ЕстьТаймер
  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. Добавить свойство строкового типа, доступное для чтения и записи, которое хранит тип данных последнего установленного свойства. При установке значения свойства никаких действий не производится

Для определения свойств создаваемой компоненты разработчику необходимо реализовать следующие методы в коде библиотеки 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:
GetNMethods , FindMethod , GetMethodName
Предназначены для получения соответственно количества методов, поиска номера и имени метода. Аналогичны соответствующим методам для свойств
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 };
Они используются в функциях GetNMethods , FindMethod и GetMethodName , по аналогии с описанием свойств.
Методы GetNParams , GetParamDefValue , HasRetVal реализуют 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; }
Внесем изменения в функцию :
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; 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)
wcode , scode - коды ошибки (список кодов ошибок с описанием можно найти на диске ИТС)
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.

Технология внешних компонентов позволяет создавать программы (внешние компоненты), которые будут динамически подключаться и тесно взаимодействовать с системой 1С:Предприятие 8, расширяя ее возможности. Данная технология позволяет подключать к системе 1С:Предприятие 8 различное торговое оборудование: сканеры штрих-кодов, принтеры этикеток и т.д.

Native API

Для создания внешних компонентов используется технология Native API – собственный интерфейс системного программирования 1С:Предприятия 8. Она поддерживает операционные системы Windows и Linux, и дает возможность создавать внешние компоненты, работающие как под одной, так и под другой операционной системой. Компоненты, созданные по технологии Native API, могут быть подключены в толстом клиенте, в тонком клиенте, в веб-клиенте, внешнем соединении и в сервере приложений.

Расширение встроенного языка

Внешние компоненты позволяют расширять встроенный язык новыми объектами. Структуры механизмов внешних компонент максимально приближены к внутренним структурам системы 1С:Предприятие 8, что повышает эффективность работы.

Вызов процедуры обработки событий, контролируемых внешней компонентой

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

Добавление страницы свойств в параметры «1С:Предприятие 8»

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

Сохранение параметров компоненты через механизм сохранения параметров «1С:Предприятие 8»

При сохранении своих параметров внешняя компонента может использовать механизмы системы 1С:Предприятие 8.

Доступ к строке состояния

При работе состояние внешней компоненты может отражаться в панели состояния системы 1С:Предприятие 8.