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:
- using the native API
- using COM technology
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:
- Assemble the external component supplied with an ITS subscription and intended to demonstrate the basic capabilities of the external component mechanism in 1C
- Connect the demo component to the 1C configuration
- 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:
- Management of the state of the object components
Methods: Enable , Disable
Properties: Enabled - 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 - The ShowVStatStatus method that displays the text passed to the method as parameters in the status bar
- 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:
- To study the implementation of the properties of VK
- Add a read / write string property
- 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.
- 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
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:
- Add the name of the property being added to the g_PropNames and g_PropNamesRu arrays ( AddInNative.cpp file)
- Add a name in the Props enumeration ( AddInNative.h file) before ePropLast that uniquely identifies the property being added.
- Organize memory for storing property values (create component module fields that store the corresponding values)
- Make changes to the GetPropVal and SetPropVal methods to interact with the memory allocated in the previous step
- 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
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:
- Extend the functionality of the external component with the following functionality:
- Examine methods for implementing external component methods
- 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
- 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
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:
- Add method name to g_MethodNames and g_MethodNamesRu arrays ( AddInNative.cpp file)
- Add a meaningful method identifier to the Methods enumeration (file AddInNative.h )
- Make changes to the code of the GetNParams function in accordance with the program logic
- If necessary, make changes to the code of the GetParamDefValue method if you want to use the default values of the method parameters.
- Make changes to HasRetVal function
- 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
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:
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:
- To study the implementation of the timer in the demonstration VC
- Modify the StartTimer method by adding the ability to transfer the timer interval in parameters (in milliseconds)
- 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,
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,
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
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; _ANONYMOUS_STRUCT struct { char* pstrVal; uint32_t strLen;
The tVariant type is a struct , which includes itself:
- mixture (union) intended for data storage directly
- data type identifier
In general, working with variables of the type tVariant occurs according to the following algorithm:
- Determining the type of data currently stored in a variable
- 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