Is it possible in 1C not to observe the technology of external components? Or How to congratulate colleagues using 1C?

There was an idea here to congratulate our chief accountant in a more or less original way, for example, with the help of her favorite 1C program? But how?



After some thought, the idea came to use for background congratulations the background image in the client area of ​​conventional forms for configurations on 1C77-1C82 or in the external window for managed forms 1C82 and in all cases for 1C83. On it, display the desired message and give links to the congratulatory video, as shown in the figure.



Congratulation in 1C



Part One - Resulting



Obviously, this idea is not new. So, in 2011, a similar solution was proposed based on FormEx.dll, by Aleksey Fedorov aka ALF . And questions on how to achieve this were asked back in 2008.



At one time, we also used this component to load the background image in 1C77. But downloading large bmp-files (and others could not be used) was slow (because of this, small pictures laid with tiles were used), so there was a desire to write your own external component (VK), which will only download the necessary images and nothing more, unless what else to be some testing ground for experiments.



Such a component was written (also, only for bmp-files, using, if necessary, tiling). The WinAPI LoadImage () function was used there. This dll did not conflict with FormEx.dll, it was simple, fast enough and served for a long time.



All this was wonderful, but it was time to expand its capabilities, and here a different approach was needed.



In this article, we do not address the issues of creating multimedia files. This is not our specialty. We restrict ourselves only to some nuances of programming external components for 1C.



1C77



Since the versions of the 1C platform can be different, there can be several solutions. In our case, these were configurations on 1C77 (Fig. 1).



Fig. 1. Congratulatory image in the test configuration on 1C77

Fig. 1. Congratulatory image in the test configuration on 1C77.



The video here, although its own, but the idea of ​​its creation is gleaned from Anna Shiyanova, under the nickname "Special case" . This girl has talent, she can be imitated, but it is hardly possible to completely repeat the style. In this case, I just wanted at least some element of creativity.



If one of the colleagues is already tired of looking at other people's congratulations, then they can overload the picture by “ Alt + I ” (Fig. 2-3).



Fig. 2. Selecting a different background image in the “File / Select Background” menu or “Alt + I”

Fig. 2. Selecting a different background image in the "File / Select Background" menu or by "Alt + I".



And at the same time see the information about the module used by " Alt + L " (Fig. 3).



Fig. 3. An overloaded background image along with information about the program (“Help / About the LionExt32.dll module” or “Alt + L”)

Fig. 3. Overloaded background image along with information about the program ("Help / About the LionExt32.dll module" or "Alt + L").



1C82 conventional forms



Naturally, the majority are now oriented toward the G8 (1C8x). However, working with the background image in 1C is possible only on ordinary forms in version 8.2 and less, and if you do not use any processing that starts in the “desktop” mode, which will simply completely overlap our background (Fig. 4).



Fig. 4. Congratulatory image in the test configuration on ordinary forms 1C82

Fig. 4. Congratulatory image in the test configuration on the usual forms 1C82.



Note that the links to Fig. 4 indicate not our video. They are shown just for the test.



In ordinary forms, 1C82 no longer provides the standard way to access the menu, since it is not systemic there, as in the "seven", but "own" (although the system can be created, but why do we need two main menus?). However, hotkeys can be used. Therefore, “Alt + I”, in our component, we invoke a dialog, as in Fig. 2, and load a different background (Fig. 5).



Fig. 5. Overloaded background image in “thick” 1C82 forms

Fig. 5. Overloaded background image in “thick” 1C82 forms.



Similarly, you can get information about the module by pressing the "Alt + L" key, as in fig. 3.



1C82 managed forms



For managed forms in 1C82, you can still find the window we need at the seventh nesting level, such as “ V8FormElement ” and draw on it, but somehow it’s not interesting.



For us, from these considerations it follows that it is easier to create an external window with a congratulatory message (Fig. 6) than to handle each individual case. The window itself can be closed, more precisely, minimized by " Esc ", " Ctrl + F4 ", " Alt + F4 " or by clicking on the " cross ".



Fig. 6. Congratulatory image in a test configuration on managed forms 1C82

Fig. 6. Congratulatory image in a test configuration on managed forms 1C82.



Moreover, the minimized window (Fig. 7) can be expanded again.



Fig. 7. The minimized image of the external window on managed forms 1C82

Fig. 7. A minimized image of the external window on managed forms 1C82.



The dimensions and relative location of the external window can be changed, everything is as usual here (see enlarged images of the external windows in Fig. 6 and Fig. 10). Note that hotkeys only work if the external window is active.



1C83 conventional forms



In 1C83 there are no more child windows at all, which can serve as a criterion for version 1C in our dll. Moreover, “thick” forms are a frame window (Fig. 8), and managed forms are frameless (Fig. 9). That is, everything that is not a frame can be redrawn. A frame can also be redrawn, but only as a system element.

Fig. 8. Frame window in “thick” forms 1C83Fig. 9. Frameless window in managed forms 1C83
Fig. 8. Frame window in “thick” forms 1C83. Fig. 9. Frameless window in managed forms 1C83.
Here, using a dynamic library, we created a test window and subordinated it to the main 1C window. The difference in behavior is seen in the figures.



1C83 managed forms



In the case of 1C83, as in the managed forms 1C82, we will draw our congratulations not against the background, but in a separate window, the prototype of which is shown in Fig. 8-9. As a result, the desired component ( LionExt32.dll or LionExt64.dll ) will give the following result (Fig. 10-12).



Fig. 10. The background image in the external window for conventional forms 1C83

Fig. 10. The background image in the external window for the usual forms 1C83.



Fig. 11. Background image in the external window of managed forms 1C83, release 14, 64-bit version

Fig. 11. The background image in the external window of managed forms 1C83, release 14, 64-bit version.



Fig. 12. Background image in the external window of managed forms 1C83, release 15, 64-bit version

Fig. 12. The background image in the external window of managed forms 1C83, release 15, 64-bit version.



Preliminary findings



This component was actually used in practice (Fig. 1), the chief accountant was satisfied, everything went wonderfully. Along the way, it turned out that users like to choose their own background pictures, in this case, to work on the "seven". For the G8, our component is adapted with a reserve for the future, while it should be considered as a demo version.



The interest here was that this component did not require compliance with the technology of creating external components from 1C . Perhaps additional ideas will arise to expand its capabilities. For example, for configurations that are fully supported, you do not want to make changes to the 1C code without special need. In this case, one could offer the option of external loading an arbitrary dll into the address space 1C. But this is the topic of another article.



Of technical innovations, a lock was used to unload our component with the 1C platform (since it does not comply with the VK format). In addition, another trick made it possible to assign a local menu to the child window, since the Windows operating system blocks the creation of such a menu for subordinate windows. Therefore, you will not see local menus in the same MDI (Multi Document Interface) anywhere. He is replaced by command panels, toolbars and a context menu. There is still a moment for updating windows. Sometimes it happens that neither UpdateWindow () nor InvalidateRect () work properly. But a couple of functions in this case are successful:



ShowWindow(hWnd, SW_HIDE); ShowWindow(hWnd, SW_SHOW);
      
      





It should also be noted that our component may conflict with others, for example, with FormEx.dll for 1C77. In this case, it needs to be loaded last.



By the way, it is noticed that if you create a configuration in version 1C-8.3.14 and higher, the component is not loaded in any regular way. But if the database was created in an earlier version of 1C, and opens in the latest versions, then there are no problems loading our VK. This once again hints at the need to create an external bootloader.



This project uses the WinAPI GDI + subsystem. Using it, you can display pictures of various formats: bmp, jpg, gif, png, tif and others. In the same order, the component attempts to load the first available Main. * File from the local Pics directory in the current configuration. If none of these files are found, then a simple background image from component resources is used. In fig. Figure 13 shows this background image for the usual forms of 64-bit 1C83, release 15. For a change, the external window of the slang has been enlarged and another image from the Main1.png file, which has been tiled , has been added to its background.



Fig. 13. The default wallpaper for regular forms 64-bit 1C83, release 15

Fig. 13. The default background image for the usual forms of 64-bit 1C83, release 15. In addition, another image from the Main1.png file, laid “tiling”, has been added.



Differences in the operation of the component in different bit modes are not observed.



It can also be noted that our component subclasses the main 1C window and its MDI client, if any. This, apparently, serves as a source of conflict with FormEx.dll when it is loaded last (in 1C77).



Part Two - Technical



Directly with the project itself can be found at the following links:





A C ++ project can be easily adapted for version 10 if the string “ v120 ” is replaced by “ v100 ” in the configuration files and “ ToolsVersion =“ 12.0 ”is replaced byToolsVersion = “4.0 ”.



The code for the 32 -bit and 64 -bit versions of 1C is the same and can be compiled at the same time.



Version 1C77 is determined in the external component by the non-zero function handle GetMenu () , and version 1C83, by the absence of child windows in the main window, the handle of which is determined by the GetForegroundWindow () function.



About the technology of creating external components for 1C



On the ITS discs of the 1C company, and on the Internet, one can easily find information on the creation of VC and the corresponding templates in different programming languages. However, in the times of 1C77, these patterns satisfied "not only everyone."



If you look at some widely used components, especially for 1C77, you will see that their authors often used special programming methods to expand the capabilities of their designs.



Perhaps one of the first such external components was "RAINBOW ADDIN 2000 for 1C: Enterprise 7.7 . " Perhaps the most important here was a deeper penetration into the bowels of the "seven" than the official VK technology allowed, although it followed the VK format. This was achieved due to the received, quite possibly non-standard methods, headers (* .h-files) of 1C77 library files used in other widely known projects.



Indeed, if such 1C functions as LoadExternalComponent () and ConnectExternalComponent () allow you to embed external dlls into your own address space (first of all, that satisfy the VK technology format), then why don't user programs succumb to the temptation and try to access other ones hidden from them, procedures and other objects of the target platform? This approach has just been successfully demonstrated by the Rainbow.dll component.



Later, a similar mechanism was adopted by other authors of component 1C version 7.7. Of particular note is the component for the "seven" 1C ++. Dll and its, as it were, a special case of FormEx.dll .



But the nontrivial approach to the design of external components for 1C77 did not end there. Apparently, someone should have said: “Why do we need a blacksmith? We don’t need a blacksmith! ” Here, by “blacksmith” we mean COM technology from MicroSoft, which, in a sense, was followed by VK technology for the “seven”. No, well, really, why do we need a registry if we download our VK directly? This may make sense for web browsers that work with the Internet, but for local operation, using the registry is clearly redundant. At the very least, this should not be a prerequisite. Moreover, for editing the registry you need administrative rights.



Note that 1C was very fond of this technology (at least until porting 1C to Linux). We treat her pretty cool. COM is convenient for using the ActiveX component and this is natural, since the latter were originally developed for the Internet.



However, in the latest versions, 1C added the ability to use the Native API technology, which eliminates the need for a registry. In principle, this is what we need, except that this technology is not applicable in the "seven", and it, for some, is still relevant.



But sometimes relatively simple tasks arise when you do not want to use a bunch of boilerplate template code VK and it is advisable to work with 1C from the side of the external component only. As, say, in our case, the demonstration of a congratulatory image in the client area or, if necessary, in a separate window, configuration 1C.



In other words, if we are not going to directly exchange data between 1C and VK, then we will be quite happy with a simpler and more universal version of the external component for 1C. Simplicity here will be achieved due to the lack of boilerplate code.



Alternative technology for creating VK for 1C



Since VK for 1C is a special case of a COM server (before the Native API technology), there were VK developers who said: “COM - no!”. Particularly noticeable is the activity in this direction by Alexander Orefkov . Its components “ 1sqlite.dll ”, “ TurboMD.dll ”, and possibly others, do not use COM from the word “completely”. The Yoksel component (" SpreadSheet.dll ") also develops along this path.



But how then does the VK loader from 1C77 load these components? After all, they do not even try to imitate some kind of COM there. Indeed, if we try to bluntly slip some standard dll generated by, say, the MS VC ++ wizard into the LoadExternalComponent () function, then we will have a bummer.



In the "seven" we get a message like:
An error occurred while creating an object from the <Full Path \ Component Name> .dll component (CLSID is missing)


In the "thick" 32-bit client of the "eight" message will be similar. The same dll will cause a similar swearing (Fig. 15):
An error occurred while calling the context method (Load ExternalComponent): An error occurred while loading an external component


So still, how do the libraries mentioned solve this problem? Studying the texts of Orefkov and Yoksel programs, we ultimately come to the conclusion that the following “ magic lines ” in the resource file (* .rc or * .rc2) are “to blame”:



 STRINGTABLE DISCARDABLE BEGIN 100 "\0sd" // 1sqlite.dll 100 "\0tmd" // TurboMD.dll 100 "\0f" // SpreadSheet.dll END
      
      





Those. without fail, in the program resources there is a line with identifier 100 and some string value, the first character of which is zero. You can experiment with variants of such lines, but the string " \ 0L " is fine with me. Thus, we create a resource file and write lines like this:



 STRINGTABLE DISCARDABLE BEGIN 100 "\0L" //    1     ! END
      
      





We connect this file to our simplest dll project generated by the MS C ++ wizard, add the code:



 BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { switch(dwReason) { case DLL_PROCESS_ATTACH: MessageBox(NULL, ",  DllMain()!", "", MB_OK); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; } // switch(dwReason) return TRUE; } // DllMain()
      
      





and observe (Fig. 14).



Fig. 14. Use of the simplest “VK” in 1C82

Fig. 14. Using the simplest “VK” in 1C82.



Without "magic lines" in the resource file, our dll, after showing MessageBox, immediately unload with a curse from the 1C side (Fig. 15).



Fig. 15. Error loading regular dll in 1C82

Fig. 15. Error loading regular dll in 1C82.



That is, these lines really have a magical effect on the loader of external 1C components.



The first, it seems, “magic lines” was described in his old article by Alexei Fedorov (ALF) , but the link to it is no longer available, and the author does not see the point in its re-publication. Moreover, Alexander Orefkov used them most intensively, and apparently, from his submission, the author was Yoksel . Therefore, we will talk about the “magical” lines of Fedorov-Orefkov . Their meaning is to block the unloading of non-standard (from the point of view of 1C) dll-files by the function Load ExternalComponent () . Moreover, as we see, this technique works not only in 1C77, but also in “thick” 1C82 forms.



However, in managed forms 1C82 and in all versions of 1C83, this feature is already completely blocked (another loader appeared - Connect External Component () ).



Thus, in modern versions of 1C, you need to look for other simple alternatives to the “magic” lines of Fedorov-Orefkov.



And such an alternative is easy to offer. The point is simple. The 1C loader unloads the “wrong” component if it throws an exception when trying to access it using the specified protocol, for example, when requesting the version of the component. Naturally, we don’t have anything of this kind, which serves as the basis for unloading a non-standard dll. But the requirement of 1C for the operating system to unload this dynamic library can be ignored by the system if this VK is still used somewhere. Instead of the deletion itself, the system simply reduces the usage counter of the desired module. And physically delete no earlier than this counter is reset. Therefore, our task is to artificially increase this counter.



To do this, you can call our dll function WinAPI LoadLibrary () again in the DLL_THREAD_ATTACH section



 BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { switch(dwReason) { case DLL_PROCESS_ATTACH: { WCHAR szDllName[_MAX_PATH] = {0}; //     dll GetModuleFileName(hModule, szDllName, _MAX_PATH); //MessageBox(NULL, szDllName, L"Info", MB_OK); //    dll (     183), //      DLL_PROCESS_ATTACH HMODULE hDll = LoadLibrary(szDllName); break; } // case DLL_PROCESS_ATTACH case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; } // switch(dwReason) return TRUE; } // DllMain()
      
      





All! The problem is resolved. Recalling the same dynamic library will increase its usage counter by one, and unloading (with a preliminary entry into the DLL_THREAD_DETACH section) will decrease by one. Total we have 2 - 1 = 1> 0 , therefore, the operating system will not unload our dll. Moreover, what is important, reinitialization of the DLL_PROCESS_ATTACH section will not occur.



From this, by the way, one can see how 1C can deal with a similar trick in its latest versions (and, apparently, it already does this in the configurations created in 1C-8.3.14 and higher). It can use the LoadLibraryEx () function with a parameter that blocks the execution of the initialization section DLL_PROCESS_ATTACH , after which it will immediately call the necessary exported functions. And, indeed, if you look at the code of the VK example for the Native API, you can see that there is no need to call the initialization code, since it must be empty by the VK format.



Regarding the examples of using COM technology, it is obvious that the execution of the initialization section DLL_PROCESS_ATTACH is necessary there, therefore, in not too new versions of 1C, more precisely, in the configurations made in 1C-8.3.13 and below, the 1C loader is suitable for us:



 (, , .COM);
      
      





Here the last parameter can be removed, since it is implied by default. At the same time, they can open normally in any higher version. In versions 1C83, the previous bootloader LoadExternalComponent (Component Address) no longer suits us (respectively, the "magic lines" of Fedorov-Orefkov do not work there).



In the general case, as already mentioned, the problem can be solved by using an external bootloader. Or, which is quite natural, to observe, to one extent or another, the technology of the external components of 1C.



It should also be noted that the experiments we conducted in file versions of 1C with different bit depths. To download our component, you may need to set the “ Synchronous Call Usage Mode ” property to “ Use ” in the configuration.



It should also be understood that you carry out the use of such a technique at your own risk, experiment in advance on test configurations or copies of workers in order to avoid potential problems in the main programs.



Update from 09/11/2019



It turned out that I was worried in vain that: "in versions 1C-8.3.14 and above, the initialization section in the external component is no longer performed from the word" completely "."



It turns out that only the return message in the ConnectExternalComponent () function does not need to be processed. Moreover, no matter what type of component we specify: COM or Native API .



Thus, you can create a configuration in all currently available versions of 1C, our component should work fine everywhere, and creating an external bootloader will be relevant, unless for the case when you do not want to change the configuration, which is fully supported.



In this regard, the code in the test configurations for 1C82 and 1C83 is slightly changed, although the differences between them are no longer fundamental.



At the same time, our remark that the 1C company can easily block the execution of the initialization code in any VK, at least for external components like the Native API , obviously remains valid, because judging by their template, this is not necessary. For a VK type COM, there is such a need so far, but what prevents it from getting rid of? At the same time, let’s see if they’ll take this information into account?



All Articles