⬆️ ⬇️

External components in 1C 8.2

Introduction



This article gives an idea of ​​the work of external components in the 1C: Enterprise system.

The process of developing an external component for the “1C: Enterprise” system version 8.2, running under Windows operating systems with a file version of the work, will be shown. This type of work is used in most solutions designed for small businesses. VK will be implemented in the programming language C ++.





External components "1C: Enterprise"



"1C: Enterprise" is an extensible system. To extend the functionality of the system uses external components (VK). From the point of view of a VC developer, it is some external object that has properties and methods, and can also generate events for processing by the “1C: Enterprise” system.

External components can be used to solve a class of problems that are difficult or even impossible to implement in the programming language built into 1C: Enterprise. In particular, this class can include tasks that require low-level interaction with the operating system, for example, to work with specific equipment.

The 1C: Enterprise system uses two technologies for creating external components:



Given the restrictions between the two technologies mentioned above, the difference is insignificant, so we will consider the development of VC using Native API. If necessary, the implemented developments can be applied for the development of VC using COM technology, as well as, with minor modifications, are applied for use in the 1C: Enterprise system with other work options that are different from the file mode of operation.



VK structure


The external component of the 1C: Enterprise system is represented as a DLL library. The library code describes a descendant class of IComponentBase. In the created class, the methods responsible for implementing the functions of the external component must be defined. The redefinable methods will be described in more detail below as the material is presented.

')

Running a demo VK



Task:

  1. Assemble the external component supplied with an ITS subscription and intended to demonstrate the basic capabilities of the external component mechanism in 1C
  2. Connect the demo component to the 1C configuration
  3. Make sure the correct functionality of the claimed functions




Compilation


The demo VK is located on the ITS subscription disk in the "/ VNCOMP82 / example / NativeAPI" directory.

We will use Microsoft Visual Studio 2008 to build the demo VK. Other versions of this product do not support the used format of the Visual Studio project.



Open the AddInNative project. In the project settings, we include the directory with the header files necessary for building the project. By default, they are located on the ITS disk in the / VNCOMP82 / include directory.

The result of the build is the file /bind/AddInNative.dll . This is the compiled library for connecting to the 1C configuration.



Connecting VK to 1C configuration


Create an empty 1C configuration.

Below is the code for the managed application module.

 ;  () ("...\bind\AddInNative.dll", "DemoVK", .Native);  = ("AddIn.DemoVK.AddInNativeExtension");  


If no error was reported when starting the 1C configuration, the VC was successfully connected.

As a result of executing the above code, the DemoKomp object appears in the global configuration visibility, having properties and methods that are defined in the code of the external component.



Demonstration of the pledged functionality


Check the performance of the demo VK. To do this, we will try to set and read some properties, call some VK methods, and also receive and process the VK message.

In the documentation supplied on the ITS disk, the following functional of the demonstration VC is declared:

  1. Management of the state of the object components

    Methods: Enable , Disable

    Properties: Enabled
  2. Timer control

    Every second, the component sends a message to the “1C: Enterprise” system with the Component , Timer parameters and the system clock counter line.

    Methods: StartTimer , StopTimer

    Properties: YesTimer
  3. The ShowVStatStatus method that displays the text passed to the method as parameters in the status bar
  4. Method DownloadPicture . It loads the image from the specified file and transfers it to the “1C: Enterprise” system as binary data.


Make sure that these functions work. To do this, execute the following code:

  ;  () (...);  = ("AddIn.DemoVK.AddInNativeExtension"); .(); (.); .(); (.); .();   (, , ) ( + " " +  + " " + );  


The result of running the configuration is shown in the image.



The "Messages" panel displays the results of calling the DemoComp. Disable () and Demo . Comp . Enable () methods. Subsequent lines on the same panel contain the results of processing the messages received from the VC - Source , Event and Data, respectively.



Arbitrary external component name



Task: Change the name of the external component to arbitrary.

The previous section used the identifier AddInNativeExtension , the meaning of which was not explained. In this case, AddInNativeExtension is the name of the extension.

In the VK code, the RegisterExtensionAs method is defined, which returns the name “1C: Enterprise” to the system, which is necessary for the subsequent registration of the VK in the system. It is recommended to specify an identifier that to some extent reveals the essence of the external component.

Here is the full code of the RegisterExtensionAs method with the modified extension name:

 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; } 


In the above example, the name of VK is changed to SomeName . Then, when connecting VC, you must specify a new name:

  = ("AddIn.DemoVK.SomeName"); 




Expansion of the list of properties VK



Task:

  1. To study the implementation of the properties of VK
  2. Add a read / write string property
  3. Add a read / write string type property that stores the data type of the last property set. When setting the property value, no action is taken.
  4. Verify the performance of the changes


To determine the properties of the component being created, the developer must implement the following methods in the code of the AddInNative.cpp library:

Getnprops

Returns the number of properties of this extension, 0 - in the absence of properties

Findprop

Returns the sequence number of the property whose name is passed in the parameters

GetPropName

Returns the property name by its ordinal number and by the passed language identifier.

Getpropval

Returns the value of the property with the specified sequence number.

SetPropVal

Sets the value of the property with the specified sequence number.

IsPropReadable

Returns the flag flag for reading the property with the specified sequence number.

IsPropWritable

Returns the flag flag for writing the property with the specified sequence number.



A complete description of the methods, including a list of parameters, is described in detail in the documentation supplied on the ITS disk.

Consider the implementation of the methods CAddInNative class.

In the demo VK, 2 properties are defined: Enabled and IsTimer ( IsEnabled and IsTimerPresent ).

In the global scope of the library code, two arrays are defined:

 static wchar_t *g_PropNames[] = {L"IsEnabled", L"IsTimerPresent"}; static wchar_t *g_PropNamesRu[] = {L"", L""}; 


which store the Russian and English names of properties. The header file AddInNative.h defines the enumeration:

 enum Props { ePropIsEnabled = 0, ePropIsTimerPresent, ePropLast // Always last }; 


ePropIsEnabled and ePropIsTimerPresent , respectively, with values ​​of 0 and 1 are used to replace the sequence numbers of properties with meaningful identifiers. ePropLast, which has a value of 2, is used to get the number of properties (using the GetNProps method). These names are used only inside the code components and are not accessible from the outside.

The FindProp and GetPropName methods make it easier to search through the g_PropNames and g_PropNamesRu arrays .

To store the value of fields in the library module, the CAddInNative class defines properties that store the value of the component properties. The GetPropVal and SetPropVal methods respectively return and set the value of these properties.

The IsPropReadable and IsPropWritable methods both return trure or false , depending on the passed sequence number of the property in accordance with the application logic.

In order to add an arbitrary property it is necessary:

  1. Add the name of the property being added to the g_PropNames and g_PropNamesRu arrays ( AddInNative.cpp file)
  2. Add a name in the Props enumeration ( AddInNative.h file) before ePropLast that uniquely identifies the property being added.
  3. Organize memory for storing property values ​​(create component module fields that store the corresponding values)
  4. Make changes to the GetPropVal and SetPropVal methods to interact with the memory allocated in the previous step
  5. In accordance with the logic of the application, make changes to the IsPropReadable and IsPropWritable methods.


Clauses 1, 2, 5 do not need clarification. The details of the implementation of these steps can be found by studying the annex to the article.

We give the names of the test properties Test and Type Test, respectively. Then as a result of the implementation of paragraph 1 we have

 static wchar_t *g_PropNames[] = {L"IsEnabled", L"IsTimerPresent", L"Test", L"TestType"}; static wchar_t *g_PropNamesRu[] = {L"", L"", L"", L""}; 


The Props enumeration will be:

  enum Props { ePropIsEnabled = 0, ePropIsTimerPresent, ePropTest1, ePropTest2, ePropLast // Always last }; 


To greatly simplify the code, we will use STL C ++. In particular, to work with WCHAR strings, we include the wstring library.

To save the value of the Test method, we define the private field in the CAddInNative class in the scope:

 string test1; 


To transfer string parameters between 1C: Enterprise and external components, the 1C: Enterprise memory manager is used. Consider his work in more detail. The functions AllocMemory and FreeMemory , defined in the file ImemoryManager.h, are used to allocate and free memory, respectively. If it is necessary to transfer a string parameter to the 1C: Enterprise system, the external component must allocate memory for it by calling the AllocMemory function. Its prototype is as follows:

 virtual bool ADDIN_API AllocMemory (void** pMemory, unsigned long ulCountByte) = 0; 


where pMemory is the address of the pointer to which the address of the allocated memory will be placed,

ulCountByte - the size of the allocated memory.

An example of memory allocation for a line:

 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); 


For the convenience of working with string types, we describe the function wstring_to_p . It gets a wstring string as a parameter. The result of the function is the completed tVariant structure. Function code:

 bool CAddInNative::wstring_to_p(std::wstring str, tVariant* val) { char* t1; TV_VT(val) = VTYPE_PWSTR; m_iMemory->AllocMemory((void**)&t1, (str.length()+1) * sizeof(WCHAR_T)); memcpy(t1, str.c_str(), (str.length()+1) * sizeof(WCHAR_T)); val -> pstrVal = t1; val -> strLen = str.length(); return true; } 


Then the corresponding case section of the switch statement of the GetPropVal method will look like this:

 case ePropTest1: wstring_to_p(test1, pvarPropVal); break; 


SetPropVal method :

  case ePropTest1: if (TV_VT(varPropVal) != VTYPE_PWSTR) return false; test1 = std::wstring((wchar_t*)(varPropVal -> pstrVal)); break; 


To implement the second property, we define the class field CaddInNative

 uint8_t last_type; 


in which we will save the type of the last passed value. To do this, add the following command to the CaddInNative :: SetPropVal method:

 last_type = TV_VT(varPropVal); 


Now, when we ask to read the value of the second property, we will return the value of last_type , which is required by the indicated task.

Check the performance of the changes.

To do this, we bring the appearance of the 1C configuration to the form:

  ;  () ("...", "DemoVK", .Native);  = ("AddIn.DemoVK.SomeName"); . = 1; ((.)); . = ""; ((.)); . = ""; ((.)); ((.));  


As a result of the launch, we will receive a sequence of messages:

3





22



The second and third messages are the result of reading the property set in the previous step. The first and second messages contain the code of the type of the last property set. 3 corresponds to the integer value, 22 - string. The correspondence of types and their codes is established in the types.h file, which is located on the ITS disk.



Expansion of the list of methods



Task:

  1. Extend the functionality of the external component with the following functionality:
  2. Examine methods for implementing external component methods
  3. Add a method-function Funk1 , which takes two lines as a parameter (“Parameter1” and “Parameter2”). The result is a string of the form: “Check. Parameter1, Parameter2
  4. Verify the performance of the changes


To define the methods of the component being created, the developer must implement the following methods in the AddInNative library code:

GetNMethods , FindMethod , GetMethodName

Designed to obtain respectively the number of methods, search for the number and name of the method Similar to the corresponding methods for properties

Getnparams

Returns the number of method parameters with the specified sequence number; if the method with this number is missing or has no parameters, returns 0

GetParamDefValue

Returns the default value of the specified parameter of the specified method.

HasRetVal

Returns the flag of the presence of a method with the specified sequence number of the return value: true for methods with a return value and false otherwise

CallAsProc

Executes the method with the specified sequence number. If the method returns false , a runtime error occurs and the execution of the 1C: Enterprise module is terminated. Memory for an array of parameters is allocated and released by 1C: Enterprise.

Callasfunc

Executes the method with the specified sequence number. If the method returns false , a runtime error occurs and the execution of the 1C: Enterprise module is terminated. Memory for an array of parameters is allocated 1C: Enterprise. If the return value is of type string or binary data, the component allocates memory with the AllocMemory function of the memory manager, writes the data there, and stores this address in the appropriate structure field. 1C: Enterprise will free this memory by calling FreeMemory .

A complete description of the methods, including a list of parameters, is described in detail in the documentation supplied on the ITS disk.

Consider the implementation of the methods described above.

In the component code, two arrays are defined:

 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""}; 


and listing:

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


They are used in the functions GetNMethods , FindMethod and GetMethodName , by analogy with the description of properties.

The methods GetNParams , GetParamDefValue , HasRetVal implement the switch, depending on the passed parameters and application logic return the desired value. The HasRetVal method in its code has a list of only methods that can return a result. For them, it returns true . Returns false for all steel methods.

The CallAsProc and CallAsFunc methods contain the directly executable method code.

To add a method that can only be called as a function, you must make the following changes in the source code of the external component:

  1. Add method name to g_MethodNames and g_MethodNamesRu arrays ( AddInNative.cpp file)
  2. Add a meaningful method identifier to the Methods enumeration (file AddInNative.h )
  3. Make changes to the code of the GetNParams function in accordance with the program logic
  4. If necessary, make changes to the code of the GetParamDefValue method if you want to use the default values ​​of the method parameters.
  5. Make changes to HasRetVal function
  6. Make changes to the logic of the CallAsProc or CallAsFunc functions by placing the directly executed code of the method


We present the g_MethodNames and g_MethodNamesRu arrays , as well as the enumeration Methods to the form:

 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 }; 


Let's edit the GetNProps function so that it returns the number of parameters of the “Test” method:

 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; } 


Let's make changes to the CAddInNative :: GetParamDefValue function :

 bool CAddInNative::GetParamDefValue(const long lMethodNum, const long lParamNum, tVariant *pvarParamDefValue) { TV_VT(pvarParamDefValue)= VTYPE_EMPTY; switch(lMethodNum) { case eMethEnable: case eMethDisable: case eMethShowInStatusLine: case eMethStartTimer: case eMethStopTimer: case eMethTest: // There are no parameter values by default break; default: return false; } return false; } 


Thanks to the added line

 case eMethTest: 


in the absence of one or more arguments, the corresponding parameters will be empty ( VTYPE_EMPTY ). If a default value is required for a parameter, it should be specified in the eMethTest section of the switch statement of the CAddInNative :: GetParamDefValue function .

Since the “Test” method can return a value, it is necessary to make changes to the code of the HasRetVal function:

 bool CAddInNative::HasRetVal(const long lMethodNum) { switch(lMethodNum) { case eMethLoadPicture: case eMethTest: return true; default: return false; } return false; } 


And add the executable code of the method to the CallAsFunc function:

 bool CAddInNative::CallAsFunc(const long lMethodNum, tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray) { ... std::wstring s1, s2; switch(lMethodNum) { case eMethLoadPicture: ... break; case eMethTest: if (!lSizeArray || !paParams) return false; s1 = (paParams) -> pwstrVal; s2 = (paParams+1) -> pwstrVal; wstring_to_p(std::wstring(s1+s2), pvarRetValue); ret = true; break; } return ret; } 


Compile the component and bring the configuration code to the form:

  ;  () ("...", "DemoVK", .Native);  = ("AddIn.DemoVK.SomeName");  = .(", ", "!"); ();  


After launching the configuration, we will receive a message: “Hello, World!”, Which indicates that the method worked successfully.



Timer



Task:

  1. To study the implementation of the timer in the demonstration VC
  2. Modify the StartTimer method by adding the ability to transfer the timer interval in parameters (in milliseconds)
  3. Verify the performance of the changes


In WinAPI, you can use the WM_TIMER message to work with time. This message will be sent to your program at a time interval that you specify when creating a timer.

To create a timer, use the SetTimer function:

 UINT SetTimer(HWND hWnd, //   UINT nIDevent, //  ()  UINT nElapse, //  TIMERPROC lpTimerFunc); //    


The operating system will send a WM_TIMER message to the program at the interval specified in the nElapse argument (in milliseconds). In the last parameter, you can specify the function that will be executed each time the timer is triggered. The title of this function should look like this (the name can be any):

 void __stdcall TimerProc (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) 


Consider the implementation of the timer in the demo VK.

Since we are considering the process of developing an external component for the OS of the Windows family, we will not consider the implementation of the timer in other operating systems. For GNU / Linux OS, in particular, the implementation will differ in the syntax of the SetTimer function and TimerProc .

In executable code, the SetTimer method is called , to which the MyTimerProc function is passed :

 m_uiTimer = ::SetTimer(NULL,0,100,(TIMERPROC)MyTimerProc); 


The identifier of the created timer is placed in the m_uiTimer variable so that it can be disabled later.

The MyTimerProc function looks like this:

 VOID CALLBACK MyTimerProc( HWND hwnd, // handle of window for timer messages UINT uMsg, // WM_TIMER message UINT idEvent, // timer identifier DWORD dwTime // current system time ) { if (!pAsyncEvent) return; wchar_t *who = L"ComponentNative", *what = L"Timer"; wchar_t *wstime = new wchar_t[TIME_LEN]; if (wstime) { wmemset(wstime, 0, TIME_LEN); ::_ultow(dwTime, wstime, 10); pAsyncEvent->ExternalEvent(who, what, wstime); delete[] wstime; } } 


The essence of the function is reduced to the fact that the ExternalEvent method is called, which sends a message to the “1C: Enterprise” system.

To expand the functionality of the StartTimer method, we perform the following actions:

We modify the code of the GetNParams method so that it returns the value 1 for the eMethStartTimer method:

 case eMethStartTimer: return 1; 


Let us call the code of the CallAsProc method:

 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; 


Now check the performance. To do this, in the module of the managed configuration application we will write the code:

  ;  () ("...", "DemoVK", .Native);  = ("AddIn.DemoVK.SomeName"); .(2000);  


After starting the configuration, the program will receive messages with an interval of 2 seconds, which indicates the correct operation of the timer.



Interaction with the system "1C: Enterprise"



For interaction between the external component and the “1C: Enterprise” system, the methods of the IAddInDefBase class described in the AddInDefBase.h file are used . We list the most frequently used:

Error message generation

 virtual bool ADDIN_API AddError(unsigned short wcode, const WCHAR_T* source, const WCHAR_T* descr, long scode) 


wcode , scode - error codes (a list of error codes with a description can be found on the ITS disk)

source - the source of the error

descr - error description

Sending a message to the 1C: Enterprise system

 virtual bool ADDIN_API ExternalEvent(WCHAR_T* wszSource, WCHAR_T* wszMessage, WCHAR_T* wszData) = 0; 


wszSource - the source of the message

wszMessage - message text

wszData - transmitted data

Interception of the message is carried out by the procedure for processing an external event.

Registration of the external component in the system "1C: Enterprise"

 virtual bool ADDIN_API RegisterProfileAs(WCHAR_T* wszProfileName) 


wszProfileName - the name of the component.

These methods are sufficient for the full interaction of VK and 1C. To receive data from an external component from the 1C: Enterprise system and vice versa, the external component sends a special message, which in turn is intercepted by the 1C system and, if necessary, calls the methods of the external component to transfer data back.



TVariant data type



When exchanging data between the external component and the “1C: Enterprise” system, the tVariant data type is used. It is described in the types.h file, which can be found on the ITS disk:

 struct _tVariant { _ANONYMOUS_UNION union { int8_t i8Val; int16_t shortVal; int32_t lVal; int intVal; unsigned int uintVal; int64_t llVal; uint8_t ui8Val; uint16_t ushortVal; uint32_t ulVal; uint64_t ullVal; int32_t errCode; long hRes; float fltVal; double dblVal; bool bVal; char chVal; wchar_t wchVal; DATE date; IID IDVal; struct _tVariant *pvarVal; struct tm tmVal; _ANONYMOUS_STRUCT struct { void* pInterfaceVal; IID InterfaceID; } __VARIANT_NAME_2/*iface*/; _ANONYMOUS_STRUCT struct { char* pstrVal; uint32_t strLen; //count of bytes } __VARIANT_NAME_3/*str*/; _ANONYMOUS_STRUCT struct { WCHAR_T* pwstrVal; uint32_t wstrLen; //count of symbol } __VARIANT_NAME_4/*wstr*/; } __VARIANT_NAME_1; uint32_t cbElements; //Dimension for an one-dimensional array in pvarVal TYPEVAR vt; }; 


The tVariant type is a struct , which includes itself:



In general, working with variables of the type tVariant occurs according to the following algorithm:

  1. Determining the type of data currently stored in a variable
  2. Refer to the appropriate mix field for direct data access.


Using the tVariant type greatly simplifies the interaction of the 1C: Enterprise system and the external component.



application



The “examples” directory contains examples for the

examples / 1 article — launching the

examples / 2 demo component — demonstrating the expansion of the

examples / 3 property list — demonstrating expanding the list of methods

Each directory contains a VS 2008 project and a ready configuration 1C.

Download the application

Source: https://habr.com/ru/post/191014/



All Articles