Технологія зовнішніх компонентів (). Технологія зовнішніх компонентів () Зовнішні компоненти «1C: Підприємство»

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 Завдання: Змінити ім'я зовнішньої частини довільне., зміст якого був пояснений. В даному випадку
- це найменування розширення.
У коді ВК визначено метод RegisterExtensionAs, що повертає системі «1С: Підприємство» ім'я, яке необхідне для подальшої реєстрації ВК у системі. Рекомендується вказувати ідентифікатор, який певною мірою розкриває суть зовнішньої компоненти.
Наведемо повний код методу

із зміненим найменуванням розширення:

bool CAddInNative::RegisterExtensionAs(WCHAR_T** wsExtensionName) ( wchar_t *wsExtension = L"SomeName"; int iActualSize = ::wcslen(wsExtension) + 1; WCHAR_T* dest = 0; ((void**)wsExtensionName, iActualSize * sizeof(WCHAR_T))) ::convToShortWchar(wsExtensionName, wsExtension, iActualSize); return false;
  1. У наведеному прикладі ім'я ВК змінено на
  2. SomeName
  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)); 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С на вигляд:
3
перем ДемоКомп; Процедура ПриПочаткуРоботиСистеми() ПідключитиЗовнішнюКомпоненту("...", "DemoVK", ТипЗовнішньоїКомпоненти.Native);
ДемоКомп = Новий ("AddIn.DemoVK.SomeName");
22

ДемоКомп.ПеревіркаТіпа = 1; Повідомити(Рядок(ДемоКомп.ПеревіркаТипу));ДемоКомп.Тест = "Вася";

Повідомити(Рядок(ДемоКомп.Тест));

Завдання:
  1. ДемоКомп.Тест = "Петя";
  2. Повідомити(Рядок(ДемоКомп.Тест));
  3. Повідомити(Рядок(ДемоКомп.ПеревіркаТипу)); КінецьПроцедури В результаті запуску отримаємо послідовність повідомлень:Вася
  4. Петро

Друге та третє повідомлення є результатом читання властивості, встановленого на попередньому кроці. Перше та друге повідомлення містять код типу останньої встановленої властивості. 3 відповідає цілісному значенню, 22 - рядковому. Відповідність типів та їх кодів встановлюється у файлі
types.h, , що знаходиться на диску ІТС., Розширення списку методів
Розширити функціонал зовнішньої компоненти наступним функціоналом:
Вивчити способи реалізації методів зовнішньої компоненти
Додати метод-функцію
Функц1
, яка як параметр приймає два рядки («Параметр1» та «Параметр2»). Як результат повертається рядок виду: «Перевірка. Параметр1, Параметр2»
Переконатися у працездатності здійснених змін
Повертає прапор наявності у методу із зазначеним порядковим номером значення, що повертається: 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);
Вони використовуються у функціях types.h, , що знаходиться на диску ІТС.ДемоКомп.Включити (); Розширення списку методів, За аналогією з описом властивостей.
Методи Вивчити способи реалізації методів зовнішньої компоненти, Функц1, Переконатися у працездатності здійснених змінреалізують switch, залежно від переданих параметрів та логіки програми повертають необхідне значення. Метод Переконатися у працездатності здійснених зміну своєму коді має лише список методів, які можуть повертати результат. Для них він повертає true. Для всіх сталевих методів повертається false.
Методи CallAsProcДемоКомп.Включити (); CallAsFuncмістять безпосередньо виконуваний код методу.
Для додавання методу, який може викликатися тільки як функція, необхідно зробити наступні зміни в вихідний кодзовнішньої компоненти:
  1. Додати ім'я методу до масивів g_MethodNamesДемоКомп.Включити (); g_MethodNamesRu(файл AddInNative.cpp)
  2. Додати осмислений ідентефікатор методу до переліку Methods (файл AddInNative.h)
  3. Внести зміни до коду функції Вивчити способи реалізації методів зовнішньої компонентивідповідно до логіки програми
  4. При необхідності внести зміни до коду методу Функц1, якщо потрібно використовувати значення стандартних параметрів методу.
  5. Внести зміни до функції Переконатися у працездатності здійснених змін
  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:)
Внесемо зміни до функції :
bool CAddInNative::GetParamDefValue(const long lMethodNum, const long lParamNum, tVariant *pvarParamDefValue) ( ​​TV_VT(pvarParamDefValue)= VTYPE_EMPTY; InStatusLine: case eMethStartTimer: case eMethStopTimer: case eMethTest: / / There are no parameter values ​​by default break;
Завдяки доданому рядку
case eMethTest:
у разі відсутності одного чи кількох аргументів відповідні параметри матимуть порожнє значення ( VTYPE_EMPTY). Якщо необхідно значення за промовчанням для параметра, слід задати його в секції eMethTestоператора switch функції CAddInNative::GetParamDefValue.
Оскільки метод «Тест» може повертати значення, необхідно внести зміни до коду функції Переконатися у працездатності здійснених змін:
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(LoMethodNum) ( case eMeMe if (!lSizeArray || !paParams) s1 = (paParams) -> pwstrVal; ; break;
Скомпілюємо компоненту і наведемо код конфігурації до виду:
перем ДемоКомп; Процедура ПриПочаткуРоботиСистеми() ПідключитиЗовнішнюКомпоненту("...", "DemoVK", ТипЗовнішньоїКомпоненти.Native);
ДемоКомп = Новий ("AddIn.DemoVK.SomeName");

пер = ДемоКомп.Тест("Привіт,", "Світ!");

bool CAddInNative::RegisterExtensionAs(WCHAR_T** wsExtensionName) ( wchar_t *wsExtension = L"SomeName"; int iActualSize = ::wcslen(wsExtension) + 1; WCHAR_T* dest = 0; ((void**)wsExtensionName, iActualSize * sizeof(WCHAR_T))) ::convToShortWchar(wsExtensionName, wsExtension, iActualSize); return false;
  1. Повідомити(пер); КінецьПроцедури
  2. Після запуску конфігурації отримаємо повідомлення: «Привіт, Світ!», Що говорить про те, що метод успішно відпрацював.
  3. Петро

Таймер Вивчити реалізацію таймера у демонстраційній ВК. Модифікувати метод "СтартТаймер", додавши можливість передавати в параметрах інтервал спрацьовування таймера (у мілісекундах)У WinAPI для роботи з часом можна скористатися повідомленням
WM_TIMER Це повідомлення:
буде надсилатися вашій програмі через інтервал часу, який ви поставите під час створення таймера.
Для створення таймера використовується функція Вивчити реалізацію таймера у демонстраційній ВК SetTimer UINT SetTimer(HWND hWnd, // описувач вікна UINT nIDevent, // ідентифікатор (номер) таймера UINT nElapse, // затримка TIMERPROC lpTimerFunc); // покажчик на функціюОпераційна система надсилатиме повідомлення
у програму з інтервалом зазначеним у аргументі
nElapse
(У мілісекундах). В останньому параметрі можна вказати функцію, яка виконуватиметься при кожному спрацюванні таймера. Заголовок цієї функції має виглядати так (ім'я може бути будь-яким): Це повідомленняДемоКомп.Включити (); void __stdcall TimerProc (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime).
Розглянемо реалізацію таймера у демонстраційній ВК. Це повідомленняТак як ми розглядаємо процес розробки зовнішньої компоненти для ОС сімейства Windows, не розглядатимемо реалізацію таймера в інших операційних системах. Для ОС GNU/Linux, зокрема, реалізація буде відрізнятися синтаксисом функції TimerProc:
У виконуваному коді викликається метод
, до якого передається функція MyTimerProc m_uiTimer = ::SetTimer(NULL,0,100,(TIMERPROC)MyTimerProc);
Ідентефікатор створеного таймера міститься у змінну TimerProc m_uiTimer
, щоб його можна було відключити.
Функція виглядає наступним чином: 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; w "ComponentNative", *what=L"Timer"; wchar_t *wstime = new wchar_t; if (wstime) , what, wstime);
Для розширення функціоналу методу та рядком лічильника системного годинника.зробимо такі дії:
Модифікуємо код методу Вивчити способи реалізації методів зовнішньої компонентитак, щоб він для методу 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
. Перерахуємо найчастіше використовувані:
Генерація повідомлення про помилку, віртуальний 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С» і, при необхідності, викликає методи зовнішньої компоненти для зворотної передачі даних.
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; uint8_t ui8Val; uint64_t ullVal; bVal; char chVal; wchar_t wD; struct (char * pstrVal; uint32_t strLen ; //count of bytes ) __VARIANT_NAME_3/*str*/; 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С Підприємства використовуються дві технології зовнішніх компонентів:

  • 1 За допомогою Native API
  • 2 З використанням технології COM
У цій статті я вирішив висвітлити роботу з компонентами Native API.
Отже, приступимо, від простого до складного:
Витяг з ІТС

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

Можливе використання в "Товстому Клієнті (звичайний додаток)";

Це найпростіший приклад роботи з компонентом Native. Слід звернути увагу, що компонент цього не вимагає реєстрацію у системі, що значно спрощує адміністрування.

2. Розглянутий вище приклад не життєвий. Найчастіше компонент розташовують у макеті. Макет повинен містити zip архів з файлами компонента та файлом MANIFEST.xml
Приклад файлу маніфесту:

3. При роботі в тонкому та web клієнті обов'язково використання методу.
Цитата з ІТС:

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

Приклад процедури встановлення зовнішнього компонента:

Встановити Зовнішню Компоненту- метод повинен викликатися лише за первинної установки компонента й у разі, коли необхідно оновити встановлену версію компонента.

У разі тонкого та товстого клієнта:
Достатньо повторно виконати операцію встановлення зовнішньої компоненти за допомогою методу Встановити Зовнішню Компоненту().

У випадку web клієнта для оновлення компонента:

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

Якщо компонент не було встановлено, то буде викликано виняток.

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

&НаКлієнті Перем АдресаАрхівуКомпонента; &НаКлієнті Перем Компонент; &На Клієнті Процедура ПриВідкритті(Відмова) // адреса, містить рядок (навігаційне посилання на двійкові дані zip архіву у // тимчасовому сховищі) КінецьПроцедури // ПриВідкритті() &НаСервері // методи ПідключитиЗовнішнюКомпоненту,ВстановитиЗовнішнюКомпоненту, можуть приймати // як перший параметр рядок у форматі "навігаційне посилання" // (URL до зовнішньої компоненти, упакованої в ZIP-архіву, у форматі ). Функція ОтриматиАдресАрхівуВчасномуСховище() Об'єктОбробки = РеквізитФормиЗначення("ОбробкаОб'єкт");

ПосиланняНаАрхів = ПоміститиВчаснеСховище(Об'єктОбробки.ОтриматиМакет("MIKO_phone_IP"), Новий УнікальнийІдентифікатор);

Повернення ПосиланняНаАрхів; КінецьФункції // ОтриматиАдресАрхівуВчасномуСховищі() &НаКлієнті //Процедура повинна викликатися лише один раз, у разі, якщо компонент ще не встановлений // або потребує оновлення Процедура ВстановитиКомпонент(Команда) Спроба ВстановитиЗовнішнюКомпоненту(АдресАррес)

На диску ІТС є повна документація щодо механізму зовнішніх компонентів, доповнена прикладом проекту та шаблоном для власної розробки. Матеріал так і називається "Технологія зовнішніх компонентів". Документація – це чудово, але в ній ще треба розібратися, а часу, як завжди, мало. Насправді існує всього кілька ключових моментів, на які варто звернути увагу, решта - тлін і суєта:)

Необхідні матеріали

Для створення зовнішньої компоненти нам знадобляться:

  1. Матеріал «Технологія створення зовнішніх компонентів», розташований на ІТС
  2. Порожній зовнішній компонент шаблон, що додається до матеріалу
  3. MS Visual Studio. Версія Express безкоштовна та більш ніж достатня для наших потреб.
  4. Наявність базових знань синтаксису C++, а саме:
  • Вміння відрізнити оголошення змінної від циклу чи умови
  • Розуміння того, що рядків у чистому вигляді в C++ не існує, є масиви, під які явно потрібно морочитися з пам'яттю
  • Ну і само собою, потрібно вміння реалізувати поставлене завдання зазначеною мовою. Як мінімум, уміння викликати з C++ якусь сторонню бібліотечку, яка сама все зробить.

Починаємо копати

Документація на Native API досить докладна. Якщо підвести резюме, вона говорить про таке:

  1. Зовнішній компонент дозволяє розширити вбудовану мову новим об'єктом (або декількома). Тобто. ми створимо якийсь клас, який зможемо створювати через оператор «Новий» та викликати методи цього об'єкта із вбудованої мови.
  2. Для того, щоб наш об'єкт працював, платформа «спілкуватиметься» з ним за певним протоколом, який ми зобов'язані забезпечити.
  3. Власне код компоненти умовно і двох частин: перша - реєстрація самої компоненти у системі, друга - робота нового класу та її взаємодію Космосу з платформою.

Ми не залізатимемо особливо реалізації, у нас терміни горять, та й компетенції обмаль. Нам потрібно швидко зрозуміти - в яке місце потрібно вписати свої рядки, щоб компонент запрацював. Для цього беремо шаблон компоненти з ІТС і відкриваємо його в Visual Studio. Шаблон знаходиться в папці template розпакованого архіву. Подивимося, що тут є.

Нас цікавить файл AddInNative.cpp. Вся реалізація закладена у ньому. Він містить заготівлі всіх необхідних методів, потрібно лише їх трохи налаштувати. Однак виявилося, що простіше взяти за основу не порожній шаблон, а розібратися з прикладом. У ньому є кілька корисних примочок, яких немає у порожньому шаблоні. Коли прийде розуміння – треба буде взяти порожній шаблон і вже зі знанням справи його доопрацювати. Приклад робочої компоненти розташований у папці example\NativeAPI, а порожній шаблон - у папці template.

Відкриємо проект із папки example і в ньому – файл AddInNative.cpp

На самому початку файлу розміщені оголошення констант та допоміжних функцій. Нас цікавлять такі рядки:

Наш об'єкт, як «справжній», буде підтримувати методи, написані як російською, так і англійською мовами. Для цього оголошено написання імен властивостей та методів двома мовами. Синя рамка – англійські терми, червона, відповідно – російські. На малюнку видно, що у прикладі вже реалізовано низку методів та властивостей. Наше завдання – їх прибрати та вставити свої.

Зеленою рамкою виділено рядок, у якому оголошено ім'я класу. Чесно зізнаюся, я не вникав, що воно означає. Якщо його змінити, то нічого не працює. Оскільки спочатку обмовилися, що я «чайник», то мені можна пробачити. :)

Таким чином, якщо наш об'єкт міститиме метод «Виконати Розрахунок» та властивість «Адресат», то нам потрібно описати це ім'я в масиві g_MethodNamesRu та g_PropNamesRu відповідно.

Виклики з мови 1С

Отже, наш об'єкт міститиме один метод та властивість, доступну для читання та запису.

Нехай буде наступний сценарій використання:

НашОб'єкт = Новий ("AddIn. MyComponent. DataSender"); НашОб'єкт. Адресат = somemail@server. com»;
НашОб'єкт. Виконати Розрахунок (СумаПлатежу, «За комунальні послуги»);

Є рядкове властивість та метод з числовим та рядковим параметром. Для того щоб все це запрацювало 1С виконує приблизно наступний протокол спілкування з компонентом:

Платформа викликає зумовлені функції нашому об'єкті, а він їй відповідає і виконує її команди. Аналогічна ситуація з методами, тільки там крім номера методу запитується кількість параметрів, наявність значення, що повертається, а також наявність необов'язкових параметрів.

Повернімося до нашого коду. Щоб уникнути «чарівних чисел» у класі CAddInNative оголошено два перерахування, що відповідають за визначення номерів методів та властивостей. Відкриємо файл CAddInNative.h і побачимо їх на самому початку:

У порожньому шаблоні цих перерахувань немає, а також немає прикладу для відділення викликів російською від виклику російською. Цей підхід є необов'язковим. Важливо дотримуватися інтерфейсу, а будуть перерахування чи ні - вирішувати вам.

Рядки Unicode

Багато хто, напевно, знає, що платформа оперує двобайтовими символами у форматі Unicode. У шаблоні оголошено спеціальний тип WCHAR_T. Цей тип є крос-платформною обгорткою та забезпечує однаковий розмір символу на Windows та на Linux. Стандартний тип wchar_t за розміром може відрізнятися різних системах. Зверніть також увагу, що всі рядкові літерали оголошуються з префіксом у вигляді літери L. Це означає, що такий рядок має тип wchar_t.

Є просте правило: всередині компоненти рядка обробляються як wchar_t (на Linux може бути 4 байти, у Windows - 2), але як тільки ми передаємо рядок в 1С або приймаємо його звідти, то потрібен WCHAR_T (суворо 2 байти на всіх системах).

Для перетворення одного типу рядків на інші в шаблоні передбачені допоміжні функції:

Перша - формує WCHAR_T із стандартного wchar_t:

uint32_t convToShortWchar(WCHAR_T** Dest, const wchar_t* Source, uint32_t len ​​= 0);

Друга – навпаки. Формує wchar_t із WCHAR_T.

uint32_t convFromShortWchar(wchar_t** Dest, const WCHAR_T* Source, uint32_t len ​​= 0);

При взаємодії з платформою завжди використовується лише WCHAR_T.

Тип Variant

Ще одна цікава річ – це універсальний тип даних Variant. Він дозволяє нам взаємодіяти з мовою 1С, яка, як відомо, не типізована і кожна змінна в ній може містити будь-що. Під час обміну значеннями використовується саме цей тип. Ми передаємо в метод Виконати Розрахунок двох параметрів - число і рядок. У компоненту "приїдуть" два значення Variant. На нас покладається обов'язок перевірити їхній дійсний тип. Ніхто не завадить передати в компонент не число, а скажімо, таблицю значень.

Хоча, схоже, помиляюся. Мені здається, що ТаблицюЗначень в NativeAPI передати таки не вийде, т.к. її немає у списку допустимих типів, але, проте, можна передати Дату замість Рядка. Це теж не є добре. Ми повинні перевірити реальний тип змінної, що приїхала з 1С.

Тип Variant влаштований просто. Це структура, властивостями якої є значення різних типів. Там є властивості типу DATE, wchar_t, int та інші. Головною частиною Variant є властивість «vt», яка зберігає справжній тип змінної, і за якою можна зрозуміти, як саме трактувати даний Variant. Крім того, оголошено низку допоміжних макросів, які спрощують роботу з типом Variant.

Ближче до діла

Начебто зі вступом все. Пропоную розглянути приклад реалізації зовнішньої компоненти. Як ТЗ виступить приклад компоненти з диска ІТС. Цей приклад описує такі можливості:

  • Виведення тексту у рядок стану головного вікна;
  • Здійснення зовнішньої події за таймером;
  • Передача двійкових даних у 1С: Підприємство;
  • Реалізація властивостей;
  • Реалізація процедур;
  • Реалізація функцій;

Компонент має наступний API:

  • Властивості:
    • Включено/IsEnabled;
    • Є Таймер/IsTimerPresent;
    • Методи:
      • Увімкнути/Enable;
      • Вимкнути/Disable;
      • ПоказатиВ рядкуСтатусу/ShowInStatusLine;
      • Включити Таймер/StartTimer;
      • Вимкнути Таймер/StopTimer;
      • ЗавантажитиЗображення/LoadPicture;

По таймеру виникає зовнішня подія, яку можна підписатися з коду 1С.

Керуючись знаннями, які ми маємо, розглянемо компоненту з самого початку.

Реєстрація компоненти

Наш об'єкт реалізується як окремого класу C++, у разі - CAddInNative. Щоб 1С змогла побачити наш клас, бібліотека dll має експортувати 3 функції:

  • GetClassObject
  • DestroyObject
  • GetClassNames

Ці експорти можна побачити у файлі AddInNative.def у дереві проекту VisualStudio. Подивимося на код цих функцій:

Найпростіша – функція GetClassNames – повідомляє платформі 1С які класи є в нашій компоненті. Нехай гуру C++ мене виправлять, мені здається, що тут потрібно відповісти платформі іменами класів C++, щоб вона могла їх до себе імпортувати. Саме для цього служить масив g_kClassNames, той самий, із зеленою «рамочкою». Спеціально не перевіряв, якщо потрібно просто змусити компоненту працювати, слід залишити все, як є в прикладі. Він і так робітник, не варто його колупати до певного часу.

Отже, GetClassNames повертає в платформу масив імен класів, що реалізують корисні об'єкти зовнішньої компоненти. У прикладі компонента поверне в платформу масив з одного елемента з ім'ям класу CAddInNative.

Зверніть увагу, до платформи піде значення типу WCHAR_T, а ім'я класу в масиві g_kClassNames має тип wchar_t. Тому виконується приведення за допомогою допоміжної функції, про яку йшлося вище.

Наступна функція – GetClassObject. Викликається, коли у коді підприємства ми написали «Новий». Платформа вимагає від нас створити новий екземпляр класу та повернути їй покажчик на новий об'єкт.

Знову ж таки, зверніть увагу, першим параметром платформа говорить нам - який саме клас створити (з тих, що дали їй методом GetClassNames). Оскільки у нас лише один клас, то це ім'я тут взагалі не перевіряється, просто створюється об'єкт через new та повертається через вихідний параметр pInterface.

І остання обов'язкова експортна функція – DestroyObject. Назва говорить сама за себе. Коли об'єкт платформі не потрібен, його потрібно видалити. Нам передається вказівник на раніше створений об'єкт. Звільняємо його за допомогою delete та обнулюємо непотрібні покажчики.

Описані реалізації досить універсальні. Якщо наша компонента реалізує лише один клас (як у прикладі), ці функції потрібно тупо скопіювати себе. Єдина умова - створити правильний клас функції GetClassObject, якщо у вас він називається не CAddInObject, а якось інакше.

Ініціалізація/завершення існування компоненти

Після створення класу, що реалізує компонент, платформа викликає методи цього класу. Перед початком роботи платформа повідомить нам об'єкт самої себе, з яким ми можемо викликати ті чи інші методи самої платформи. Відбувається це у методі Init. У прикладі об'єкт платформи зберігається у змінній m_iConnect.

Ще один важливий метод - setMemManager. Дозволяє виділяти блоки пам'яті, які звільнятиме сама платформа. Реалізується так:

Ми просто зберігаємо покажчик на менеджера пам'яті, який передає нам платформа. Потім цим менеджером ми виділятимемо пам'ять, що звільняється самою платформою.

І знову ж таки, як і у випадку з експортними функціями, методи ініціалізації досить універсальні, їх можна просто скопіювати до себе і не дбати про їхнє «допилювання», поки це не стане справді потрібно.

Корисне навантаження. Методи та властивості об'єкта компоненти

Реєстрація

Ну і зрозуміло, ми створювали компонент не заради її ініціалізації, а заради якогось корисного функціоналу. Настав час розглянути, як він реалізується.

По-перше, ми повинні зареєструвати об'єкт, який може бути створений та викликаний із мови 1С. Цей об'єкт реєструється у методі RegisterExtensionAs.

У цьому методі ми повідомляємо платформі ім'я нашого класу, як воно буде видно із мови 1С. Саме з цього імені ми його створюватимемо через «Новий». В даному випадку створення об'єкта буде виконуватися наступним кодом:

Підключити ЗовнішнюКомпоненту(Файл, "МояКомпонента", ТипЗовнішньоїКомпоненти. Native);
Об'єкт Компоненти = Новий( "AddIn.МояКомпонента.AddInNativeExtension");

Згідно з документацією, пам'ять для рядка з ім'ям класу виділяється менеджером пам'яті, і на цю адресу записується ім'я - "AddInNativeExtension". Тут можна безболісно написати своє ім'я. Зверніть увагу, знову відбувається перетворення з wchar_t на платформний WCHAR_T.

Використання

Як я писав вище, платформа опитує компоненту щодо різних мовних особливостей. Чи є вказана властивість, чи підтримує вона запис, чи є параметр функції значення за промовчанням, чи є значення, що повертається і т.п. Якщо взяти наведений раніше приклад коду:

НашОб'єкт = Новий( "AddIn.MyComponent.DataSender"); // DataSender - це ім'я з ф-ції RegisterExtensionAs (розглянута нижче).
НашОб'єкт. Адресат = " [email protected]" ;
НашОб'єкт. ВиконатиРозрахунок(СумаПлатежу, "За комунальні послуги");

то буде виконано наступне опитування:

  1. Чи є властивість «Адресат»
  2. Чи підтримує воно запис
  3. Чи є метод Виконати Розрахунок
  4. Скільки у нього параметрів
  5. Чи є у нього значення, що повертається
  6. Які умовчання у необов'язкових параметрів (якщо є)

Тут найкорисніше дивитися в приклад і звірятися з документацією. Реалізація всіх цих опитувань досить прямолінійна. За взаємодію відповідає цілий зоопарк методів. Все не розглядатиму, вони досить добре документовані, а крім того, реалізуються просто. Розглянуті будуть лише найзначніші моменти, в які нам, як «чайникам», треба буде залізти своїми ручками:). Основний підхід наступний: при першій згадці про ту чи іншу властивість або метод платформа попросить нас пошукати його на ім'я. Ми повинні будемо відповісти унікальним номером цієї властивості (методу). Все подальше спілкування відбуватиметься лише за номерами. Тут і допоможуть згадані перерахування, що зберігають ці номери.

Властивості

Перше, що варто розглянути – це інфраструктура властивостей. Платформа вимагає існування властивості методом FindProp

Платформа передає ім'я шуканого властивості як WCHAR_T. Воно перетворюється на wchar_t допоміжним методом, і відбувається пошук цього тексту спочатку в англомовних термах, а потім у російськомовних. Повернути ми маємо номер якості. Зверніть увагу, тут задіяно допоміжну функцію findName. Її реалізація є у прикладі, але немає у порожньому шаблоні компоненти. Здається доцільним перетягувати її до себе, якщо у компоненті плануються двомовні терми.

Далі, метод GetPropName виконує обернену задачу, отримує ім'я властивості за його номером. Рядок з ім'ям також виділяється через менеджер пам'яті підприємства. Підозрюю, що метод GetPropName спільно з GetNProps використовується, коли ми розгортаємо властивості об'єкта "плюсиком" у налагоджувачі. Тоді платформа отримає загальну кількість властивостей і кожного з них запросить ім'я.

Наступна пара методів IsPropReadable/IsPropWritable. Тут все просто, для зазначеного номера властивості ми повинні сказати, чи можна його читати/писати.

Отримання та запис значень виконуються методами GetPropVal/SetPropVal. Тут варто зупинитись докладніше. Ми починаємо працювати з типами 1С:Підприємства, а значить, на сцену виходить Variant.

Шаблон компоненти визначає набір допоміжних макросів для спрощення роботи з Variant. Перший - це перевірка типу значення. Наприклад, макрос TV_VT дозволяє перевірити/встановити тип значення. Визначено також іменовані константи для кожного з типів, що підтримуються. Ці константи та їх відповідності типу 1С:Підприємства перераховані в документації.

Макрос TV_BOOL отримує з варіанта булевого значення, з яким можна працювати. За аналогією виходять цілі значення (TV_INT), рядки (TV_WSTR) та інші. Точні значення є у коді, їх завжди можна переглянути.

Важливий момент - мало надати варіанту якесь значення, потрібно також надавати і дійсний тип. Зверніть увагу на GetPropVal. Крім присвоєння TV_BOOL = true йде присвоєння типу: TV_VT = VTYPE_BOOL. Якщо тип не привласнити, платформа не знатиме - який тип значення їй повернули. Зрозуміло, можна накосячіть і задати невірний тип. Часто це супроводжується падінням платформи.

Підіб'ємо підсумок вищесказаного:

Отримуємо значення з варіанта:

bool someVariable = TV_BOOL(pVariant);

Записуємо значення у варіант:

TV_VT(pVariant) = VTYPE_BOOL; // Тип даних, що діє

TV_BOOL(pVariant) = someBooleanVariable; // встановлюємо саме значення

А тепер – горбатий методи!

З методами трохи складніше, але загалом схоже на властивості. По-перше, є така сама функція пошуку методу по імені, отримання загальної кількості методів, отримання імені за номером. Однак крім перерахованого додаються такі особливості:

  • Якщо метод може повертати значення, це означає, що його можна використовувати в «Обчислити» і записувати праворуч від операції присвоєння в мові 1С. Якщо ні, то це процедура і подібні речі призводитимуть до виключення «Використання процедури як функції»
  • Метод має параметри. Платформа має знати їх кількість. Якщо під час виклику вказано аргументів більше, ніж у сигнатурі методу, виникає помилка «Занадто багато параметрів»
  • Якщо методу передано недостатню кількість аргументів, отже, деякі з них можуть бути необов'язковими, а якщо необов'язкових параметрів немає, виникає помилка «Недостатньо параметрів».
  • При виклику, якщо це процедура, то значення, що повертається, бути не може. Якщо це функція, тобто значення, що повертається. Його також потрібно обробити.

Є ряд простих методів, призначення яких зрозуміло з їх імен та документації. Сюди відносяться HasRetVal, GetNParams, GetParamDefValue. Їх пропоную не розглядати, прикладу більш ніж достатньо. Наш інтерес буде спрямований у бік безпосередньої реалізації корисного навантаження. Вона реалізується у методах CallAsProc та CallAsFunc. Перший відповідає за виклик процедур, другий – за виклик функцій. Вони відрізняються тим, що CallAsFunc має додатковий вихідний параметр, в якому ми передамо платформі значення функції, що повертається.

Виклик здійснюється наступним чином: платформа передає нам номер методу, що викликається, масив фактичних параметрів та їх кількість. Ми повинні проаналізувати номер методу та згодувати йому передані параметри. У разі функції ми повинні також записати щось у значення, що повертається.

У прикладі номер методу аналізується у switch/case та в залежності від номера виконується логіка методу. Для методів Увімкнути/Вимкнути просто встановлюється прапорець. Цікавий метод Показати В рядку Статусу. Він показує те, що йому передали у рядку стану вікна 1С:Підприємства. Для цього використовується об'єкт підключення до платформи m_iConnect, який був «виданий» нам при реєстрації компоненти. Повний перелік його можливостей описано у документації.

Цікавий момент. Тут, у прикладі, не перевіряється тип значення, що приїхав з 1С, а просто викликається SetStatusLine з рядковою частиною Variant. Я підозрюю, що якщо викликати метод компоненти з мови 1С, передавши туди число чи дату (замість рядка), то працювати нічого не буде… Знову ж таки, нехай гуру поправлять, але здається, що покажчик pwstrVal буде вказувати невідомо куди, якщо з підприємства приїхало скажімо, число, а чи не чесний рядок. При виклику SetStatusLine, платформа спробує прочитати з невідомої адреси рядок і швидше за все впаде. Найкраще перевіряти очікуваний тип. Мало чого.

Функція Завантажити Картинку в прикладі реалізована цікавіше, там розглянута можливість обміну з платформою рядками та двійковими даними.

По-перше, тут перевіряється кількість переданих параметрів. Якщо їх немає, то виклик визнається невдалим. Повертається false, що трактується платформою як помилка виклику.

Далі тут перевіряється тип переданого параметра. Якщо це вузька рядок (VTYPE_PSTR), використовується char-ова частина варіанта. У прикладі написано paParam-> pstrVal, але можна скористатися макросом TV_STR, буде те саме, але ще й дотримано однаковості роботи з варіантом.

Якщо це широкий рядок (VTYPE_PWSTR), то перетворення відбувається спочатку до wchar_t, а потім до char. Річ у тім, що з мови 1С у цей метод передається шлях до файлу, який потім використовують у функції fopen(char*). Ця функція на вхід вимагає тип char*, а з платформи до нас прийде WCHAR_T. Для коректної роботита виконуються перетворення рядків.

Та й останнє, якщо це взагалі не рядок, то виклик визнається невдалим, повертається false.

Ми виділяємо пам'ять під двійкові дані менеджером пам'яті. Це логічно, двійкові дані стануть повноцінним об'єктом усередині платформи, і розпоряджатися ними має саме вона. Пам'ять виділяється для варіанта pvarRetValue, що являє собою значення функції зовнішньої компоненти, що повертається.

У виділений буфер зчитується файл повністю, крім того, обов'язкововказується байтовий розмір як варіант варіанта strLen і тип даних варіанта VTYPE_BLOB. Якщо пам'ять виділиться успішно, то повертаємо true, як ознака успішного виклику всієї функції.

Таким чином, коли в мові 1С буде написано:

ДвійковіДані = Компонента. ЗавантажитиКартинку("C: pic.jpg");

буде викликаний метод CallAsFunc об'єкта компоненти з передачею шляху та поверненням двійкових даних, як описано вище.

У разі успіху змінна ДвійковіДані міститиме повноцінний об'єкт мови 1С. Коли він вийде з області видимості, вся зайнята ним пам'ять буде звільнена платформою. Саме тому вона виділялася через менеджер пам'яті.

Висновок

Розповідь писалася чайником для чайників, тому, швидше за все, рясніє термінологічними неточностями. Проте мета статті - швидке введення в зовнішні компоненти. Якщо в стислі терміни, без зайвих проблем, без тривалих розглядів необхідно швидко створити компоненту, то я сподіваюся, що ця стаття вам допоможе. Якщо від будь-яких моїх помилок вам, як гуру C++, стало погано - повідомляйте в коментарях, виправлятимемо.

Дякую за увагу.

Технологія зовнішніх компонентів дозволяє створювати програми (зовнішні компоненти), які динамічно підключатися і тісно взаємодіяти з системою 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.

ОЛЕГ ФІЛІПІВ, АНТ-Інформ, заступник начальника відділу розробки, [email protected]

Розширюємо функціональність 1С:Підприємства
Частина 1. Зовнішні компоненти COM

У житті кожного розробника 1С настає момент, коли завдання, перед ним поставлені, перевершують можливості платформи 1С. Розглянемо способи, як «подолати межу можливого»

Досвідчені розробники 1С вже, напевно, за першими трьома словами назви статті здогадалися, що йтиметься про зовнішні компоненти для 1С:Підприємства. Ця технологія існує ще з часів платформи 1С:Підприємство 7.7 (або раніше). Історично з'явилася в основному для вирішення завдань взаємодії з торговим обладнанням (сканерами ШК, електронними вагами, ККМ), у ході яких виникає необхідність обробки платформою 1С певних подій, що ініціюються обладнанням. Такі події є чим іншим, як послідовностями байт, що надходять на COM/LPT-порти. Звичайно, адаптувати платформу 1С для роботи з такими низькорівневими механізмами було б не дуже правильно, тому вигадали технологію зовнішніх компонентів для 1С:Підприємства.

У цьому циклі статей йтиметься не лише про зовнішні компоненти: зовнішні компоненти – досить потужний і складний механізм, тому їх використання для вирішення деяких завдань бачиться як «стрільба з гармати по горобцях». Проте зовнішні компоненти є найбільш універсальним засобомдля вирішення різноманітних завдань, які виходять за рамки платформи 1С:Підприємство, тому почнемо ми саме з них. У статті буде розглянуто найстарішу, часто використовувану та перевірену часом технологію створення зовнішніх компонент – на основі COM.

Що таке зовнішні компоненти

Зовнішні компоненти, як було зазначено вище, з'явилися в 1С:Підприємстві ще з версії 7.7. Спочатку за традицією розробники платформи 1С «не морочилися», і зовнішні компоненти являли собою об'єкт з певними обов'язковими властивостями та методами. У тому вигляді компоненти існують і донині. Тобто компоненти, написані для 1С: Підприємства 7.7, (теоретично) працюватимуть і з 1С: Підприємством 8.3.

На практиці існує ряд тонкощів: якщо зовнішня компонента активно взаємодіє з самою платформою, її функції будуть спеціалізованими для конкретної версії.

З версії 8.2 в 1С:Підприємстві з'явилася нова технологія розробки зовнішніх компонентів, так звана NativeAPI. Компоненти, написані за цією технологією, не є COM-об'єктами. З одного боку, це плюс - ці компоненти не вимагають реєстрації, з іншого, використання їх ще де-небудь, крім платформи 1С:Підприємство, неможливо. Дещо складніше стала розробка зовнішніх компонентів, трохи Велика кількістьобов'язкових інтерфейсів вони мають підтримувати. Але про неї йтиметься у наступній статті.

Зовнішні компоненти та технологія COM

Докладно описувати саму технологію COM не буду, тому що літератури на цю тему маса. Варто лише, напевно, сказати, що багато хто «плутає» створення звичайного inproc-сервера зі створенням зовнішніх компонентів для 1С:Підприємства, але це недалеко від істини. Дуже часто можна обійтися лише цим функціоналом. Як створювати COM-об'єкт на Visual Studio, описано багато різних джерел. Зокрема, людина детально написав, як це робиться, з прикладом виклику з 1С:Підприємства.

Якщо ж хочеться все-таки зробити повноцінну COM зовнішню компоненту для 1С:Підприємства (з варіантів, навіщо це може знадобитися, можу придумати лише один – компонента має активно взаємодіяти з системою на платформі 1С, оповіщати користувачів, змінювати рядок статусу, відображати діалогові вікна та т.п.), то треба скористатися безпосередньо технологією зовнішніх компонентів. Отже, почнемо.

Поділитися