Технология на външни компоненти (). Технология на външни компоненти () Външни компоненти "1C: Enterprise"

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

Външни компоненти в 1C 8.2

  • програмиране,
  • C++
  • урок

Въведение

Тази статия дава представа как работят външните компоненти в системата 1C:Enterprise.
Ще бъде показан процесът на разработване на външен компонент за системата 1C:Enterprise версия 8.2, работеща под операционна система Windows с файлова версия на работа. Тази опция се използва в повечето решения, предназначени за малки фирми. VC ще бъде реализиран на езика за програмиране C++.

Външни компоненти "1C: Enterprise"

"1C: Enterprise" е разширяема система. За разширяване на функционалността на системата се използват външни компоненти (VC). От гледна точка на разработчика, VC е външен обект, който има свойства и методи и може също да генерира събития за обработка от системата 1C:Enterprise.
Външни компоненти могат да се използват за решаване на клас задачи, които са трудни или дори невъзможни за изпълнение с помощта на езика за програмиране, вграден в 1C: Enterprise. По-специално, този клас включва задачи, които изискват взаимодействие на ниско ниво с операционната система, например за работа със специфичен хардуер.
Системата 1C:Enterprise използва две технологии за създаване на външни компоненти:
  • използвайки Native API
  • с помощта на COM технология
Като се имат предвид ограниченията между двете горни технологии, разликата е незначителна, така че ще разгледаме разработването на VK с помощта на Native API. Ако е необходимо, внедрените разработки могат да бъдат приложени към разработването на VC с помощта на COM технологията, а също и с незначителни модификации да се прилагат за използване в системата 1C: Enterprise с други опции за работа, различни от файловия режим на работа.
VK структура
Външният компонент на системата 1C:Enterprise е представен като DLL библиотека. Кодът на библиотеката описва производния на IComponentBase клас. В създавания клас трябва да се дефинират методите, отговорни за изпълнението на функциите на външния компонент. Заменените методи ще бъдат описани по-подробно по-нататък в хода на изложението на материала.

Стартиране на демото VK

Задача:
  1. Сглобете външния компонент, доставен с абонамента за ITS и предназначен да демонстрира основните възможности на механизма на външния компонент в 1C
  2. Свържете демонстрационния компонент към конфигурацията на 1C
  3. Уверете се, че декларираните функции работят правилно
Компилация
Демонстрационният VC се намира на ITS абонаментния диск в директорията "/VNCOMP82/example/NativeAPI".
Ще използваме Microsoft Visual Studio 2008, за да сглобим демонстрационния VK.Другите версии на този продукт не поддържат използвания формат на проекта Visual Studio.


Отворете проекта AddInNative. В настройките на проекта свързваме директорията със заглавните файлове, необходими за изграждането на проекта. По подразбиране те се намират на ITS диска в директорията /VNCOMP82/включва.
Резултатът от изграждането е файл /bind/AddInNative.dll. Това е компилираната библиотека за свързване към конфигурацията на 1C.
Свързване на VK към 1C конфигурация
Нека създадем празна конфигурация 1C.
Следва кодът за модула на управляваното приложение.
var DemoComp; SystemStartup Procedure() ConnectExternalComponent("...\bind\AddInNative.dll", "DemoVK", ExternalComponentType.Native); DemoComp = New("AddIn.DemoVK.AddInNativeExtension"); EndProcedure
Ако не е отчетена грешка при стартиране на конфигурацията на 1C, тогава VK е свързан успешно.
В резултат на изпълнението на горния код се появява обект в глобалната видимост на конфигурацията DemoCompТова има свойства и методи, които са дефинирани във външния bean код.
Демонстрация на вградената функционалност
Нека проверим ефективността на демото VK. За да направите това, нека се опитаме да зададем и прочетем някои свойства, да извикаме някои VK методи и също да получим и обработим VK съобщение.
Документацията, предоставена на ITS диска, посочва следната функционалност на демонстрационния VC:
  1. Управление на състоянието на компонентен обект
    Методи: Включи, Изключвам
    Имоти: Включени
  2. Управление с таймер
    Всяка секунда компонентът изпраща съобщение до системата 1C: Enterprise с параметри Компонент, таймери низ от брояч на системния часовник.
    Методи: StartTimer, StopTimer
    Имоти: Има таймер
  3. Метод ShowInStatusLine, който показва в лентата на състоянието текста, предаден на метода като параметри
  4. Метод Качи изображение. Зарежда изображение от посочения файл и го прехвърля в системата 1C:Enterprise като двоични данни.
Нека се уверим, че тези функции работят. За да направим това, ще изпълним следния код:
var DemoComp; SystemStart() Процедура ConnectExternalComponent(...); DemoComp = New("AddIn.DemoVK.AddInNativeExtension"); DemoComp.Disable(); Известяване (DemoComp. Активиран); DemoComp.Enable(); Известяване (DemoComp. Активиран); DemoComp.StartTimer(); EndProcedure ProcedureExternalEventHandler(Източник, Събитие, Данни) Доклад(Източник + " " + Събитие + " " + Данни); EndProcedure
Резултатът от изпълнението на конфигурацията е показан на изображението


Резултатите от извикванията на метода се показват в панела "Съобщения". DemoComp.Disable()И Demo.Comp.Enable(). Следващите редове на същия панел съдържат резултатите от обработката на съобщения, получени от VK - Източник, СъбитиеИ Даннисъответно.

Произволно име на външен компонент

Задача: Променете името на външния компонент на произволно.
Предишният раздел използва идентификатора AddInNativeExtension, чието значение не беше обяснено. В такъв случай AddInNativeExtensionе името на разширението.
VC кодът дефинира метод RegisterExtensionAs, който връща името на системата 1C: Enterprise, което е необходимо за последващата регистрация на VC в системата. Препоръчва се да се посочи идентификатор, който до известна степен разкрива същността на външния компонент.
Ето пълния код на метода 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; )
В горния пример името на VK е променено на Някакво име. След това, когато свързвате VK, трябва да посочите ново име:
DemoComp = New("AddIn.DemoVK.SomeName");

Разширяване на списъка с VK имоти

Задача:
  1. Проучете изпълнението на VC свойства
  2. Добавете свойство за четене-запис от тип низ
  3. Добавете свойство тип низ за четене/запис, което съхранява типа данни на последния набор от свойства. При задаване на стойността на свойството не се предприемат действия

За да дефинира свойствата на създавания компонент, разработчикът трябва да приложи следните методи в кода на библиотеката AddInNative.cpp:
GetNProps
Връща броя свойства за това разширение, 0, ако няма свойства
FindProp
Връща поредния номер на свойството, чието име е подадено в параметрите
GetPropName
Връща името на свойството по неговия ред и по подаден идентификатор на език
GetPropVal
Връща стойността на свойството с посочения реден номер
SetPropVal
Задава стойността на свойството с посочения ред
IsPropReadable
Връща флага за четимост на свойството с посочения ред
IsPropWritable
Връща флага за запис на свойството с посочения пореден номер


Помислете за изпълнението на горните методи на класа CAddInNative.
Демонстрационният VC дефинира 2 свойства: ВключениИ Има таймер (е активираноИ IsTimerPresent).
Два масива са дефинирани в глобалния обхват на кода на библиотеката:
статичен wchar_t *g_PropNames = (L"IsEnabled", L"IsTimerPresent"); static wchar_t *g_PropNamesRu = (L"Активирано", L"Има таймер");
които съхраняват руските и английските имена на имотите. В заглавния файл AddInNative.hдефинира се enum:
enum Props ( ePropIsEnabled = 0, ePropIsTimerPresent, ePropLast // Винаги последен );
ePropIsEnabledИ ePropIsTimerPresent, съответно със стойности 0 и 1, се използват за замяна на поредните номера на свойствата със смислени идентификатори. ePropLast, който има стойност 2, се използва за получаване на броя свойства (чрез метода GetNProps). Тези имена се използват само в кода на компонента и не са достъпни отвън.
Методите FindProp и GetPropName търсят в масиви g_PropNamesИ g_PropNames.
За да съхранява стойността на полетата в библиотечния модул, класът CAddInNative има свойства, които съхраняват стойността на свойствата на компонента. Методи GetPropValИ SetPropValсъответно връща и задава стойността на тези свойства.
Методи IsPropReadableИ IsPropWritableи обратно вярноили невярно, в зависимост от предавания порядък на свойството според логиката на приложението.
За да добавите персонализирано свойство:

  1. Добавете името на свойството, което да се добави към масивите g_PropNamesИ g_PropNames(файл AddInNative.cpp)
  2. Към изброяване реквизит(файл AddInNative.h) преди ePropLastдобавете име, което уникално идентифицира собствеността, която ще бъде добавена
  3. Организирайте паметта за съхраняване на стойностите на свойствата (създайте полета на компонентен модул, които съхраняват съответните стойности)
  4. Направете промени в методите GetPropValИ SetPropValза взаимодействие с паметта, разпределена в предишната стъпка
  5. В съответствие с логиката на приложението, направете промени в методите IsPropReadableИ IsPropWritable
Точки 1, 2, 5 не се нуждаят от обяснение. Подробности за изпълнението на тези стъпки можете да намерите в приложението към статията.
Нека назовем свойствата на теста ТестИ Проверка на типасъответно. Тогава, в резултат на параграф 1, имаме:
статичен wchar_t *g_PropNames = (L"IsEnabled", L"IsTimerPresent", L"Test", L"TestType"); статичен wchar_t *g_PropNamesRu = (L"Enabled", L"HasTimer", L"Test", L"CheckType");
Изброяване реквизитще изглежда така:
enum Props ( ePropIsEnabled = 0, ePropIsTimerPresent, ePropTest1, ePropTest2, ePropLast // Винаги последен);
За значително опростяване на кода ще използваме STL C++. По-специално за работа с низове WCHAR, свържете библиотеката wstring.
За съхраняване на стойността на метод Тест, дефинирайте в класа CAddInNativeчастно поле в обхват:
низ тест1;
За прехвърляне на параметри на низ между 1C:Enterprise и външен компонент се използва мениджърът на паметта 1C:Enterprise. Нека да разгледаме по-отблизо работата му. За да разпределите и освободите съответно памет, използвайте функциите Разпределяне на паметтаИ FreeMemory, дефинирани във файла MemoryManager.h. Ако е необходимо да се предаде параметър на низ към системата 1C: Enterprise, външният компонент трябва да разпредели памет за него, като извика функцията Разпределяне на паметта. Прототипът му изглежда така:
виртуален 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(); върне вярно;)
След това съответния case раздел на оператора switch на метода GetPropValще приеме формата:
case ePropTest1: wstring_to_p(test1, pvarPropVal); прекъсване;
метод SetPropVal:
case ePropTest1: ако (TV_VT(varPropVal) != VTYPE_PWSTR) връща невярно; test1 = std::wstring((wchar_t*)(varPropVal -> pstrVal)); прекъсване;
За да реализираме второто свойство, ние дефинираме поле за клас CaddInNative
uint8_t последен_тип;
в който ще съхраняваме типа на последната предадена стойност. За да направите това, добавете следната команда към метода CaddInNative::SetPropVal:
last_type = TV_VT(varPropVal);
Сега, когато поискаме да прочетем стойността на второто свойство, ще върнем стойността последен_тип, което се изисква от определената задача.
Нека проверим ефективността на направените промени.
За да направите това, привеждаме външния вид на конфигурацията 1C във формата:
var DemoComp; SystemStartup Procedure() ConnectExternalComponent("...", "DemoVK", ExternalComponentType.Native); DemoComp = New("AddIn.DemoVK.SomeName"); DemoComp.CheckType = 1; Доклад(низ(DemoComp.TypeCheck)); DemoComp.Test = "Вася"; Доклад(Низ(ДемоКомп.Тест)); DemoComp.Test = "Петя"; Доклад(Низ(ДемоКомп.Тест)); Доклад(низ(DemoComp.TypeCheck)); EndProcedure
В резултат на стартирането получаваме поредица от съобщения:
3
Вася
Петър
22

Второто и третото съобщение са резултат от четене на свойството, зададено в предишната стъпка. Първото и второто съобщение съдържат кода на типа на последния набор от свойства. 3 съответства на целочислена стойност, 22 - на низова стойност. Във файла се установява съответствието на видовете и техните кодове типове.ч, който се намира на ITS диска.

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

Задача:
  1. Разширете функционалността на външния компонент със следната функционалност:
  2. Научете как да прилагате методи на външни компоненти
  3. Добавете метод-функция Функция1, който приема два низа ("Параметър1" и "Параметър2") като параметър. В резултат на това се връща низ от формата: „Проверете. Параметър1, Параметър2"
  4. Проверете дали направените промени работят.

За да дефинира методите на създавания компонент, разработчикът трябва да внедри следните методи в кода на библиотеката AddInNative:
GetNMethods, FindMethod, GetMethodName
Проектиран да получи съответно броя на методите, търсене на номера и името на метода. Подобно на съответните методи за свойства
GetNParams
Връща броя на параметрите на метода с посочения пореден номер; ако няма метод с това число или няма параметри, връща 0
GetParamDefValue
Връща стойността по подразбиране на посочения параметър на посочения метод
HasRetVal
Връща флаг, който методът с посочената върната стойност ординал има: вярно за методи с върната стойност и невярнов противен случай
CallAsProc
невярно, възниква грешка по време на изпълнение и изпълнението на модула 1C: Enterprise спира. Паметта за масива от параметри се разпределя и освобождава от 1C: Enterprise.
CallAsFunc
Изпълнява метода с посочения ред. Ако методът се върне невярно, възниква грешка по време на изпълнение и изпълнението на модула 1C: Enterprise спира. Паметта за масива от параметри се разпределя от 1C: Enterprise. Ако върнатата стойност е от тип низ или двоични данни, компонентът разпределя памет с функцията Разпределяне на паметтамениджър на паметта, записва данни там и съхранява този адрес в съответното поле на структурата. 1С: Предприятието ще освободи тази памет чрез повикване FreeMemory.
Пълно описание на методите, включително списък с параметри, е описано подробно в документацията, предоставена на ITS диска.
Помислете за прилагането на описаните по-горе методи.
В кода на компонента са дефинирани два масива:
статичен wchar_t *g_MethodNames = (L"Активиране", L"Деактивиране", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadPicture"); статичен wchar_t *g_MethodNamesRu = (L"Активиране", L"Деактивиране", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadImage");
и enum:
enum Методи ( eMethEnable = 0, eMethDisable, eMethShowInStatusLine, eMethStartTimer, eMethStopTimer, eMethLoadPicture, eMethLast // Винаги последен);
Те се използват във функции GetNMethods, FindMethodИ GetMethodName, по аналогия с описанието на имотите.
Методи GetNParams, GetParamDefValue, HasRetVal implement switch, в зависимост от предадените параметри и логиката на приложението, връща необходимата стойност. Метод HasRetValв кода си има списък само с методи, които могат да върнат резултат. За тях се връща вярно. За всички стоманени методи за връщане невярно.
Методи CallAsProcИ CallAsFuncсъдържат директно изпълним код на метода.
За да добавите метод, който може да бъде извикан само като функция, трябва да направите следните промени в изходния код на външния компонент:
  1. Добавете име на метод към масиви g_MethodNamesИ g_MethodNames(файл AddInNative.cpp)
  2. Добавете смислен идентификатор на метод към списъка Methods (файл AddInNative.h)
  3. Направете промени в кода на функцията GetNParamsспоред програмната логика
  4. Ако е необходимо, направете промени в кода на метода GetParamDefValueако искате да използвате стойностите по подразбиране на параметрите на метода.
  5. Направете промени във функция HasRetVal
  6. Направете промени в логиката на функциите CallAsProcили CallAsFunc, като поставите директно изпълнимия код на метода там
Да донесем масиви g_MethodNamesИ g_MethodNames, както и изброяване методикъм гледката:
статичен wchar_t *g_MethodNames = (L"Активиране", L"Деактивиране", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadPicture", L"Test"); статичен wchar_t *g_MethodNamesRu = (L"Активиране", L"Деактивиране", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadImage", L"Тест");

Методи Enum ( eMethEnable = 0, eMethDisable, eMethShowInStatusLine, eMethStartTimer, eMethStopTimer, eMethLoadPicture, eMethTest, eMethLast // Винаги последен);
Нека редактираме функцията 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 eMethShowInStatus Ред: случай eMethStartTimer: случай eMethStopTimer: случай eMethTest : // Няма стойности на параметри по подразбиране break; по подразбиране: return false; ) return false; )
Благодарение на добавената линия
случай eMethTest:
при липса на един или повече аргументи, съответните параметри ще имат празна стойност ( VTYPE_EMPTY). Ако имате нужда от стойност по подразбиране за параметър, трябва да я посочите в секцията eMethTestоператор за превключване на функции 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) връща false; s1 = (paParams) -> pwstrVal; s2 = (paParams+1) -> pwstrVal; wstring_to_p(std::wstring(s1+s2), pvarRetValue); ret = true ; break; ) return ret; )
Нека компилираме компонента и пренесем конфигурационния код във формата:
var DemoComp; SystemStartup Procedure() ConnectExternalComponent("...", "DemoVK", ExternalComponentType.Native); DemoComp = New("AddIn.DemoVK.SomeName"); lane = DemoComp.Test("Здравей, ", "Свят!"); Уведомяване (за); EndProcedure
След стартиране на конфигурацията ще получим съобщение: „Здравей, свят!“, което показва, че методът е работил успешно.

Таймер

Задача:
  1. Проучете изпълнението на таймера в демонстрационния VK
  2. Променете метода "StartTimer", като добавите възможност за предаване в параметрите на интервала на таймера (в милисекунди)
  3. Проверете дали направените промени работят.

В WinAPI, за да работите с времето, можете да използвате съобщението WM_TIMER. Това съобщение ще бъде изпратено до вашата програма в интервала от време, който сте посочили при създаването на таймера.
За да създадете таймер, използвайте функцията SetTimer:
UINT SetTimer(HWND hWnd, // манипулатор на прозорец UINT nIDevent, // идентификатор на таймера (номер) UINT nElapse, // забавяне TIMERPROC lpTimerFunc); // указател на функция
Операционната система ще изпрати съобщение WM_TIMERкъм програмата с интервала, посочен в аргумента nИзтичане(в милисекунди). В последния параметър можете да посочите функция, която да се изпълнява всеки път, когато таймерът се задейства. Заглавката на тази функция трябва да изглежда така (името може да бъде каквото и да е):
void __stdcall TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
Помислете за внедряването на таймера в демонстрационния VK.
Тъй като обмисляме процеса на разработване на външен компонент за семейството на Windows OS, няма да разглеждаме внедряването на таймера в други операционни системи. По-специално за GNU/Linux OS реализацията ще се различава в синтаксиса на функцията SetTimerИ TimerProc.
Методът се извиква в изпълнимия код SetTimer, към който се предава функцията MyTimerProc:
m_uiTimer = ::SetTimer(NULL,0,100,(TIMERPROC)MyTimerProc);
Идентификационният номер на създадения таймер се поставя в променлива m_uiTimerза да можете да го изключите по-късно.
функция MyTimerProcкакто следва:
VOID CALLBACK MyTimerProc(HWND hwnd, // манипулатор на прозорец за съобщения на таймера UINT uMsg, // WM_TIMER съобщение UINT idEvent, // идентификатор на таймера DWORD dwTime // текущо системно време) ( 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 , какво, wstime); изтрийте wstime; ) )
Същността на функцията е, че методът се извиква Външно събитие, който изпраща съобщение до системата 1C: Enterprise.
За разширяване на функционалността на метода StartTimerнека направим следното:
Промяна на кода на метода GetNParamsтака че да е за метода eMethStartTimerвърната стойност 1:
случай eMethStartTimer: връщане 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;
Сега нека проверим функционалността. За да направите това, в модула на управляваното приложение на конфигурацията ще напишем кода:
var DemoComp; SystemStartup Procedure() ConnectExternalComponent("...", "DemoVK", ExternalComponentType.Native); DemoComp = New("AddIn.DemoVK.SomeName"); DemoComp.StartTimer(2000); EndProcedure
След стартиране на конфигурацията, програмата ще получи съобщения с интервал от 2 секунди, което показва правилната работа на таймера.

Взаимодействие със системата 1C: Enterprise

За взаимодействие между външния компонент и системата 1C:Enterprise, методите на класа IAddInDefBase, описани във файла AddInDefBase.h. Изброяваме най-често използваните:
Генериране на съобщение за грешка
virtual bool ADDIN_API AddError(неподписан кратък wcode, const WCHAR_T* източник, const WCHAR_T* descr, дълъг scode)
wcode, код- кодове за грешки (списък с кодове за грешки с описание може да се намери на ITS диска)
източник- източник на грешка
описание- описание на грешката
Изпращане на съобщение до системата 1C: Enterprise
виртуален bool ADDIN_API ExternalEvent(WCHAR_T* wszSource, WCHAR_T* wszMessage, WCHAR_T* wszData) = 0;
wszSource- източник на съобщение
wszСъобщение- Текст на съобщението
wszData- предадени данни
Прихващането на съобщението се осъществява от процедурата HandlingExternalEvent
Регистрация на външен компонент в системата 1C:Enterprise
виртуален bool ADDIN_API RegisterProfileAs(WCHAR_T* wszProfileName)
wszProfileName- име на компонента.
Тези методи са достатъчни за пълното взаимодействие на VK и 1C. За да получи данни от външен компонент от системата 1C:Enterprise и обратно, външният компонент изпраща специално съобщение, което от своя страна се прихваща от системата 1C и, ако е необходимо, извиква методите на външния компонент за изпращане обратни данни.

tVariant тип данни

При обмен на данни между външен компонент и системата 1C:Enterprise се използва типът данни tVariant. Описано е във файла types.h, който може да се намери на ITS диска:
struct _tVariant ( _ANONYMOUS_UNION съюз ( int8_t i8Val; int16_t shortVal; int32_t lVal; int intVal; неподписан int uintVal; int64_t llVal; uint8_t ui8Val; uint16_t ushortVal; uint32_t ulVal; uint64 _t ullVal; int32_t errCode; дълго hRes; float fltVal; двойно 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 ; //брой байтове ) __VARIANT_NAME_3/*str*/; _ANONYMOUS_STRUCT struct ( WCHAR_T* pwstrVal; uint32_t wstrLen; //брой символи ) __VARIANT_NAME_4/*wstr*/; ) __VARIANT_NAME_1; uint32_t cbElements; //Измерение за едно - размерен масив в pvarVal TYPEVAR vt; );
Тип tVariantе структура, която включва:
  • смес (обединение), предназначена директно за съхранение на данни
  • идентификатор на тип данни
Като цяло работата с променливи от тип tVariantпротича по следния алгоритъм:
  1. Определяне на типа данни, съхранявани в момента в променлива
  2. Достъп до съответното поле на сместа, за директен достъп до данни
Тип използване tVariantзначително опростява взаимодействието на системата 1C: Enterprise и външен компонент

Приложение

Директорията "examples" съдържа примери за статията
examples/1 - стартирайте демонстрационния компонент
examples/2 - демонстрация на разширение на списък със свойства
examples/3 - демонстрация на разширение на списък с методи
Всяка директория съдържа VS 2008 проект и предварително изградена 1C конфигурация.

Тази статия е посветена на работата с външни компоненти, а именно тяхната връзка. В момента за разширяване на възможностите на 1C Enterprise се използват две технологии на външни компоненти:

  • 1 Използване на Native API
  • 2 Използване на COM технология
В тази статия реших да подчертая работата с Native API компоненти.
И така, нека да започнем от просто към сложно:
Екстракт от ITS

1. Да приемем, че нашият VK се намира в определена директория на диска:

Може да се използва в "Дебел клиент (обикновено приложение)";

Това е най-простият пример за работа с компонента Native. Моля, имайте предвид, че този тип компоненти не изискват регистрация в системата, което значително улеснява администрирането.

2. Разгледаният по-горе пример изобщо не е жизненоважен. Най-често даден компонент се поставя в оформление. Оформлението трябва да съдържа zip архив с компонентни файлове и файл MANIFEST.xml
Примерен манифестен файл:

3. При работа в тънък и уеб клиент е задължително използването на .
Цитат от ITS:

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

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

Инсталирайте външен компонент- методът трябва да се извиква само при първоначалната инсталация на компонента и в случай, че е необходимо да се актуализира инсталираната версия на компонента.

За слаби и дебели клиенти:
Достатъчно е да повторите операцията по инсталиране на външния компонент по метода Инсталирайте външен компонент().

В случай на уеб клиент за актуализиране на компонент:

  • Необходимо е да премахнете плъгина чрез механизма за добавки на уеб браузъра (Mozilla FF)
  • използвайте метода Инсталирайте външен компонент
За да свържете VC, можете да използвате следната процедура:

Ако компонентът не е инсталиран, ще бъде хвърлено изключение.

2. Има случаи, когато компонент трябва да бъде инсталиран от временно хранилище (файлът е получен от източник на трета страна, външна обработка), в този случай първият параметър в методите ConnectExternalComponent и InstallExternalComponent е адресът на архива във временния склад. По-долу е възможен пример за това как работи:

&AtClient VariableComponentArchiveAddress; &OnClient променлив компонент; &OnClient Процедура OnOpen(Failure) // адрес, съдържа низ (навигационна връзка към бинарни данни на zip архив във // временно хранилище) ComponentArchiveAddress = GetArchiveAddressInTemporaryStorage(); EndProcedure // OnOpen() &AtServer // методи ConnectExternalComponent,InstallExternalComponent могат да приемат // като първи параметър низ във формат "навигационна връзка" // (URL към външен компонент, пакетиран в ZIP архив, във формат, подобен на / / GetNavigationLink ). Функция GetArchiveAddressInTemporaryStorage() ProcessingObject = FormAttributeToValue("ProcessingObject"); ArchiveReference = PlaceToTemporaryStorage(ProcessingObject.GetLayout("MIKO_phone_IP"), Нов UniqueIdentifier); Връщане на LinkToArchive; EndFunction // GetArchiveAddressIn TempStorage() &OnClient // Процедурата трябва да се извика само веднъж, в случай че компонентът все още не е инсталиран // или трябва да се актуализира Процедура SetComponent(Command) Опит за InstallExternalComponent(ComponentArchiveAddress); Доклад за изключение ("Неуспешно инсталиране на външен компонент."); Край на опита; EndProcedure // SetComponent() &AtClient // процедура за инициализация на основния компонент Initialize(Command) Опит за включване на ExternalComponent(ComponentArchiveAddress,"Comp",ExternalComponentType.Native); Component = New("AddIn.Comp.MIKO_phone_IP"); Доклад за изключение ("Изключение по време на инициализация. Компонентът може още да не е инсталиран."); Край на опита; EndProcedure

Заглавието на статията съдържа фразата "за манекени". Под чайник имах предвид преди всичко себе си. Всичките ми знания за C ++ останаха на ниво 3-4 години в университета, когато попаднах на кривия път 1C. И всичко щеше да е наред, но наскоро имаше задача, която изискваше писане на външен компонент. Трябваше да раздвижа спомени и да отърся праха от знанията на C ++. Оказва се, че всичко не е толкова страшно. Кратка образователна програма за писане на външни компоненти е това, което искам да ви предложа.

Шаблон за ITS компонент

ITS дискът съдържа пълна документация за механизма на външните компоненти, допълнена от примерен проект и шаблон за вътрешна разработка. Материалът се нарича "Технология на външни компоненти". Документацията е страхотна, но все още трябва да бъде подредена, а времето, както обикновено, е малко. Всъщност има само няколко основни момента, на които трябва да обърнете внимание, останалото е разпад и суета :)

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

За да създадем външен компонент, имаме нужда от:

  1. Материалът "Технология за създаване на външни компоненти", намиращ се на ИТС
  2. Празен шаблон за външен компонент, прикрепен към материала
  3. MS Visual Studio. Express версията е безплатна и е повече от достатъчна за нашите нужди.
  4. Основни познания за синтаксиса на C++, а именно:
  • Способност за разграничаване между декларация на променлива и цикъл или условие
  • Разбирайки, че низовете в тяхната чиста форма не съществуват в C ++, има масиви, за които очевидно трябва да се занимавате с памет
  • Е, разбира се, изисква се способността за изпълнение на задачата на посочения език. Като минимум възможността да се обадите на библиотека на трета страна от C ++, която ще направи всичко сама.

Да започнем да копаем

Документацията за Native API е доста подробна. За да обобщим, тя казва следното:

  1. Външен компонент ви позволява да разширите вградения език с нов обект (или няколко). Тези. ще създадем клас, който можем да създадем чрез оператора "New" и да извикаме методите на този обект от вградения език.
  2. За да работи обектът ни, платформата ще „комуникира“ с него по определен протокол, който сме длъжни да предоставим.
  3. Самият код на компонента условно се състои от две части: първата е регистрацията на самия компонент в системата, втората е работата на новия клас и взаимодействието му с платформата.

Няма да навлизаме в детайли по изпълнението, сроковете ни изгарят, а компетентността ни не достига. Трябва бързо да разберем къде трябва да въведем нашите редове, за да може компонентът да работи. За да направим това, вземаме шаблона на компонента с ITS и го отваряме в Visual Studio. Шаблонът се намира в папката с шаблони на разопакования архив. Да видим какво имаме тук.

Интересуваме се от файла AddInNative.cpp. Цялото изпълнение е в него. Той съдържа заготовки на всички необходими методи, просто трябва леко да ги персонализирате. Оказа се обаче, че е по-лесно да се вземе за основа не празен шаблон, а да се работи с работещ пример. Има няколко полезни джаджи, които не са в празния шаблон. Когато дойде разбирането, ще трябва да вземете празен шаблон и да го прецизирате с познания по въпроса. Примерен работен компонент се намира в папката example\NativeAPI, а празен шаблон се намира в папката с шаблони.

Нека отворим проекта от примерната папка и в него - файла AddInNative.cpp

В самото начало на файла има декларации на константи и помощни функции. Интересуваме се от следните редове:

Нашият обект, като „истински“, ще поддържа методи, написани както на руски, така и на английски. За това се декларират изписването на имената на свойствата и методите на два езика. Синя рамка - английски термини, червена, съответно - руски. Картината показва, че редица методи и свойства вече са имплементирани в примера. Нашата задача е да ги премахнем и да вмъкнем наши собствени.

Зелената рамка подчертава реда, където е декларирано името на класа. Честно казано, не знаех какво означава. Ако го промените, тогава нищо не работи. Тъй като първоначално беше уговорено, че съм "чайник", тогава може да ми бъде простено. :)

По този начин, ако нашият обект ще съдържа метода „Извършване на изчисление“ и свойството „Дестинация“, тогава трябва да опишем това име съответно в масивите g_MethodNamesRu и g_PropNamesRu.

Обаждания от 1C език

И така, нашият обект ще съдържа един метод и свойство, достъпно за четене и писане.

Нека имаме следния случай на употреба:

OurObject = New("AddIn. MyComponent. DataSender"); Нашият обект. Дестинация = " [имейл защитен] com";
Нашият обект. Извършване на изчисление (Сума на плащането, „За комунални услуги“);

Има свойство на низ и метод с числов и низов параметър. За да работи всичко това, 1C изпълнява приблизително следния протокол за комуникация с компонента:

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

Да се ​​върнем към нашия код. За да се избегнат "магическите числа", в класа CAddInNative са декларирани две изброявания, които отговарят за определянето на номерата на методите и свойствата. Нека отворим файла CAddInNative.h и да ги видим в самото начало:

Празният шаблон не съдържа тези изброявания и няма пример за разделяне на повиквания на руски от повиквания на неруски. Този подход не е задължителен. Важно е да спазвате интерфейса, а дали ще има изброявания или не зависи от вас.

Unicode низове

Мнозина вероятно знаят, че платформата работи с двубайтови знаци във формат Unicode. Шаблонът декларира специален тип WCHAR_T за това. Този тип е обвивка на различни платформи и осигурява същия размер на знаците в Windows и Linux. Стандартният тип wchar_t може да варира по размер в различните системи. Също така имайте предвид, че всички низови литерали са декларирани с префикс L. Това означава, че такъв низ е от тип wchar_t.

Има просто правило: вътре в компонента низовете се третират като wchar_t (в Linux може да бъде 4 байта, в Windows - 2), но веднага щом прехвърлим низ в 1C или го получим от там, имаме нужда от WCHAR_T ( строго 2 байта на всички системи).

В шаблона са предоставени помощни функции за преобразуване на един тип низ в друг:

Първият генерира WCHAR_T от стандартен wchar_t:

uint32_t convToShortWchar(WCHAR_T** Dest, const wchar_t* Източник, uint32_t len ​​​​= 0);

Второто е обратното. Форми wchar_t от WCHAR_T.

uint32_t convFromShortWchar(wchar_t** Dest, const WCHAR_T* Източник, uint32_t len ​​​​= 0);

При взаимодействие с платформата винаги се използва само WCHAR_T.

Тип вариант

Друго интересно нещо е генеричният тип данни Variant. Това ни позволява да взаимодействаме с езика 1C, който, както знаете, не е въведен и всяка променлива в него може да съдържа всичко. При обмен на стойности се използва този тип. Предаваме два параметъра на метода RunCalculation - число и низ. Компонентът ще получи две стойности Variant. Наша отговорност е да проверим действителния им тип. Никой не си прави труда да предаде на компонента не число, а, да речем, таблица със стойности.

Макар че май греша. Струва ми се, че таблицата със стойности в NativeAPI все още няма да работи, защото не е в списъка с разрешени типове, но все пак можете да подадете дата вместо низ. Това също не е добре. Трябва да проверим реалния тип на променливата, която идва от 1C.

Типът Variant е прост. Това е структура, чиито свойства са стойности от различни типове. Има свойства като DATE, wchar_t, int и други. Основната част от Variant е свойството “vt”, което съхранява реалния тип на променливата и чрез което можете да разберете как точно да интерпретирате този Variant. Освен това са декларирани редица помощни макроси, за да се опрости работата с типа Variant.

Преминете към точката

Изглежда, с въведението всичко. Предлагам да разгледаме пример за внедряване на външен компонент. Пример за компонент от ITS диска ще действа като TK. Този пример описва следните функции:

  • Извеждане на текст в лентата на състоянието на главния прозорец;
  • Изпращане на събитие от външен таймер;
  • Прехвърляне на двоични данни към 1C:Enterprise;
  • Изпълнение на имота;
  • Изпълнение на процедури;
  • Изпълнение на функции;

Компонентът има следния API:

  • Имоти:
    • Включено/Включено е;
    • IsTimer/IsTimerPresent;
    • Методи:
      • Активиране / Активиране;
      • Изключване / Деактивиране;
      • ShowInStatusLine/ShowInStatusLine;
      • Активиране на таймер/старттаймер;
      • Изключете таймера/Стоптаймера;
      • LoadPicture/LoadPicture;

Според таймера възниква външно събитие, за което можете да се абонирате от кода 1C.

Въз основа на знанията, които имаме, нека разгледаме компонента от самото начало.

Регистриране на компонент

Нашият обект е имплементиран като отделен C++ клас, в този случай CAddInNative. За да може 1C да види нашия клас, dll библиотеката трябва да експортира 3 функции:

  • GetClassObject
  • DestroyObject
  • GetClassNames

Тези експорти могат да се видят във файла AddInNative.def в дървото на проекта на VisualStudio. Нека да разгледаме кода на тези функции:

Най-простият - функцията GetClassNames - казва на платформата 1C какви класове има в нашия компонент. Нека C++ гурутата ме поправят, струва ми се, че тук трябва да отговорите на платформата с имената на C++ класовете, за да може да ги импортира в себе си. Ето за какво служи масивът g_kClassNames, този със зелената „рамка“. Не го проверих специално, ако просто трябва да накарате компонента да работи, тогава трябва да оставите всичко както е в примера. Той вече работи, не трябва да го избирате за момента.

Така GetClassNames връща на платформата масив от имена на класове, които имплементират полезни обекти на външния компонент. В нашия пример компонентът ще върне на платформата масив от един елемент с име на клас CAddInNative.

Моля, обърнете внимание, че стойността на типа WCHAR_T ще отиде в платформата, а името на класа в масива g_kClassNames е от типа wchar_t. Следователно прехвърлянето се извършва с помощта на помощната функция, обсъдена по-горе.

Следващата функция е GetClassObject. Извиква се, когато сме написали "Ново" в корпоративния код. Рамката изисква от нас да създадем нов екземпляр на класа и да върнем указател към новия обект.

Отново имайте предвид, че първият параметър, който платформата ни казва, е кой клас да създадем (от тези, които са й дадени от метода GetClassNames). Тъй като имаме само един клас, това име изобщо не се проверява тук, обектът просто се създава чрез new и се връща чрез изходния параметър pInterface.

И последната задължителна експортна функция е DestroyObject. Името говори само за себе си. Когато даден обект вече не е необходим на платформата, той трябва да бъде изтрит. Предаден ни е указател към предварително създаден обект. Освобождаваме го с изтриване и нулиране на ненужни указатели.

Описаните реализации са доста универсални. Ако нашият компонент имплементира само един клас (както в примера), тогава тези функции трябва да бъдат глупаво копирани в нас. Единственото условие е да създадете правилния клас във функцията GetClassObject, ако името ви не е CAddInObject, а нещо друго.

Инициализация/извеждане от експлоатация на компонент

След създаването на клас, който имплементира компонента, платформата извиква методите на този клас. Преди да започне работа, платформата ще ни каже обекта на "самата себе си", с който можем да извикаме определени методи на самата платформа. Това се случва в метода Init. В примера обектът на платформата се съхранява в променливата m_iConnect.

Друг важен метод е setMemManager. Позволява ви да разпределите блокове памет, които самата платформа ще освободи. Изпълнява се както следва:

Ние просто съхраняваме указател към мениджъра на паметта, който платформата ни предава. След това с този мениджър ще разпределим памет, освободена от самата платформа.

И отново, както в случая с функциите за експортиране, методите за инициализация са доста универсални, можете просто да ги копирате в себе си и да не се притеснявате да ги „довършите“, докато наистина не стане необходимо.

Полезен товар. Методи и свойства на компонентен обект

Регистрация

Е, разбира се, ние създадохме компонента не в името на неговата инициализация, а в името на някаква полезна функционалност. Време е да помислим как се изпълнява.

Първо, трябва да регистрираме обект, който може да бъде създаден и извикан от езика 1C. Този обект е регистриран в метода RegisterExtensionAs.

При този метод казваме на платформата името на нашия клас, както ще се види от езика 1C. Именно с това име ще го създадем чрез „Ново“. В този случай създаването на обекта ще се извърши от следния код:

ConnectExternalComponent(Файл, "MyComponent" , ExternalComponentType. Native);
ComponentObject = Нов( "AddIn.MyComponent.AddInNativeExtension");

Според документацията паметта за низа с името на класа се разпределя от мениджъра на паметта, а името се записва на този адрес - "AddInNativeExtension". Тук можете безболезнено да напишете името си. Обърнете внимание, отново има преобразуване от wchar_t към платформа WCHAR_T.

Използване

Както писах по-горе, платформата проверява компонента за различни езикови функции. Съществува ли посоченото свойство, може ли да се записва, има ли параметърът на функцията стойност по подразбиране, има ли върната стойност и т.н. Като вземем примера с код по-горе:

OurObject = New( "AddIn.MyComponent.DataSender"); // DataSender е името от функцията RegisterExtensionAs (обсъдена по-долу).
Нашият обект. Дестинация = " [имейл защитен]" ;
Нашият обект. Извършете изчисление (сума на плащане, "За комунални услуги");

след това ще бъде изпълнена следната заявка:

  1. Има ли свойство "Дестинация".
  2. Поддържа ли запис
  3. Има ли метод Execute Calculation
  4. Колко параметри има
  5. Има ли възвръщаема стойност
  6. Какви са настройките по подразбиране за незадължителни параметри (ако има такива)

Тук е най-полезно да разгледате примера и да проверите документацията. Изпълнението на всички тези проучвания е доста лесно. Цял зоопарк от методи е отговорен за взаимодействието. Няма да разглеждам всичко, те са доста добре документирани и освен това се изпълняват просто. Ще бъдат разгледани само най-значимите моменти, в които ние, като "манекени", ще трябва да се катерим с малките си ръчички :). Основният подход е следният: при първото споменаване на определено свойство или метод, платформата ще ни помоли да го търсим по име. Ще трябва да отговорим с уникален номер на това свойство (метод). Цялата по-нататъшна комуникация ще се осъществява само чрез номера. Тук ще помогнат споменатите изброявания, които съхраняват тези числа.

Имоти

Първото нещо, което трябва да вземете предвид, е рамката на свойствата. Рамката пита за съществуването на свойство, използвайки метода FindProp

Платформата ни дава името на желаното свойство под формата на WCHAR_T. Той се преобразува в wchar_t чрез спомагателен метод и този текст се търси първо по английски термини, а след това по руски. Трябва да върнем номера на имота. Имайте предвид, че тук е включена помощната функция findName. Неговата реализация е в примера, но не и в шаблона на празния компонент. Изглежда подходящо да го плъзнете към вас, ако в компонента са планирани двуезични термини.

След това методът GetPropName изпълнява обратната задача, като получава името на свойството по неговия номер. Низът с име също се разпределя чрез мениджъра на корпоративната памет. Подозирам, че методът GetPropName, заедно с GetNProps, се използва, когато разширяваме свойствата на обект със знак плюс в дебъгера. След това платформата ще получи общия брой имоти и ще поиска име за всеки от тях.

Следващата двойка методи е IsPropReadable/IsPropWritable. Тук всичко е просто, за посочения номер на имот трябва да кажем дали може да се чете / пише.

Получаването и записването на стойности се извършват от методите GetPropVal/SetPropVal. Тук си струва да спрем по-подробно. Започваме да работим с типове 1C:Enterprise, което означава, че Variant излиза на сцената.

Шаблонът на компонента дефинира набор от помощни макроси, за да улесни работата с варианта. Първият е проверка на тип стойност. Например, макросът TV_VT ви позволява да проверите/зададете типа на стойност. Наименуваните константи също са дефинирани за всеки от поддържаните типове. Тези константи и техните съответствия с типовете 1C:Enterprise са изброени в документацията.

Макросът 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; // задава самата стойност

А сега - гърбави методи!

Методите са малко по-сложни, но като цяло са подобни на свойствата. Първо, има абсолютно същата функция за търсене на метод по име, получаване на общия брой методи, получаване на име по номер. Въпреки това, в допълнение към горното, се добавят следните функции:

  • Ако методът може да върне стойност, тогава той може да се използва в "Изчисляване" и да бъде написан отдясно на операцията за присвояване на езика 1C. Ако не, това е процедура и подобни неща ще хвърлят изключение „Използване на процедура като функция“.
  • Методът има параметри. Платформата трябва да знае техния брой. Ако по време на извикването са посочени повече аргументи, отколкото в сигнатурата на метода, възниква грешката „Твърде много параметри“.
  • Ако към метода не са предадени достатъчно аргументи, тогава някои от тях може да са незадължителни, а ако няма незадължителни параметри, тогава възниква грешката „Няма достатъчно параметри“.
  • При извикване, ако е процедура, тогава не може да има върната стойност. Ако е функция, тогава има върната стойност. Той също трябва да бъде обработен.

Има редица прости методи, чиято цел е ясна от техните имена и от документацията. Те включват HasRetVal, GetNParams, GetParamDefValue. Предлагам да не ги разглеждаме, примерът е повече от достатъчен. Нашият интерес ще бъде насочен към директното внедряване на полезния товар. Реализира се в методите CallAsProc и CallAsFunc. Първият отговаря за извикване на процедури, вторият за извикване на функции. Те се различават по това, че CallAsFunc има допълнителен изходен параметър, в който ще предадем върнатата стойност на функцията към платформата.

Извикването става по следния начин: платформата ни дава номера на извикания метод, масив от реални параметри и техния брой. Трябва да анализираме номера на метода и да му подадем подадените параметри. В случай на функция, ние също трябва да напишем нещо към върнатата стойност.

В примера номерът на метода се анализира в switch/case и логиката на метода се изпълнява в зависимост от номера. Методите за активиране/деактивиране просто поставят отметка в квадратчето. Методът ShowInStatusLine е интересен. Той показва какво му е предадено в лентата на състоянието на прозореца на 1C:Enterprise. За целта се използва обектът за свързване на платформата m_iConnect, този, който ни беше „издаден“ при регистриране на компонента. Пълният списък на неговите функции е описан в документацията.

Интересен момент. Тук, в примера, типът на стойността, пристигнала от 1C, не се проверява, но SetStatusLine просто се извиква с частта от низ Variant. Подозирам, че ако извикате метода на компонента от езика 1C, предавайки номер или дата (вместо низ), тогава нищо няма да работи ... Отново, нека гурутата да коригират, но изглежда, че указателят pwstrVal ще посочи неизвестно къде, ако компанията е пристигнала, да кажем число, а не честен низ. При извикване на SetStatusLine, платформата ще се опита да прочете ред от неизвестен адрес и най-вероятно ще се срине. По-добре е винаги да проверявате за очаквания тип. Никога не знаеш какво.

Функцията LoadPicture в примера е реализирана по по-интересен начин, тя разглежда възможността за обмен на низове и двоични данни с платформата.

Първо тук се проверява броят на предадените параметри. Ако не са, тогава обаждането се счита за неуспешно. Връща false, което се интерпретира от платформата като грешка при повикване.

След това тук се проверява типът на предадения параметър. Ако е тесен низ (VTYPE_PSTR), тогава се използва частта char на варианта. В примера пише paParam->pstrVal, но можете да използвате макроса TV_STR, ще бъде същото, но също така се наблюдава еднаквост на работа с опцията.

Ако това е широк низ (VTYPE_PWSTR), тогава преобразуването се извършва първо към wchar_t и след това към char. Факт е, че пътят до файла ни се предава от езика 1C към този метод, който след това се използва във функцията fopen(char*). Тази функция изисква входен тип char * и WCHAR_T ще дойде при нас от платформата. За правилна работа се извършват преобразувания на низове.

И накрая, ако това изобщо не е низ, тогава повикването се счита за неуспешно, връща се false.

Разпределяме памет за двоични данни с мениджър на паметта. Това е логично, бинарните данни ще станат пълноценен обект в рамките на платформата и тя е тази, която трябва да се разпорежда с тях. Паметта се разпределя за варианта pvarRetValue, който е върнатата стойност на външната bean функция.

Целият файл се чете в разпределения буфер, в допълнение, Задължителноразмерът на байта е посочен в свойството на вариант strLen и типът данни на варианта е VTYPE_BLOB. Ако паметта е разпределена успешно, тогава връщаме true, като знак за успешно извикване на цялата функция.

Така, когато на езика 1C ще бъде написано:

BinaryData = Компонент. Качи изображение("C:\pic.jpg");

методът CallAsFunc на bean обекта ще бъде извикан, предавайки пътя и връщайки двоичните данни, както е описано по-горе.

Ако успее, променливата BinaryData ще съдържа пълноценен езиков обект 1C. Когато излезе извън обхвата, цялата памет, заета от него, ще бъде освободена от платформата. Ето защо се открои чрез мениджъра на паметта.

Заключение

Историята е написана от чайник за чайници, следователно най-вероятно е пълна с терминологични неточности. Целта на тази статия обаче е бързо въведение във външните компоненти. Ако трябва бързо да направите компонент за кратко време, без ненужни проблеми, без дълги изпитания, тогава се надявам тази статия да ви помогне. Ако някоя от моите грешки ви кара да се чувствате зле, като C++ гуру, уведомете ни в коментарите, ние ще я поправим.

Благодаря за вниманието.

Технологията на външните компоненти ви позволява да създавате програми (външни компоненти), които динамично ще се свързват и взаимодействат тясно със системата 1C:Enterprise 8, разширявайки нейните възможности. Тази технология ви позволява да свържете различно търговско оборудване към системата 1C: Enterprise 8: скенери за баркод, принтери за етикети и др.

Роден API

Технологията се използва за създаване на външни компоненти Роден API- собствен интерфейс за системно програмиране 1C:Enterprise 8. Той поддържа операционни системи Windows и Linux и дава възможност за създаване на външни компоненти, които работят под една или друга операционна система. Компонентите, създадени с помощта на Native API технология, могат да бъдат свързани в дебел клиент, тънък клиент, уеб клиент, външна връзка и сървър на приложения.

Вградено разширение за език

Външните компоненти ви позволяват да разширите вградения език с нови обекти. Структурите на механизмите на външните компоненти са възможно най-близки до вътрешните структури на системата 1C:Enterprise 8, което повишава ефективността на работа.

Извикване на процедура за обработка на събития, контролирана от външен компонент

Външен компонент може да генерира събития, обработвани в предварително дефинирана езикова процедура Обработка на външно събитие. Това ви позволява да свържете скенери и други устройства, които изискват асинхронен обмен на данни към системата 1C:Enterprise 8.

Добавяне на страница със свойства към настройките на 1C:Enterprise 8

Външните компоненти могат да добавят своите страници със свойства към диалоговия прозорец за настройки на системата 1C:Enterprise 8. По този начин оборудването за търговия на дребно може да бъде включено в системата и контролирано по стандартния начин за системата 1C:Enterprise 8.

Запазване на параметрите на компонента чрез механизма за запазване на параметри на 1C:Enterprise 8

Когато запазва параметрите си, външният компонент може да използва механизмите на системата 1C:Enterprise 8.

Достъп до лентата на състоянието

По време на работа състоянието на външен компонент може да бъде отразено в панела за състояние на системата 1C:Enterprise 8.

ОЛЕГ ФИЛИПОВ, ANT-Inform, заместник-ръководител на отдела за развитие, [имейл защитен]

Разширяване на функционалността на 1C:Enterprise
Част 1: Външни COM компоненти

В живота на всеки разработчик на 1C идва момент, когато възложените му задачи надхвърлят възможностите на платформата 1C. Обмислете начини за „преодоляване на границата на възможното“

Опитните разработчици на 1C вероятно вече са се досетили от първите три думи от заглавието на статията, че говорим за външни компоненти за 1C: Enterprise. Тази технология съществува от платформата 1C:Enterprise 7.7 (или по-рано). Исторически се появи главно за решаване на проблемите на взаимодействие с търговско оборудване (барови скенери, електронни везни, касови апарати), по време на които става необходимо да се обработват определени събития, инициирани от оборудването от платформата 1C. Такива събития не са нищо повече от последователности от байтове, пристигащи към COM/LPT портовете. Разбира се, не би било много правилно да се адаптира платформата 1C за работа с такива механизми на ниско ниво, затова измислихме технологията на външните компоненти за 1C:Enterprise.

В тази поредица от статии ще говорим не само за външни компоненти: външните компоненти са доста мощен и сложен механизъм, така че използването им за решаване на някои проблеми се разглежда като „стрелба по врабчета от оръдие“. Въпреки това външните компоненти са най-универсалният инструмент за решаване на различни задачи, които надхвърлят платформата 1C:Enterprise, така че ще започнем с тях. Статията ще разгледа най-старата, най-често използваната и изпитана във времето технология за създаване на външни компоненти - базирана на COM.

Какво представляват външните компоненти

Външните компоненти, както беше споменато по-горе, се появиха в 1C:Enterprise от версия 7.7. Първоначално, по традиция, разработчиците на платформата 1C „не се притесняваха“, а външните компоненти бяха обект с определени задължителни свойства и методи. В същата форма компонентите съществуват и до днес. Тоест компонентите, написани за 1C:Enterprise 7.7, (на теория) ще работят с 1C:Enterprise 8.3.

На практика има редица тънкости: ако външен компонент активно взаимодейства със самата платформа, тогава неговите функции ще бъдат специализирани за определена версия.

От версия 8.2 1C:Enterprise въведе нова технология за разработване на външни компоненти, така наречения NativeAPI. Компонентите, написани с помощта на тази технология, вече не са COM обекти. От една страна, това е плюс - тези компоненти не изискват регистрация, от друга страна, те не могат да се използват никъде другаде, с изключение на платформата 1C:Enterprise. Разработването на външни компоненти стана малко по-трудно, те трябва да поддържат малко по-голям брой необходими интерфейси. Но това ще бъде обсъдено в следващата статия.

Външни компоненти и COM технология

Няма да описвам подробно самата COM технология, защото има много литература по тази тема. Трябва само да се каже, може би, че мнозина "бъркат" създаването на обикновен inproc-сървър със създаването на външни компоненти за 1C:Enterprise, но това не е далеч от истината. Много често можете да се справите само с тази функционалност. Как да създадете COM обект във Visual Studio е описано в много различни източници. По-специално, човекът написа подробно как се прави това, с пример за обаждане от 1C: Enterprise.

Ако все още искате да направите пълноценен COM външен компонент за 1C:Enterprise (от опциите, поради които това може да е необходимо, мога да се сетя само за един - компонентът трябва активно да взаимодейства със системата на платформата 1C, да уведомява потребителите, промяна на лентата на състоянието, показване на диалогови прозорци и т.н.), тогава трябва да използвате технологията на външни компоненти директно. Така че да започваме.

Дял