THE BELL

There are those who read this news before you.
Subscribe to get the latest articles.
Email
Name
Surname
How would you like to read The Bell
No spam

Question: External Native Api component in C++ for Linux (Ubuntu x64) on 1C 8.3


I am writing VK, I can not connect to 1s on ubuntu. Even the exapl from 1s is not connected. So a question about it:

1) I'm trying to connect VK from the VNCOMPS example given in the article

(the link can be found at the very end: "Copying").
Internally, the NativeApi project has a makefile. With it, I build the .so library on Ununtu.
But at "Connect the External Component" 1 takes off.
Similarly, if I build using "build.sh" (at the root of the project).

In the makefile itself, I change the flag from m32 to m64, because 1c and the x64 system itself. (with the m32 parameter it doesn't pick up anyway)
Here is an example of calling VK from 1C 8.3:
ConnectionDone = Connect an External Component("/home/alexeyubuntux64-20 gb/Documents/VNCOMP83/example/NativeAPI/AddInNative.so", "AddInNative", ExternalComponentType.Native); There is an article just on this topic.
But, as far as I can see, all these points have already been taken into account and corrected in the VNCOMPS example.

But as a matter of fact business in parameters of compilation. MB 32bit external component hooks up to 32bit 1c normally, but I deployed on Ubuntu x64 1c enterprise83 8.3.5-1486 amd64. And I want to pick up VK to her.

Does anyone have any idea how to solve this issue?
The VNCOMPS example should work, but the build parameters need to be corrected, or the platform itself on which I'm testing is incorrect.

Answer: Interestingly, is it possible to write an external component in Java?

Question: External component (Native) is not connected


Compiled an example with ITS, for 64 and 32 bit systems.

I connect like this:
ConnectionResult = ConnectExternalComponent(CDLLPath, "Comp", ExternalComponentType.Native); One PC connects, the other doesn't. There is a difference in OS. Where the connection goes, there is Win7, where there is no Win10. At the same time, standard components work on a PC where my component does not work.

Tested on different platforms (8.3.4.482, 8.3.6.2100, 8.3.11.2700, 8.3.12.1412).

How to understand why it does not connect?

Answer: vc_redist did not forget?

Question: 1C8 and an external component with the Native type


Good afternoon.
There is a configuration of BP 3.0.50.12 and a desire to implement weighing from the company Scales-Soft using UniServerAuto.
The developers compiled the Native component for Windows 32 and 64, and put it into an archive with the maifest file. There is also an example for 1C, how weight can be calculated. In it, with the help of a layout with binary data, this archive is indicated, as I understand it. In the example, everything is fine: the component is installed, connected, then the connection is established and the weight is read.
But as soon as you start to transfer yourself to 1C, the weight is not read. It seems that everything is simply written, but I don’t understand where the rake is.
Who will have a little time - help, take a look with one eye, maybe the solution is on the surface, but I'm walking somewhere in the wrong place and doing something wrong. I have never worked with Native technology before...

And in the attachment is my processing text

Answer:

Well, I have some news...
I just started looking at the step by step at what point it starts to fail. To do this, I created an empty database and processing with the command. By analogy with the example of the supplier, I transferred the layout to a new conf - it works the second time. Those. First time no, second time yes. This prompted the idea that it would still be necessary in its processing to separate the connection of the component and the object according to different procedures.
Then I transferred it to my database with a layout connection - it works. Fuh, it's already good .... But I would like it without making changes to the configuration, so let's move on

I'm trying to add a layout to processing. Its size immediately increases from 10kb to 3mb and a significant slowdown is noticed - it does not fit. I start to dig towards connection of a component through dll. Those. basically the same as where you started. But there is one "BUT" here: by searching by the name of the dll in the user's folder, I noticed that this dll lies where (as I understand it) the dlls registered in 1C are added:
C:\Users\USER\AppData\Roaming\1C\1cv8\ExtCompT
accordingly, there is no need to use the full path to the dll, you can simply write its name:
ConnectExternalComponent("Add1CUniServerAuto32.dll", "UniServerAuto", ExternalComponentType.Native);

I try ... swears at registration, but returns the result of weighing. It turns out that the dll is already registered, which means you just need to connect it. I remove it and everything works.
I summarize:
1. In the processing of weighing in the procedure When Opening, I added the connection of an external component and the connection to the object.
2. The path to the dll I did not write, I just indicated its name.

Now I sit and think, when did the dll install in 1C? At the time of software installation? Hardly... At the time of running the developer configuration of this dll, where is it set when the form is opened? I don't know, but it seems close to me... What do you think?
And secondly, in a new place, when there is a need to install the same terminal, what needs to be done to make everything work? Install the software completely, run the supplier's config to check the work and then (in theory) my processing should work? Something a bit complicated... Or after installing the software in my processing, do InstallExternalComponent once?

I would love to hear your thoughts on this...

Question: External component.dll


Good day everyone.
A question.
dll component that works fine in 1C 7.7
in 1s 8.1 does not want to boot at all ...
Tried and paste it in C:\Program Files\1cv81\bin\cache1c.dll
I tried to register using regsvr32 "C:\Program Files\1cv81\bin\cache1c.dll"
Registered without problems.
When I try to access it, I get an error message:

Error loading external component! cache1c.dll
Procedure Execute ButtonClick(Button) Attempt to Load External Component( "C:\Program Files\1cv81\bin\cache1c.dll"); Exception Report( "Error loading external component!"+ "cache1c.dll"); End of Attempt; Attempt // Get component object. // m = New ("cache1c.GTMcmd" ); m = New COMObject("cache1c.GTMcmd" ); Exception Report(); End of Attempt; EndProcedure

Answer: Banal to the point of impossibility...
It is necessary to sustain pauses between calls (milliseconds)...
Procedure ButtonExecuteClick(Button) Attempt // Get the component object. m = New COMObject("cache1c.GTMcmd" ); Exception Report( "Could not create external component object"); End of Attempt; m.RemoteHost = "192.168.1.101" ; m.RemotePort = 6330; m.Connect(); m.pause(100); ...... etc
For 1s 7.7 - this is not necessary, it turns out that the handling is faster.

Question: The work of an external component with the 1C server ...


Good afternoon,

There is an external component written in C++, whose task is to receive information from an external database and return the result of the query in the form of a Table of Values ​​in 1C.
To form a table of values ​​at the current moment, the IDispatch* pBackConnection interface is used, which is received as a parameter in the Init() function. Further, I simply, using the 1C functions, form a table of values, fill it in and return it to the second parameter in CallAsFunc (...).
Problems began with the transition to 1C thin clients. On the server side, the external component does not really start. You can run it on the client side, but it all looks like crutches and falls out of the general "client-server" logic in 1C. For example, the client does not understand what a table of values ​​is, problems with "global" variables, sessions, etc.
NativeAPI is even more truncated in this regard.
Dancing with a tambourine led to the fact that I was able to launch an external component under the 1C server, BUT the work happens until an attempt is made to call Invoke on pBackConnection. The 64-bit version of the 8.2 server tries to do something until it fails due to a timeout, the 32-bit one (of course, VC is also 32 bit) just immediately falls off.
I assume that the 1C server does not serve this mode of operation.
Accordingly, questions arise, is it temporary or is the logic of 1C reduced to the abolition of this scheme of work? If it is impossible to create internal 1C structures (a table of values) in this way, is there, in principle, a description of what a table of values ​​is at the system level in order to try to create it in C ++, fill it in, and then just slip 1C as a return parameter? I would like to at least get a direction in which direction to dig.

Thank you.

Answer:

You write one thing and mean another.
In the 1C environment, declaring variables that will be visible in different sessions is not impossible now, and it was not possible before. A different session is a physically different process.
Session is a database connection session, i.e. user session. Or do you put something of your own and this concept?

Within one session, it was possible, and it is possible now, to declare variables in the session module, which will live and be visible within the session from different places ... actually, there are 4 of them.
- Session module;
- Regular application module;
- Managed application module;
- External connection module.

And of course, you need to remember about the context. The server context is not directly accessible on the client side and vice versa.

In general, the 1C architecture provides that data exchange will go on:
- by means of parameters/returns of procedures/functions;
- by means of the so-called session parameters (they cannot be objects, you can actually see it in the palette).

The table on the form... and it is connected with any table of object (processings, for example)? or not. If yes, then it is also available on the server (&AtServer) and edit there....

And yes, the ValueTable is not available in UV on the client side. Well, 1C decided so.

Come on! Here it works with Excel, it works with FSO and a bunch of everything else, but here it doesn’t work. Catch the error and analyze....

Attempt
...
your actions
...
Exception
str = DescriptionError();
End of Attempt;

With modern hardware capabilities, this is not an argument at all.

Purely your personal opinion. Has nothing to do with reality. Not in any way. I repeat once again, 1C works great with COM. Both with in-proc and out-proc.

Give the code that you upload and apply to VK.

By the way, VK... in your case, is it COM or Native API?
If COM, then you register it as... through regsvr32... then how do you "resolve" the bit depth issue?

Question: Installing an external component


Please tell me how to install an external component. When executing the following code, an error is thrown. Find NameDecl.dll in the layout

Attempt to SetExternalComponent("GeneralLayout.Layout"); Exception EndTry ;
Error: Plugin installation failed!

Answer: ()
ConnectExternalComponent("GeneralLayout.Layout", "NameDecl", ExternalComponentType.Native) returns FALSE ,
New("AddIn.NameDecl.CNameDecl", Undefined) = (()): Type not defined (AddIn.NameDecl.NameDecl)

Question: Native dll is not connected to 1s 8.1 (fptrwin32_fz54_9_11_0_5549.dll)


Hello.
1C updated the dll for online cash registers atol for fdd 1.05 (included in the fptrwin32_fz54_9_11_0_5549.dll maintenance processing).
I have an old 1C 8.1. Unlike 8.2, it does not support working with external equipment in the same way as 8.2, so you first need to register the dll in windows, and then only connect it to 1C?

ProgID = "AddIn.IntegrationComponent.ATOL_KKT_1C83_V9"; LoadExternalComponent("C:\fptrwin32_fz54_9_11_0_5549.dll"); ConnectExternalComponent(ProgID); Driver = New(ProgID);

However, the old processing was written in "technology" com, and the new native. Accordingly, when registering, regsvr32 gives an error:
The module was loaded, but the DllRegisterServer entry point was not found. And suggests checking that this file is the correct dll or OCX file.
Who faced a similar situation, how did you get out? I understand that a similar problem will be in 7.7.
Code 8.2:

Layout = GetLayout("IntegrationComponent"); Address = PlaceInTempStorage(Layout); ConnectExternalComponent(Address, "IntegrationComponent", ExternalComponentType.Native); Driver = New("AddIn.IntegrationComponent.ATOL_KKT_1C83_V9");

1C 8.2:
Connect External Component(<Местоположение>, <Имя>, <Тип>)
1C 8.1:
Connect External Component(<Идентификатор объекта>)
Options:
<Идентификатор объекта>(required)
Type: String. ProgID (Programmatic Identifier) ​​of the external component object. Must match the information in the system registration database (Registry).
Description:
Connects external component objects to 1C:Enterprise.
Not available on the 1C:Enterprise server. Not used in the external connection module.
Note:
External components are compatible with 1C:Enterprise 7.7 components.
Example:
Attempt
ConnectExternalComponent("AddinObject.Scanner");
alert("Barcode scanner component loaded");
Exception
alert("Barcode scanner component not loaded");
End Attempts

Is there any way to connect this dll to 8.1 or not?

Thank you!

Answer:

I also recently had this problem. It was not possible to convert to a later version of 1s. dll with which this configuration works simply stopped working and 1c fell out with an error.
The problem was solved in the following way:
I created an empty database 8.3 in which I processed the initialization of the component and then from 8.1 via COM connection I accessed the database created earlier and initialized the component there. Then, already in 8.1, I called the methods of this component.
Of course, this is a crutch, but I have not found another way out yet (

Code example 8.3:
Rem Driver Export;
Function ConnectionComponentsCCP() Export
Attempt

Layout = GetLayout("IntegrationComponent");
Address = PlaceInTempStorage(Layout);
ConnectExternalComponent(Address, "IntegrationComponent", ExternalComponentType.Native);
Driver = New("AddIn.IntegrationComponent.SMDrvFR1C20");
result = true;​

Exception

result = false;​

End of Attempt;
Return Result
EndFunctions

Code example 8.1

Function CreateDriverObject(Driver) Export

result = true;

Attempt

ConnectionString="File="""Path to database""";
ComObject= New COMObject("V83.ComConnector");
Connect = ComObject.Connect(ConnectionString);

Processing = Connect.Processing.ConnectingExternalComponent.Create();
ConnectionResult = Processing.ConnectingKKT Components();
If Result Connects Then
Driver = Processing.Driver;
EndIf;​

Exception
Anyone who has done this or experienced similar issues, please explain. simple example the principle itself. It seems that everything is clear with the connection of external components.

// An example of filling the table of values TK.Clear(); Request = New Request; Query.Text = "SELECT | Nomenclature. Link HOW Nomenclature | FROM | Directory.Nomenclature AS Nomenclature"; QueryResult = Query.Execute(); Sampling = QueryResult.Select(); While Sampling.Next() Loop Str = TK.Add(); FillPropertyValues(Pr, Sampling); EndCycle;
Could you explain with this example what part of the code is usually taken out. It would be logical to take out the part with the request, but then how can we access the database from the external component from the external component, bypassing the platform? The text is pointless. Or take out the very formation of the tabular part. Please share your experience with this.

Answer: And that the word "Incompatible" always means the word "Bad"? Yes, it seems to me that if I call my style “1C: The worst programming on this scripting engine that exists in nature (translated into a literary language)!” and then for sure there will be those who want to check out this beast. And it looks like a classic: “I haven’t read Pasternak, but I completely disagree with him!” :)

Question: Connecting an external component in 1s 8.3.6 and Win8


It is necessary to connect the vk_rs232.dll ExternalComponent to the self-written configuration. Like registered through regsvr32.exe. "It seems" because I received a message that "the component is registered, but something is wrong with the firewall." Relying on the first half of the message, I write the code in 1s
AfterConnection = New NotificationDescription("AfterConnectionVK",ThisForm); StartExternalComponentInstallation(,"C:\Controller\vk_rs232.dll"); StartConnectingExternalComponent(AfterConnecting,"C:\Controller\vk_rs232.dll","DLL_Scales");
and I get the error that
"External component installation failed! The component for the client application you are using may be missing!".

And now I don't understand
1. Maybe the component is not registered in the registry - how can I check it there?
2. Maybe its "version" does not work under Win8, although I have it 32-bit.
3. Maybe 1s itself is too new, i.e. accordingly cannot work with this dll?
4. Well, it's banal - I'm writing something wrong.

Answer: And all this led me to the next problem. VneshComp Installed, now you need to connect it. And here are both options.
ConnectExternalComponent("C:\Controller\vk_rs232.dll","Scales")
ConnectExternalComponent("GeneralLayout.Layout","Scales")

Syntax option: By name and location

Syntax:

Connect External Component(<Местоположение>, <Имя>, <Тип>)
Options:

<Местоположение>(required)

Type: String.
The location of the external component.
Location can be used:
path to the file of the external component in the file system (not available on the web client), not a ZIP archive;
full name a layout that stores binary data or a ZIP archive;
The URL to the external component, in the form of binary data or a ZIP archive, in a , similar to GetNaviLink.
<Имя>(required)

Type: String.
The symbolic name of the pluggable external component.
The name must follow the naming conventions of the built-in language.
<Тип>(optional)

Type: ExternalComponent Type.
The type of external component to be connected.
Not used if the component is packaged in a ZIP archive.
Description of the method variant:

Connects components made using Native and COM technology.
The component can be stored in an infobase or configuration layout as binary data or in a ZIP archive.
For the "Thin client" and "Web client" launch modes, the component must be previously installed using the Install External Component method.
Syntax option: By ID

Syntax:

Connect External Component(<ИдентификаторОбъекта>)
Options:

<ИдентификаторОбъекта>(required)

Type: String.
External component object identifier in the form of ProgID (Programmatic Identifier) ​​of the MS Windows registry (for example: "AddIn.Scanner").
Must match the information in the system registration database (Registry).
Description of the method variant:

The component must be implemented using COM technology and registered in the MS Windows registry.
These components are compatible with 1C:Enterprise 7.7 components.
Attention! The variant of the method does not work on the server and in the outer connection.
Return value:

Type: Boolean.
True - the connection was successful.
Description:

Connects an external component to 1C:Enterprise.
External components can be stored in the infobase or configuration layouts as a ZIP archive or as binary data, or in a file system file.
When working on a thin client and web client, the component must be pre-installed.

Availability:

Thin client, web client, server, external connection.
Note:

External components can be implemented using Native API or COM technology. Components made using COM technology are compatible with 1C:Enterprise 7.7 components.
The web client can only work with components in an infobase that are packed into an archive.
The thin client can work with components in the infobase, packed in an archive, and components located in the file system.
The thick client can work with all storage options for components. In this case, if the component is installed using the InstallExternalComponent method, then the installed component is used, and if it is not installed, then the component will be received at the time of connection.
The server can work with all components. The component is cached for the server session.
Example:

If ConnectExternalComponent("AddinObject.Scanner") Then
alert("Barcode scanner component loaded");
Otherwise
alert("Barcode scanner component not loaded");
EndIf;

  • tutorial

Introduction

This article gives an idea of ​​how external components work in the 1C:Enterprise system.
The process of developing an external component for the 1C: Enterprise version 8.2 system running under the Windows operating system with a file version of work will be shown. This option is used in most solutions designed for small businesses. The VC will be implemented in the C++ programming language.

External components "1C: Enterprise"

"1C: Enterprise" is an extensible system. For expansion functionality system uses external components (VC). From a developer's point of view, a VC is an 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 tasks that are difficult or even impossible to implement using the programming language built into 1C: Enterprise. In particular, this class includes tasks that require low-level interaction with the operating system, for example, to work with specific hardware.
The 1C:Enterprise system uses two technologies for creating external components:
  • using Native API
  • using COM technology
With the given restrictions, the difference between the above two technologies is insignificant, so we will consider the development of VK using the Native API. If necessary, the implemented developments can be applied to the development of VC using COM technology, and also, with minor modifications, applied for use in the 1C: Enterprise system with other operating options other than the file mode of operation.
VK structure
The external component of the 1C:Enterprise system is presented as a DLL library. The library code describes the IComponentBase derived class. In the class being created, the methods responsible for implementing the functions of the external component must be defined. The overridden methods will be described in more detail later in the course of the presentation of the material.

Launching the demo VK

A task:
  1. Assemble the external component supplied with the ITS subscription and designed to demonstrate the main capabilities of the external component mechanism in 1C
  2. Connect the demo component to the 1C configuration
  3. Make sure that the declared functions work correctly
Compilation
The demo VC is located on the ITS subscription disk in the "/VNCOMP82/example/NativeAPI" directory.
We will use Microsoft Visual Studio 2008 to assemble the demo VC. Other versions of this product do not support the used Visual Studio project format.


Open the AddInNative project. In the project settings, we connect the directory with the header files necessary to build the project. By default, they are located on the ITS disk in the directory /VNCOMP82/include.
The build result is a file /bind/AddInNative.dll. This is the compiled library for connecting to the 1C configuration.
Connecting VK to 1C configuration
Let's create an empty configuration 1C.
Following is the code for the managed application module.
var DemoComp; SystemStartup Procedure() ConnectExternalComponent("...\bind\AddInNative.dll", "DemoVK", ExternalComponentType.Native); DemoComp = New("AddIn.DemoVK.AddInNativeExtension"); EndProcedure
If no error was reported when starting the 1C configuration, then the VK was successfully connected.
As a result of the execution of the above code, an object appears in the global visibility of the configuration DemoComp An that has properties and methods that are defined in the external bean code.
Demonstration of the embedded functionality
Let's check the performance of the demo VK. To do this, let's try to set and read some properties, call some VC methods, and also receive and process a VC message.
The documentation supplied on the ITS disk states the following functionality of the demo VC:
  1. Component object state management
    Methods: Turn on, Switch off
    Properties: Included
  2. Timer control
    Every second, the component sends a message to the 1C: Enterprise system with parameters Component, timer and a system clock counter string.
    Methods: StartTimer, StopTimer
    Properties: There is a timer
  3. Method ShowInStatusLine, which displays in the status bar the text passed to the method as parameters
  4. Method Upload Image. Loads an image from the specified file and transfers it to the 1C:Enterprise system as binary data.
Let's make sure that these functions work. To do this, we will execute the following code:
var DemoComp; SystemStart() Procedure ConnectExternalComponent(...); DemoComp = New("AddIn.DemoVK.AddInNativeExtension"); DemoComp.Disable(); Notify(DemoComp. Enabled); DemoComp.Enable(); Notify(DemoComp. Enabled); DemoComp.StartTimer(); EndProcedure ProcedureExternalEventHandler(Source, Event, Data) Report(Source + " " + Event + " " + Data); EndProcedure
The result of running the configuration is shown in the image


The results of method calls are displayed in the "Messages" panel DemoComp.Disable() and Demo.Comp.Enable(). The subsequent lines on the same panel contain the results of processing messages received from VK - Source, Event and Data respectively.

Arbitrary external component name

Task: Change the name of the external component to an arbitrary one.
The previous section used the identifier AddInNativeExtension, the meaning of which was not explained. In this case AddInNativeExtension is the name of the extension.
The VC code defines a method RegisterExtensionAs, which returns the name to the 1C: Enterprise system, which is necessary for the subsequent registration of the VC in the system. It is recommended to specify an identifier that, to a certain extent, reveals the essence of the external component.
Here is the full code of the method RegisterExtensionAs with the extension name changed:
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 example above, the VK name has been changed to Some Name. Then, when connecting the VK, you must specify a new name:
DemoComp = New("AddIn.DemoVK.SomeName");

Expansion of the list of VK properties

A task:
  1. Study the implementation of VC properties
  2. Add a read-write property of a string type
  3. Add a read/write string type property that stores the data type of the property last set. No action is taken when setting the property value

To define the properties of the component being created, the developer needs to implement the following methods in the code of the AddInNative.cpp library:
GetNProps
Returns the number of properties for this extension, 0 if there are no properties
FindProp
Returns the ordinal number of the property whose name is passed in the parameters
GetPropName
Returns the name of the property by its ordinal and by the passed language ID
GetPropVal
Returns the value of the property with the specified ordinal
SetPropVal
Sets the value of the property with the specified ordinal
IsPropReadable
Returns the readability flag of the property with the specified ordinal
IsPropWritable
Returns the property writability flag with the specified sequence number


Consider the implementation of the above class methods CAddInNative.
The demo VC defines 2 properties: Included and There is a timer (IsEnabled and IsTimerPresent).
Two arrays are defined in the global scope of the library code:
static wchar_t *g_PropNames = (L"IsEnabled", L"IsTimerPresent"); static wchar_t *g_PropNamesRu = (L"Enabled", L"There is a Timer");
which store the Russian and English names of the properties. In header file AddInNative.h an enum is defined:
enum Props ( ePropIsEnabled = 0, ePropIsTimerPresent, ePropLast // Always last );
ePropIsEnabled and ePropIsTimerPresent, respectively having the values ​​0 and 1, are used to replace the ordinal 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 only used inside the component's code and are not accessible from outside.
The FindProp and GetPropName methods search through arrays g_PropNames and g_PropNames.
To store the value of the fields in the library module, the CAddInNative class has properties that store the value of the component's properties. Methods GetPropVal and SetPropVal respectively return and set the value of these properties.
Methods IsPropReadable and IsPropWritable and return true or false, depending on the passed ordinal of the property according to the application logic.
To add a custom property:

  1. Add the name of the property to be added to arrays g_PropNames and g_PropNames(file AddInNative.cpp)
  2. To enumeration props(file AddInNative.h) before ePropLast add a name that uniquely identifies the property to be added
  3. Organize memory for storing property values ​​(create component module fields that store the corresponding values)
  4. Make changes to methods GetPropVal and SetPropVal to interact with the memory allocated in the previous step
  5. In accordance with the application logic, make changes to the methods IsPropReadable and IsPropWritable
Items 1, 2, 5 need no explanation. Details of the implementation of these steps can be found in the appendix to the article.
Let's name the test properties Test and Type Check respectively. Then, as a result of paragraph 1, we have:
static wchar_t *g_PropNames = (L"IsEnabled", L"IsTimerPresent", L"Test", L"TestType"); static wchar_t *g_PropNamesRu = (L"Enabled", L"HasTimer", L"Test", L"CheckType");
Enumeration props will look like:
enum Props ( ePropIsEnabled = 0, ePropIsTimerPresent, ePropTest1, ePropTest2, ePropLast // Always last );
To significantly simplify the code, we will use STL C++. In particular, for working with strings WCHAR, connect the library wstring.
To store the value of a method Test, define in the class CAddInNative in scope private field:
string test1;
To transfer string parameters between 1C:Enterprise and an external component, the 1C:Enterprise memory manager is used. Let's take a closer look at his work. To allocate and free memory, respectively, use the functions AllocMemory and FreeMemory, defined in the file MemoryManager.h. If it is necessary to pass a string parameter to the 1C: Enterprise system, the external component must allocate memory for it by calling the function AllocMemory. Its prototype looks like this:
virtual bool ADDIN_API AllocMemory (void** pMemory, unsigned long ulCountByte) = 0;
where pMemory- the address of the pointer in which the address of the allocated memory will be placed,
ulCountByte- the size of the allocated memory area.
An example of allocating memory for a string:
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 data types, we describe the function wstring_to_p. It takes a wstring string as a parameter. The result of the function is a filled structure tVariant. 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 method GetPropVal will take the form:
case ePropTest1: wstring_to_p(test1, pvarPropVal); break;
method SetPropVal:
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 a class field CaddInNative
uint8_t last_type;
in which we will store 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 requesting to read the value of the second property, we will return the value last_type, which is required by the specified task.
Let's check the performance of the changes made.
For this, we present appearance configuration 1C to the view:
var DemoComp; SystemStartup Procedure() ConnectExternalComponent("...", "DemoVK", ExternalComponentType.Native); DemoComp = New("AddIn.DemoVK.SomeName"); DemoComp.CheckType = 1; Report(String(DemoComp.TypeCheck)); DemoComp.Test = "Vasya"; Report(String(DemoComp.Test)); DemoComp.Test = "Petya"; Report(String(DemoComp.Test)); Report(String(DemoComp.TypeCheck)); EndProcedure
As a result of the launch, we get a sequence of messages:
3
Vasya
Petya
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 type code of the last property set. 3 corresponds to an integer value, 22 - to a string value. The correspondence of types and their codes is established in the file types.h, which is located on the ITS disk.

Expanding the list of methods

A task:
  1. Extend the functionality of the external component with the following functionality:
  2. Learn how to implement external component methods
  3. Add method-function Function1, which takes two strings ("Parameter1" and "Parameter2") as a parameter. As a result, a string of the form is returned: “Check. Parameter1, Parameter2"
  4. Verify that the changes made are working.

To define the methods of the component being created, the developer needs to implement the following methods in the code of the AddInNative library:
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 there is no method with this number or has no parameters, returns 0
GetParamDefValue
Returns the default value of the specified parameter of the specified method
HasRetVal
Returns a flag that the method with the specified return value ordinal has: true for methods with a return value and false otherwise
CallAsProc
false, a run-time error occurs and the execution of the 1C: Enterprise module stops. Memory for the array of parameters is allocated and released by 1C: Enterprise.
CallAsFunc
Executes the method with the specified ordinal. If the method returns false, a run-time error occurs and the execution of the 1C: Enterprise module stops. Memory for the array of parameters is allocated by 1C: Enterprise. If the return value is of type string or binary data, the component allocates memory with the function AllocMemory memory manager, writes data there and stores this address in the corresponding field of the structure. 1С: The enterprise will release 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"Enable", L"Disable", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadImage");
and enum:
enum Methods ( eMethEnable = 0, eMethDisable, eMethShowInStatusLine, eMethStartTimer, eMethStopTimer, eMethLoadPicture, eMethLast // Always last );
They are used in functions GetNMethods, FindMethod and GetMethodName, by analogy with the description of properties.
Methods GetNParams, GetParamDefValue, HasRetVal implement switch, depending on the passed parameters and application logic, return the required value. Method HasRetVal in its code has a list of only methods that can return a result. For them it returns true. For all steel methods returns false.
Methods CallAsProc and CallAsFunc contain directly executable method code.
To add a method that can only be called as a function, you need to make the following changes in the source code of the external component:
  1. Add method name to arrays g_MethodNames and g_MethodNames(file AddInNative.cpp)
  2. Add a meaningful method identifier to the Methods enum (file AddInNative.h)
  3. Make changes to the function code GetNParams according to program logic
  4. If necessary, make changes to the code of the method GetParamDefValue if you want to use the default values ​​of the method parameters.
  5. Make changes to a function HasRetVal
  6. Make changes to the logic of the functions CallAsProc or CallAsFunc, by placing the directly executable code of the method there
Let's bring arrays g_MethodNames and g_MethodNames, as well as enumeration methods to the view:
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"Enable", L"Disable", L"ShowInStatusLine", L"StartTimer", L"StopTimer", L"LoadImage", L"Test");

Enum Methods ( eMethEnable = 0, eMethDisable, eMethShowInStatusLine, eMethStartTimer, eMethStopTimer, eMethLoadPicture, eMethTest, eMethLast // Always last );
Let's edit the function GetNProps 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 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: case / 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 have an empty value ( VTYPE_EMPTY). If you need a default value for a parameter, you must specify it in the section eMethTest function switch statement CAddInNative::GetParamDefValue.
Since the "Test" method can return a value, you need to make changes to the function code HasRetVal:
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 function CallAsFunc:
bool CAddInNative::CallAsFunc(const long lMethodNum, tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray) ( ... std::wstring s1, s2; switch(lMethodNum) ( case eMethLoadPicture: ... break; case eMethTest: if (!lSizeArray || !paParams) return false; s1 = (paParams) -> pwstrVal; s2 = (paParams+1) -> pwstrVal; wstring_to_p(std::wstring(s1+s2), pvarRetValue); ret = true ; break; ) return ret; )
Let's compile the component and bring the configuration code to the form:
var DemoComp; SystemStartup Procedure() ConnectExternalComponent("...", "DemoVK", ExternalComponentType.Native); DemoComp = New("AddIn.DemoVK.SomeName"); lane = DemoComp.Test("Hi, ", "World!"); Notify(trans); EndProcedure
After starting the configuration, we will receive a message: “Hello, World!”, Which indicates that the method has worked successfully.

Timer

A task:
  1. Study the implementation of the timer in the demo VK
  2. Modify the "StartTimer" method by adding the ability to pass in the parameters the timer interval (in milliseconds)
  3. Verify that the changes made are working.

In WinAPI, to work with time, you can use the message WM_TIMER. This message will be sent to your program at the time interval you specify when creating the timer.
To create a timer use the function SetTimer:
UINT SetTimer(HWND hWnd, // window handle UINT nIDevent, // timer ID (number) UINT nElapse, // delay TIMERPROC lpTimerFunc); // function pointer
The operating system will send a message WM_TIMER to the program with the interval specified in the argument nElapse(in milliseconds). In the last parameter, you can specify a function that will be executed every time the timer fires. The header of this function should look like this (the name can be anything):
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 Windows OS 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 function SetTimer and TimerProc.
The method is called in the executable code SetTimer, to which the function is passed MyTimerProc:
m_uiTimer = ::SetTimer(NULL,0,100,(TIMERPROC)MyTimerProc);
The ID of the created timer is placed in a variable m_uiTimer so that you can turn it off later.
Function MyTimerProc as follows:
VOID CALLBACK MyTimerProc(HWND hwnd, // handle of window for timer messages UINT uMsg, // WM_TIMER message UINT idEvent, // timer identifier DWORD dwTime // current system time) ( if (!pAsyncEvent) return; wchar_t *who = L "ComponentNative", *what = L"Timer"; wchar_t *wstime = new wchar_t; if (wstime) ( wmemset(wstime, 0, TIME_LEN); ::_ultow(dwTime, wstime, 10); pAsyncEvent->ExternalEvent(who , what, wstime); delete wstime; ) )
The essence of the function is that the method is called ExternalEvent, which sends a message to the 1C: Enterprise system.
To expand the functionality of the method StartTimer let's do the following:
Modifying the method code GetNParams so that it is for the method eMethStartTimer returned value 1:
case eMethStartTimer: return 1;
Here is the method code CallAsProc to the view:
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 let's check the functionality. To do this, in the module of the managed application of the configuration, we will write the code:
var DemoComp; SystemStartup Procedure() ConnectExternalComponent("...", "DemoVK", ExternalComponentType.Native); DemoComp = New("AddIn.DemoVK.SomeName"); DemoComp.StartTimer(2000); EndProcedure
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 1C: Enterprise system

To interact between the external component and the 1C:Enterprise system, methods of the IAddInDefBase class described in the file AddInDefBase.h. We list the most commonly used:
Error message generation
virtual bool ADDIN_API AddError(unsigned short wcode, const WCHAR_T* source, const WCHAR_T* descr, long scode)
wcode, code- error codes (a list of error codes with a description can be found on the ITS disk)
source- error source
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- message source
wszMessage- Message text
wszData- transmitted data
The interception of the message is carried out by the procedure HandlingExternalEvent
Registration of an external component in the 1C:Enterprise system
virtual bool ADDIN_API RegisterProfileAs(WCHAR_T* wszProfileName)
wszProfileName- component name.
These methods are sufficient for the full interaction of VK and 1C. To receive data by 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 send back data.

tVariant data type

When exchanging data between an 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* pstr2_t Lint3 ; //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; );
Type of tVariant is a structure that includes:
  • mixture (union) intended directly for data storage
  • data type identifier
In general, working with variables of type tVariant occurs according to the following algorithm:
  1. Determining the type of data currently stored in a variable
  2. Access to the corresponding field of the mixture, for direct access to data
Type usage tVariant greatly simplifies the interaction of the 1C: Enterprise system and an external component

Application

The "examples" directory contains examples for the article
examples/1 - run the demo component
examples/2 - property list extension demo
examples/3 - demonstration of method list extension
Each directory contains a VS 2008 project and a prebuilt 1C configuration.

Often, programmers have problems connecting external components (for example, commercial equipment drivers) when users work with 1C by connecting to the server through the terminal.

At the same time, users see, for example, the picture presented in the announcement of the article.

While when working from local computers, there are no problems with connecting external components.

What is it connected with? This is because when users work through the Terminal Server, they have fewer rights than when they work on the local computer.

This is easy to verify if you log into the terminal server under an account with administrative rights.

The reason for this difference is that 1C cannot register an external component in the registry when the user works in the terminal under normal rights, because a normal user does not have write permissions to the system registry branch HKEY_CLASSES_ROOT.

In publications on the topic of connecting external components in the terminal, a variety of methods for solving this problem are offered.

For example, these:

1. Run 1C for the first time under administrative rights.

This option doesn't always work. I'll explain why below.

2. Give ordinary users of the terminal the right to write to the system registry branch HKEY_CLASSES_ROOT.

Insufficiently "advanced" users should not do this, otherwise there may be problems.

3. Using various "gadgets" register VK on behalf of a user with full rights.

It's not good to eat either.

So what is the best way to get out of this situation?

I offer my solution to this problem. In my opinion - simple and beautiful, not previously offered at the infostart.

Investigating this problem, I wondered - why does 1C even try to register VK along a new path? After all, it is already registered in the system.

The thing turned out to be that in typical 1C configurations (for example, "Trade Management") the following syntax of the global context method is used ConnectExternalComponent() :

ConnectExternalComponent("Reference.ConnectedEquipment.Layout.DriverATOLBarcodeScanner", "ATOLScanner");

As you can see, the VK driver is connected from the "DriverATOLScannerBarcode" layout of the "Connected Equipment" directory.

What then happens?

1C saves the component in the user's temporary folder, for example "C:\Documents and Settings\User\Local Settings\Temp\1032\v8_4_12.tmp"

and tries to register it in the HKEY_CLASSES_ROOT registry branch along this path.

On the terminal, ordinary users do not have rights to change this registry branch, so the component is not connected for them.

Now about how to get out of this situation.

Global context method ConnectExternalComponent() has several syntax options. This is what we will use.

So, step by step:

1. Register the external component using the regsvr32.exe utility on the terminal server in the C:\WINDOWS\SYSTEM32 folder for a 32-bit OS or in the folder C:\WINDOWS\SYSWOW64 for 64-bit OS.

2. Use one of two additional syntax options for the ConnectExternalComponent() method:

Option 1:

ConnectExternalComponent("C:\WINDOWS\SysWOW64\Scaner1C.dll", "ATOLScanner", ExternalComponentType.COM);

DriverObject = New("AddIn.ATOLScanner.Scanner45");

Option 2:

ProgID = "AddIn.Scanner45";

Connect External Component (ProgID);

DriverObject = New(ProgID);

In my opinion, option #2 is preferable.

At the same time, 1C does not try to re-register the VC along a new path in the registry, and thus, all problems are solved.

Well, that's all. Good luck in job!

For example, it will not be possible to rewrite a component if you are not its author and there are simply no source codes. Or if the simplest types supported by Native API technology (number, string, boolean, date) are not enough for its operation.

There are no special problems when working with the file base. The scheduled task is called in the background process of a regular user. Therefore, client calls are available to him. There is no client context in the server database when a scheduled task is started, so the call ConnectExternalComponent() not available.

In this case, you can call the component on the client. To do this, it is enough to launch another 1C session from the scheduled task on the server in which to perform the necessary actions on the client. Well, do not forget to end the running session later.

Let's say that in our scheduled task, we generate and save a report that uses the external COM component NameDeclension.dll to decline the full name. On the file base, such a scheduled task will work correctly, but it will not work on the server component.

To fix the problem, let's add a procedure to the scheduled task module that will launch another session in server mode and in it will execute a report generation call on the client from external processing.

#If Client Then Procedure ExecuteFormationAndSavingReport() Export If ConnectExternalComponent("CommonLayout.NAMEDECL","Cl",ExternalComponentType.COM) Then Component = New ("AddIn.Cl.NameDeclension"); //Here is the code for generating and saving the report. ElseJoinRegisterLog("RegistTask", LevelJoinLog.Error, "Failed to connect the external component on the client"); EndIf; End of Procedure #Otherwise Procedure ExecuteFormationAndSavingReport() Export ExecuteOperationOnClient("RegularTasks.ExecuteFormationAndSavingReport()"); EndProcedure Procedure PerformOperationOnClient(ParameterToExecute) ExportUserName = ""; UserPassword = ""; PathToExternalProcessing = "c:/temp/Autostart.epf"; Quote = """"; DirectoryBIN = DirectoryProgram(); PathToConfiguration = InfoBase ConnectionString(); ConfigurationPath = StrReplace(ConfigurationPath, Quote, Quote + Quote); StartString = Quote + Bin Directory + "1cv8.exe" + Quote + " ENTERPRISE" + " /IBConnectionString " + Quote + ConfigurationPath + Quote + " /N " + Quote + Username + Quote + " /P " + Quote + UserPassword + Quote + " /Execute " + Quote + PathToExternalProcessing + Quote + " /C " + Quote + ParameterToExecute + Quote; StartApplication(StartString); EndProcedure #EndIf

External processing code that will simply cause the client context to print the required report from the scheduled jobs module and end the session after the report is generated.

Attempt to Execute(StartupParameter); Exception EndTry; ShutdownSystem(False);

The convenience of the solution is that when setting up scheduled tasks, it does not matter in what mode the task will be launched. If the database is a file one, then the necessary procedure will immediately start. If the database is server-side and there is no client context at startup, then a new session will be initialized and the procedure will work correctly in the client context.

Code for a typical application. Theoretically, it will work in a completely similar way in a managed one.

p.s. Also, this approach can be used to execute any client procedures in scheduled jobs.

THE BELL

There are those who read this news before you.
Subscribe to get the latest articles.
Email
Name
Surname
How would you like to read The Bell
No spam