This feature is implemented in 1C:Enterprise version 22.214.171.1248.
In short, you can now use extensions to modify the modules of standard configurations and add new modules.
Or rather, now you can change any modules, with the exception of ordinary form modules:
- Common modules
- Object modules (object module, manager module, etc.) for all types of objects
- Session module
- Managed application module
- External connection module
- Command modules
- Form modules
Please note that you were already able to modify managed form modules in the past, but now we have introduced some changes to this process.
For the simplicity sake, we will refer to the phrase “procedure/function” as “method”. Thus, all changes that you can implement in the modules can be divided into 4 groups:
- Hooking. You can intercept any methods of standard configurations, framing them with the custom ones, or even replacing them entirely.
- Adding handlers. You can add custom handlers to standard configuration events (if, for example, they were not yet assigned in the standard configuration).
- Adding modules. You can create custom common modules in extensions.
- Call. And finally, you can call any methods of standard configurations in your extension.
When you adopt and extend a module of standard configuration, your module that extends it is located in the same namespace as the module of standard configuration. Therefore, you can access any variables and methods of the standard module directly when you are in the module that extends a standard configuration.
If you are in another module that exists in the extension, you have access to your exported variables and methods of the module that extends a standard configuration. This is due to the fact that they are added to the resulting public context of the standard module.
The task of method hooking, in the vast majority of cases, is to surround the call of the standard method with certain before-and/or after actions. We did not exclude the option of complete block of the standard method call and implemented this feature.
You can fully describe the hooking of a standard method in the module that extends a standard configuration. In order for you to do this, we have introduced a new structural element of 1C:Enterprise script - an annotation. You can use an annotation located before the method declaration to specify which standard method is intercepted by the procedure/function and how it is done. For example:
// Procedure text
&Before ("Procedure1") annotation means that standard procedure named Procedure1 is intercepted. The annotation name “Before” means that your hook Ext_Proc1() is executed first, followed by the standard Procedure1().
At the moment, we have added three possible annotation names to the platform.
An annotation with this name means that your hook is executed before execution of the standard method begins.
In the diagram, the rectangles denote the standard module and module that extends a standard configuration, and the arrow shows the sequence of 1C:Enterprise script execution.
This annotation means that your hook is executed after the standard method is executed.
This annotation is the one that completely blocks the standard method. That is, the standard method is not executed at all. Instead, only your hook is executed.
You can set one of the following hook combinations for a standard procedure:
- &Before and &After
The latest combination of hooks (&Before and &After) is executed as follows:
If you are intercepting a standard function rather than a procedure, you can only use the &Around hook.
Calling a method blocked by &Around annotation
What we have now seems unfair. You can block or frame a procedure. However, as far as a function goes - you can only completely block it.
To address this unfairness, we implemented a new method for 1C:Enterprise script - ProceedWithCall(). If you call this method inside your hook function, the function that you blocked is executed, after which the script execution returns to your hook:
In 1C: Enterprise script, this hook function might look as follows:
Var1 = ProceedWithCall(Param1);
EndFunctionThus, your hook function is split into two parts. The part that is executed before the execution of the standard function, and the one that is executed after the standard function.
You can use the ProceedWithCall() method not only when blocking functions, but also when blocking procedures. In this case, the result of its use is the same as when using the hook pair &Before and &After. The only difference is that in this case, your part “before” and your part “after” exist within the same context. In some situations, it may be useful. In 1C:Enterprise script, this hook might look as follows:
Var1 = ProceedWithCall(Param1);
Which one is better - &Before, &After or &Around?
When you intercept standard configuration methods, you should always remember two things:
- Once you have written your extension, the standard configuration will change.
- Your goal is to add your functionality rather than give up what is already there and what will be in the standard configuration for good.
From this perspective, it is better to use hooks &Before and &After. Because with them, the intercepted standard method is always executed, without any conditions. If developers of the standard configuration at a later time will make any changes to this method, these changes will definitely have effect when using your extension.
You can also use the hook &Around and method ProceedWithCall(). But here you have the opportunity and temptation to call the standard method not always, but depending on some of your own conditions. You should be careful here and always remember that when you refrain from calling the standard method, you refrain from not only calling the method that is present in the configuration at the moment, but also from all of its variations, which will appear in the future. However, in the future, the configuration may be updated with important and useful changes.
And finally, the “worst” option is a complete block of the standard method by the hook &Around. In this case, the standard handler will not run now or in any future versions. That is, you and your extensions are assuming all of the responsibility for the work of future versions of the configuration. Certainly, there are situations where a complete block is necessary, but we encourage you to treat this decision carefully and cautiously.
Call sequence for method interception
Before we get deeper into this topic, we must clarify something first. An important, so to speak, "ideological" characteristic of extensions is their autonomy. That is, extensions must be designed in such a way as not to depend on one another.
But when an application is running, it is natural and obvious that there is a certain call sequence for the attached extensions. This sequence is known, and we will tell you about it. But we're not going to tell you about it so that you could use it to create interdependent extensions or extensions with a single and strictly defined attachment sequence. We will tell you about it so that you could address the arising issues and debug the script.
When you attach an extension to a standard configuration, a "multi-layered cake" is formed. At the base of this cake is the standard configuration, and on its top - the last attached extension.
Whether in Designer or in 1C:Enterprise mode, the last attached extension is listed last.
Thus, in the example below, the standard configuration is located down below, Extension2 is up top, and Extension1 is in between. Each subsequent extension intercepts (extends) what is located below it.
When the platform comes across the hooks defined in the extensions, the 1C:Enterprise script is executed from top to bottom of the cake in accordance with the annotations of the hooks. Up until the level, to which it can reach. After that it comes back up if there are any hooks, and then once again goes to the standard configuration.
For example, if the same standard method is intercepted (framed) in two extensions, the call sequence of the handlers is as follows:
- First, a hook from Extension2 is called because it is at the top. It is the &Before hook because that is its annotation.
- Then, a hook from Extension 1 is called because it is the next in the cake. It is once again the &Before hook because that is its annotation.
- Afterwards, the standard method is called because there are no more hooks that delay its execution.
- Then, following the reverse order of the “cake”, hook &After from Extension 1 and hook &After from Extension2 are called.
This example illustrates one peculiarity really well: if in one of the hooks, an unprocessed exception occurs, the entire chain is interrupted and the exception is escalated.
If the hooks use the ProceedWithCall() method, the same “cake” principle applies.
- First, a hook from Extension3 is called because it's at the top. It is the &Around hook because that is its annotation.
- When the platform attempts to call the standard method, the remaining “cake” is analyzed. It is analyzed in the same way as described in the previous example.
- As a result, the script execution goes back to the hook &Around, and upon its completion - to the standard configuration.
It is important to understand that when you use the &Around annotation to block a method, you actually block not only the call of the main method, but also of the hooks that are located lower in the “cake”.
In this, only the hook &Around from Extension2 is executed. This is due to the fact that it blocks the standard method (and the whole “cake” located below it).
It is, in fact, a variation of the second example, but under the upper extension, the extension that “lowers” the call of the standard procedure is located.
In fact, it once again simply illustrates that calling the standard method applies to the whole “cake” located under the extension. This is why after calling the hook from Extension2, the hook from Extension1 is called. Because it blocks the call of the standard method, which Extension2 wants to “reach”, in the remaining “cake”.
Intercepting event handlers and adding custom handlers to the modules of objects, managers, etc.
Any methods in these modules are intercepted in exactly the same way as it is described above. However, if the intercepted procedure is an event handler, there are some peculiarities you need to be aware of. These peculiarities have to do with the fact that in these modules, all event handlers have fixed names.
Firstly, the event name is specified as the name of the intercepted method. For example, BeforeWrite:
// Insert handler code.
EndProcedureSecondly, the standard handler for this event does not have to be available. If the standard handler is not available, your hook is called. Thanks to this feature, you can assign custom handlers to the events that are not handled in the standard configuration.
Since event handlers have fixed names in the object modules, and the list of annotations is known to us, we can offer a certain “service” for you. When you create a handler extension, a dialog box opens that allows you to select the call type. Then, a hook template is created in the module.
Intercepting event handlers and custom handlers in the form modules
Any methods in these modules are also intercepted in exactly the same way as it is described above. However, there are peculiarities associated with interception of event handlers. These peculiarities have to do with the fact that in these modules, all event handlers are assigned and do not have fixed names. As you probably know, in order for the platform to know what to use to handle an event, a specific procedure should be assigned to a specific event in a property palette in Designer.
It is for this reason and only when intercepting event handlers in the forms, you need to use the property palette rather than annotations. Even though you can use annotations to intercept any other module methods that are not event handlers.
An event hook in a form module looks like this:
Procedure Ext2_OnCreateAtServerAround(Cancel, StandardProcessing)
// Insert handler code.
That is, an annotation is not used, and the hook type is specified in the property palette. It can be easily done. When you create a handler in the extension, clicking the "magnifier" button opens a dialog box. It allows you to specify, in addition to the context, the type of hook (Before, After, or Around).
After you create the procedure template in the property palette, an icon representing the type of hook appears next to the name of the hook.
If you block the standard handler (Around), it is just a dot.
If you create a hook Before or After, this is a dot next to the vertical line. The location of the dot before or after the line denotes the type of hook. In addition, the second (empty) field appears next to this event in the property palette. You can use it to set a paired hook if there is a need to frame the standard handler with the Before-After pair.
Event hooks set in this way will work even if there is no standard handler for this event. This is exactly how you can assign custom handlers to the form events that are not handled in the standard configuration.
Speaking of form modules, we would like to point out one more thing. We slightly changed the behavior of the modules extending the form modules that existed previously. We did this in order for it to match the behavior of other modules and to ensure the script stability.
Previously, all the modules that extend the form module and the form module itself were located in the same namespace. Thus, it was possible to call from the upper extension not only methods of the standard configuration, but also the methods that were located below the extensions. Now we have closed this "loophole", and methods of the extensions located below are no longer available. Now you can access only the methods contained in the standard module which you are extending.
You can create any custom common modules in an extension. There are only two restrictions:
- They must not be global server modules.
- They must not be privileged modules.
When you extend a standard module of a standard configuration, you are faced with the same restrictions:
- You cannot adopt global server modules.
- Your extension script will be executed only in nonprivileged mode (unless otherwise authorized in the security profile).
The operation of adopting a global server module is not forbidden in the configuration tree, but when updating the database configuration, you will receive an error message, and the update will fail.
Server methods do not always extend
The fact that your extension was successfully attached to a standard configuration does not mean that all the hooks that your extension has will be applied and executed. There are some peculiarities related to security.
If the application works in file mode or in client/server mode without security profiles, when you attach your extension:
- In regular mode, all methods of the standard solution - client and server ones - are extended.
- In safe mode, only client methods and server form handlers are extended. The extension does not apply to the remaining server procedures/functions.
When an application works in client/server mode and if a specific security profile is specified when attaching the extension, or if regular and safe mode profiles are assigned to the infobase, the "server" portion of the extension is applied as indicated in the corresponding profile.
For this purpose, we have added several new properties to the security profile.
The simplest of these is the all module extensions check box in the Grant full access to group. In "one fell swoop", it allows the server context to be extended.
Finer tuning is available using the Modules available for extension and Modules not available for extension fields. We assume that you will use them as follows:
- If you did not allow full access to the extensions, you should list the names of those modules, for which extension of the server context can be allowed and will not present any threat, in the Modules available for extension field;
- If you allowed full access to extensions, you should list some modules, for which extension of the server context should not be allowed, in the Modules not available for extension field.