Il y avait une idée ici pour féliciter notre chef comptable d'une manière plus ou moins originale, par exemple, avec l'aide de son programme 1C préféré? Mais comment?
Après réflexion, l'idée est venue d'utiliser pour les félicitations d'arrière-plan l'image d'arrière-plan dans la zone client des formulaires conventionnels pour les configurations sur 1C77-1C82 ou dans une fenêtre externe pour les formulaires gérés 1C82 et dans tous les cas pour 1C83. Sur celui-ci, affichez le message souhaité et donnez des liens vers la vidéo de félicitations, comme indiqué sur la figure.

Première partie - Résultat
De toute évidence, cette idée n'est pas nouvelle. Ainsi, en 2011, une
solution similaire a été proposée basée sur
FormEx.dll, par Aleksey Fedorov alias ALF . Et des questions sur la
manière d'y parvenir ont été posées en 2008.
À un moment donné, nous avons également utilisé ce composant pour charger l'image d'arrière-plan dans 1C77. Mais le chargement de gros fichiers bmp (et d'autres ne pouvaient pas être utilisés) était lent (à cause de cela, de petites images posées avec des tuiles ont été utilisées), donc il y avait un désir d'écrire votre propre composant externe (VK), qui ne téléchargera que les images nécessaires et rien de plus, sauf si quoi d'autre pour être un terrain d'essai pour des expériences.
Un tel composant a été écrit (également, uniquement pour les fichiers bmp, en utilisant, si nécessaire, le pavage). La fonction
WinAPI LoadImage () y a été utilisée. Cette DLL n'a pas été en conflit avec FormEx.dll, elle était simple, assez rapide et servie pendant longtemps.
Tout cela était merveilleux, mais il était temps d'étendre ses capacités, et ici une approche différente était nécessaire.
Dans cet article, nous n'abordons pas les problèmes de création de fichiers multimédias. Ce n'est pas notre spécialité. Nous nous limitons uniquement à certaines nuances de programmation de composants externes pour 1C.
1C77
Étant donné que les versions de la plate-forme 1C peuvent être différentes, il peut y avoir plusieurs solutions. Dans notre cas, il s'agissait de configurations sur 1C77 (Fig.1).

Fig. 1. Image de félicitations dans la configuration de test sur 1C77.
La vidéo ici, bien que la sienne, mais l'idée de sa création est glanée auprès d'
Anna Shiyanova, sous le surnom de "Cas spécial" . Cette fille a du talent, elle peut être imitée, mais il n'est guère possible de répéter complètement le style. Dans ce cas, je voulais juste au moins un élément de créativité.
Si l'un des collègues est déjà fatigué de regarder les félicitations des autres, il peut surcharger l'image avec «
Alt + I » (Fig. 2-3).

Fig. 2. Sélection d'une image d'arrière-plan différente dans le menu "Fichier / Sélectionner l'arrière-plan" ou par "Alt + I".
Et en même temps voir les informations sur le module utilisé par "
Alt + L " (Fig. 3).

Fig. 3. Image d'arrière-plan surchargée avec des informations sur le programme («Aide / À propos du module LionExt32.dll» ou «Alt + L»).
Formes conventionnelles 1C82
Naturellement, la majorité est désormais orientée vers le G8 (1C8x). Cependant, travailler avec l'image d'arrière-plan dans 1C n'est possible que sur les formulaires ordinaires dans la version 8.2 et moins, et si vous n'utilisez aucun traitement qui démarre en mode «bureau», qui chevauchera simplement complètement notre arrière-plan (Fig. 4).

Fig. 4. Image de félicitations dans la configuration de test sur les formulaires habituels 1C82.
Notez que les liens vers la Fig. 4 n'indiquez pas notre vidéo. Ils sont montrés juste pour le test.
Dans les formes ordinaires, 1C82 ne fonctionne plus de manière standard pour accéder au menu, car il n'y est pas systémique, comme dans le "sept", mais "propre" (bien que le système puisse être créé, mais pourquoi avons-nous besoin de deux menus principaux?). Cependant, des raccourcis clavier peuvent être utilisés. De la même manière, «Alt + I», dans notre composant, nous invoquons une boîte de dialogue, comme dans la Fig. 2 et chargeons un autre arrière-plan (Fig. 5).

Fig. 5. Image d'arrière-plan surchargée sous des formes 1C82 «épaisses».
De même, vous pouvez obtenir des informations sur le module en appuyant sur la touche "Alt + L", comme dans la fig. 3.
Formulaires gérés 1C82
Pour les formulaires gérés dans 1C82, vous pouvez toujours trouver la fenêtre dont nous avons besoin au septième niveau d'imbrication, comme "
V8FormElement " et dessiner dessus, mais en quelque sorte ce n'est pas intéressant.
Pour nous, il résulte de ces considérations qu'il est plus facile de créer une fenêtre externe avec un message de félicitations (Fig. 6) que de traiter chaque cas individuel. La fenêtre elle-même peut être fermée, ou plutôt minimisée par "
Esc ", "
Ctrl + F4 ", "
Alt + F4 " ou en cliquant sur la "
croix ".

Fig. 6. Image de félicitations dans une configuration de test sur les formulaires gérés 1C82.
De plus, la fenêtre minimisée (Fig. 7) peut à nouveau être agrandie.

Fig. 7. Une image réduite de la fenêtre externe sur les formulaires gérés 1C82.
Les dimensions et la position relative de la fenêtre extérieure peuvent être modifiées, tout est comme d'habitude ici (voir des images agrandies de fenêtres extérieures sur la Fig. 6 et la Fig. 10). Notez que les raccourcis clavier ne fonctionnent que si la fenêtre externe est active.
Formes conventionnelles 1C83
Dans 1C83, il n'y a plus de fenêtres enfants du tout, ce qui peut servir de critère pour la version 1C dans notre DLL. De plus, les formulaires «épais» sont une fenêtre de cadre (Fig. 8), et les formulaires gérés sont sans cadre (Fig. 9). Autrement dit, tout ce qui n'est pas un cadre peut être redessiné. Un cadre peut également être redessiné, mais uniquement en tant qu'élément système.
Ici, nous avons créé une fenêtre de test à l'aide d'une bibliothèque dynamique et l'avons subordonnée à la fenêtre principale 1C. La différence de comportement est visible dans les figures.
Formulaires gérés 1C83
Dans le cas de 1C83, comme dans les formulaires gérés 1C82, nous tirerons nos félicitations non pas sur le fond, mais dans une fenêtre distincte, dont le prototype est illustré sur la Fig. 8-9. Par conséquent, le composant souhaité (
LionExt32.dll ou
LionExt64.dll ) donnera le résultat suivant (Fig. 10-12).

Fig. 10. L'image d'arrière-plan dans la fenêtre externe pour les formulaires conventionnels 1C83.

Fig. 11. L'image d'arrière-plan dans la fenêtre externe des formulaires gérés 1C83, version 14, version 64 bits.

Fig. 12. L'image d'arrière-plan dans la fenêtre externe des formulaires gérés 1C83, version 15, version 64 bits.
Constatations préliminaires
Cette composante était effectivement utilisée en pratique (Fig.1), le chef comptable était satisfait, tout s'est merveilleusement bien passé. En cours de route, il s'est avéré que les utilisateurs aiment choisir leurs propres images d'arrière-plan, dans ce cas, pour travailler sur les "sept". Pour le G8, notre composant est adapté avec une réserve pour l'avenir, alors qu'il devrait être considéré comme une version de démonstration.
L'intérêt ici était que
ce composant ne nécessitait pas la conformité avec la technologie de création de composants externes à partir de 1C . Peut-être que d'autres idées surgiront pour étendre ses capacités. Par exemple, pour les configurations entièrement prises en charge, vous ne souhaitez pas apporter de modifications au code 1C sans besoin particulier. Dans ce cas, on pourrait offrir l'option de chargement externe d'une DLL arbitraire dans l'espace d'adressage 1C. Mais c'est le sujet d'un autre article.
Parmi les innovations techniques, un verrou a été utilisé pour décharger notre composant avec la plateforme 1C (car il n'est pas conforme au format VK). De plus, une autre astuce a permis d'affecter un
menu local à la fenêtre enfant, puisque le système d'exploitation Windows bloque la création d'un tel menu pour les fenêtres subordonnées. Par conséquent, vous ne verrez nulle part les menus locaux dans la même interface
MDI (Multi Document Interface). Il est remplacé par des panneaux de commandes, des barres d'outils et un menu contextuel. Il y a encore un moment pour mettre à jour les fenêtres. Il arrive parfois que ni
UpdateWindow () ni
InvalidateRect () ne fonctionnent correctement. Mais quelques fonctions réussissent dans ce cas:
ShowWindow(hWnd, SW_HIDE); ShowWindow(hWnd, SW_SHOW);
Il convient également de noter que notre composant peut entrer en conflit avec d'autres, par exemple, avec FormEx.dll pour 1C77. Dans ce cas, il doit être chargé en dernier.
Par ailleurs, il est à noter que si vous créez une configuration dans la version 1C-8.3.14 et supérieure, le composant n'est pas chargé de manière régulière. Mais si la base de données a été créée dans une version antérieure de 1C et s'ouvre dans les dernières versions, il n'y a aucun problème de chargement de notre VK. Cela suggère une fois de plus la nécessité de créer un chargeur de démarrage externe.
Ce projet utilise le sous-système
WinAPI GDI + . En l'utilisant, vous pouvez afficher des images de différents formats:
bmp, jpg, gif, png, tif et autres. Dans le même ordre, le composant tente de charger le premier fichier
Main. * Disponible à partir du répertoire
Pics local dans la configuration actuelle. Si aucun de ces fichiers n'est trouvé, une simple image d'arrière-plan provenant des ressources des composants est utilisée. Dans la fig. La figure 13 montre cette image d'arrière-plan pour les formes habituelles de 64 bits 1C83, version 15. Pour une modification, la fenêtre externe de l'argot a été agrandie et une autre image du fichier
Main1.png , qui a été «mosaïque», a été ajoutée à son arrière-plan.

Fig. 13. L'image d'arrière-plan par défaut pour les formes habituelles de 64 bits 1C83, version 15. De plus, une autre image du fichier Main1.png, posée en "mosaïque", a été ajoutée.
Il n'y a aucune différence dans le fonctionnement du composant dans différents modes de bits.
On peut également noter que notre composant sous-classe la fenêtre principale 1C et son client MDI, le cas échéant. Cela, apparemment, sert de source de conflit avec FormEx.dll lors de son dernier chargement (en 1C77).
Deuxième partie - Technique
Le projet lui-même peut être trouvé sur les liens suivants:
Un projet
C ++ peut être facilement adapté pour la version
10 si la chaîne "
v120 " est remplacée par "
v100 " et "
ToolsVersion =" 12.0 " " par "
ToolsVersion =" 4.0 " dans les fichiers de configuration.
Le code des versions
32 bits et
64 bits de
1C est le même et peut être compilé en même temps.
La version 1C77 est déterminée dans le composant externe par le descripteur de fonction GetMenu
() non nul et la version 1C83, par l'absence de fenêtres enfants dans la fenêtre principale, dont le
descripteur est déterminé par la fonction
GetForegroundWindow () .
À propos de la technologie de création de composants externes pour 1C
Sur les disques ITS de la société 1C, et sur Internet, on peut facilement trouver des informations sur la création de VC et les modèles correspondants dans différents langages de programmation. Cependant, au temps du 1C77, ces schémas satisfaisaient «pas seulement tout le monde».
Si vous regardez certains composants largement utilisés, en particulier pour 1C77, vous verrez que leurs auteurs ont souvent utilisé des méthodes de programmation spéciales pour étendre les capacités de leurs conceptions.
Peut-être que l'un des premiers composants externes de ce type était
"RAINBOW ADDIN 2000 for 1C: Enterprise 7.7" . Le plus important ici était peut-être une pénétration plus profonde dans les entrailles du «sept» que la technologie officielle VK ne le permettait, même si elle suivait le format VK. Cet objectif a été atteint grâce aux en-têtes (fichiers * .h) reçus, probablement non standard, des fichiers de bibliothèque 1C77 utilisés dans d'autres projets largement connus.
En effet, si des fonctions 1C telles que
LoadExternalComponent () et
ConnectExternalComponent () vous permettent d'incorporer
des DLL externes dans votre propre espace d'adressage (tout d'abord, qui satisfont le format de la technologie VK), alors pourquoi les programmes utilisateur ne succombent-ils pas à la tentation et tentent d'accéder à d'autres cachés de eux, les procédures et autres objets de la plate-forme cible? Cette approche a été démontrée avec succès par le composant
Rainbow.dll .
Plus tard, un mécanisme similaire a été adopté par d'autres auteurs du composant 1C version 7.7. Le composant pour la "sept"
1C ++. Dll et son, pour ainsi dire, un cas spécial de
FormEx.dll sont particulièrement
remarquables .
Mais l'approche non triviale de la conception de composants externes pour 1C77 ne s'arrête pas là. Apparemment, quelqu'un aurait dû dire: «Pourquoi avons-nous besoin d'un forgeron? Nous n'avons pas besoin d'un forgeron! " Ici, par «forgeron», nous entendons la technologie COM de MicroSoft, qui, dans un sens, a été suivie par la technologie VK pour les «sept». Non, vraiment, pourquoi avons-nous besoin d'un registre si nous téléchargeons notre VK directement? Cela peut être logique pour les navigateurs Web qui fonctionnent avec Internet, mais pour un fonctionnement local, l'utilisation du registre est clairement redondante. À tout le moins, cela ne devrait pas être une condition préalable. De plus, pour éditer le registre, vous avez besoin de droits administratifs.
Notez que 1C aimait beaucoup cette technologie (au moins jusqu'au portage de 1C vers Linux). Nous la traitons plutôt cool. COM est pratique pour utiliser le composant ActiveX et cela est naturel, car ces derniers ont été initialement développés pour Internet.
Cependant, dans les dernières versions, 1C a ajouté la possibilité d'utiliser la technologie
API native , ce qui élimine le besoin d'un registre. En principe, c'est ce dont nous avons besoin, sauf que cette technologie n'est pas applicable dans les «sept» et qu'elle est, pour certains, toujours d'actualité.
Mais parfois, des tâches relativement simples surviennent lorsque vous ne souhaitez pas utiliser un tas de code passe-partout pour VK et qu'il est conseillé de travailler avec 1C uniquement du côté du composant externe. Comme, disons, dans notre cas, la démonstration d'une image de félicitations dans l'espace client ou, si nécessaire, dans une fenêtre séparée, la configuration 1C.
En d'autres termes, si nous n'allons pas échanger directement des données entre 1C et VK, nous serons très satisfaits d'une version plus simple et plus universelle du composant externe pour 1C. Ici, la simplicité sera obtenue en raison de l'absence de code passe-partout.
Technologie alternative pour créer VK pour 1C
Étant donné que VK pour 1C est un cas particulier de
serveur COM (avant la technologie
Native API ), certains développeurs VK ont dit: «COM - non!». L'activité dans ce sens d'
Alexandre Orefkov est particulièrement notable. Ses composants «
1sqlite.dll », «
TurboMD.dll » et éventuellement d'autres n'utilisent pas COM à partir du mot «complètement». Le composant
Yoksel ("
SpreadSheet.dll ") se développe également le long de ce chemin.
Mais comment alors le chargeur VK de 1C77 charge-t-il ces composants? Après tout, ils n'essaient même pas d'imiter une sorte de COM là-bas. En effet, si nous essayons de glisser franchement une DLL standard générée par, disons, l'assistant
MS VC ++ dans la fonction
LoadExternalComponent () , alors nous aurons une erreur.
Dans le "sept", nous obtenons un message comme:
Une erreur s'est produite lors de la création d'un objet à partir du composant <Chemin d'accès complet \ Nom du composant> .dll (CLSID est manquant)
Dans le client "épais" 32 bits du message "huit" sera similaire. La même dll provoquera une prestation de serment similaire (Fig.15):
Erreur lors de l'appel de la méthode de contexte (Load External Component): erreur lors du chargement du composant externe
Alors, comment les bibliothèques mentionnées résolvent-elles ce problème? En étudiant les textes des programmes Orefkov et Yoksel, nous concluons finalement que les «
lignes magiques » suivantes dans le fichier de ressources (* .rc ou * .rc2) sont «à blâmer»:
STRINGTABLE DISCARDABLE BEGIN 100 "\0sd" // 1sqlite.dll 100 "\0tmd" // TurboMD.dll 100 "\0f" // SpreadSheet.dll END
C'est-à-dire sans faute, dans les ressources du programme, il y a une ligne avec l'identifiant
100 et une certaine valeur de chaîne, dont le premier caractère est zéro. Vous pouvez expérimenter avec des variations de telles chaînes, mais la chaîne "
\ 0L " me convient. Ainsi, nous créons un fichier de ressources et écrivons des lignes comme ceci:
STRINGTABLE DISCARDABLE BEGIN 100 "\0L" // 1 ! END
Nous connectons ce fichier à notre projet de DLL le plus simple généré par l'assistant MS C ++, ajoutez le 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; }
et observez (Fig. 14).

Fig. 14. Utilisation du «VK» le plus simple en 1C82.
Sans "lignes magiques" dans le fichier de ressources, notre dll, après avoir montré MessageBox, se décharge immédiatement avec une malédiction de 1C (Fig. 15).

Fig. 15. Erreur lors du chargement de la DLL régulière dans 1C82.
Autrement dit, ces lignes ont vraiment un effet magique sur le chargeur de composants externes 1C.
La première, semble-t-il, des «lignes magiques» a été décrite dans son ancien article d'
Alexei Fedorov (ALF) , mais le lien vers celle-ci n'est plus disponible, et l'auteur ne voit pas l'intérêt de sa nouvelle publication. De plus,
Alexander Orefkov les a utilisés de la manière la plus intensive, et apparemment, d'après sa soumission, l'auteur était
Yoksel . Par conséquent, nous parlerons
des lignes «magiques» de Fedorov-Orefkov . Leur signification est de bloquer le déchargement des fichiers dll non standard (du point de vue de 1C) par la fonction
LoadExternalComponent () . De plus, comme nous le voyons, cette technique fonctionne non seulement en 1C77, mais aussi sous des formes 1C82 «épaisses».
Cependant, dans les formulaires gérés 1C82 et dans toutes les versions de 1C83, cette fonctionnalité a déjà été complètement interrompue (un autre chargeur est également apparu -
ConnectExternalComponent () ).
Ainsi, dans les versions modernes de 1C, vous devez rechercher d'autres alternatives simples aux lignes «magiques» de Fedorov-Orefkov.
Et une telle alternative est facile à offrir. Le point est simple. Le chargeur 1C décharge le «mauvais» composant s'il lève une exception lorsqu'il essaie d'y accéder en utilisant le protocole spécifié, par exemple, lors de la demande de la version du composant. Naturellement, nous n’avons rien de ce type, qui sert de base au déchargement d’une DLL non standard. Mais l'exigence de 1C pour que le système d'exploitation décharge cette bibliothèque dynamique peut être ignorée par le système si ce VK est toujours utilisé quelque part. Au lieu de la suppression elle-même, le système réduit simplement le compteur d'utilisation du module souhaité. Et supprimez physiquement au plus tôt ce compteur est réinitialisé. Par conséquent, notre tâche consiste à augmenter artificiellement ce compteur.
Pour ce faire, vous pouvez appeler à nouveau notre fonction dll WinAPI
LoadLibrary () dans la section
DLL_THREAD_ATTACH BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { switch(dwReason) { case DLL_PROCESS_ATTACH: { WCHAR szDllName[_MAX_PATH] = {0};
Voilà! Le problème est résolu. Le rappel de la même bibliothèque dynamique augmentera son compteur d'utilisation de un et le déchargement (avec un accès préliminaire à la section
DLL_THREAD_DETACH ) diminuera de un. Au total, nous avons
2 - 1 = 1> 0 , par conséquent, le système d'exploitation ne déchargera pas notre DLL. De plus, ce qui est important, la réinitialisation de la section
DLL_PROCESS_ATTACH ne se produira pas.
De cela, d'ailleurs, on peut voir comment 1C peut gérer une astuce similaire dans ses dernières versions (et, apparemment, il le fait déjà dans les configurations créées dans 1C-8.3.14 et plus). Il peut utiliser la fonction
LoadLibraryEx () avec un paramètre qui bloque l'exécution de la section d'initialisation
DLL_PROCESS_ATTACH , après quoi il appellera immédiatement les fonctions exportées nécessaires. Et, en effet, si vous regardez le code de l'exemple VK pour l'API native, vous pouvez voir qu'il n'est pas nécessaire d'appeler le code d'initialisation, car il doit être vide au format VK.
Concernant les exemples d'utilisation de la technologie COM, il est évident que l'exécution de la section d'initialisation
DLL_PROCESS_ATTACH y est nécessaire, donc, dans les versions pas trop récentes de 1C, plus précisément, dans les configurations faites en 1C-8.3.13 et ci-dessous, le chargeur 1C nous convient:
(, , .COM);
Ici, le dernier paramètre peut être supprimé, car il est implicite par défaut. En même temps, ils peuvent s'ouvrir normalement dans n'importe quelle version supérieure. Dans les versions 1C83, l'ancien chargeur de démarrage
LoadExternalComponent (Component Address) ne nous convient plus (respectivement, les "lignes magiques" de Fedorov-Orefkov n'y fonctionnent pas).
Dans le cas général, comme déjà mentionné, le problème peut être résolu en utilisant un chargeur de démarrage externe. Ou, ce qui est tout à fait naturel, d'observer, dans une mesure ou une autre, la technologie des composants externes du 1C.
Il convient également de noter que les expériences que nous avons menées dans des versions de fichiers de 1C avec différentes profondeurs de bits. Pour télécharger notre composant, vous devrez peut-être définir la propriété «
Synchronous Call Usage Mode » sur «
Use » dans la configuration.
Il faut également comprendre que vous réalisez l'utilisation d'une telle technique à vos risques et périls, expérimentez à l'avance des configurations de test ou des copies de travailleurs pour éviter les problèmes potentiels dans les programmes principaux.
Mise à jour du 09/11/2019
Il s'est avéré que je m'inquiétais en vain que: "dans les versions 1C-8.3.14 et supérieures, la section d'initialisation dans le composant externe n'est plus effectuée à partir du mot" complètement "".
Il s'avère que seul le message de retour dans la fonction
ConnectExternalComponent () n'a pas besoin d'être traité. De plus, quel que soit le type de composant que nous spécifions:
COM ou
API native .
Ainsi, vous pouvez créer une configuration dans toutes les versions actuellement disponibles de 1C, notre composant devrait fonctionner correctement partout, et la création d'un chargeur de démarrage externe sera pertinente, sauf dans le cas où vous ne souhaitez pas modifier la configuration, qui est entièrement prise en charge.
À cet égard, le code dans les configurations de test pour 1C82 et 1C83 est légèrement modifié, bien que les différences entre elles ne soient plus fondamentales.
Dans le même temps, notre remarque selon laquelle la société 1C peut facilement bloquer l'exécution du code d'initialisation dans n'importe quel VK, au moins pour les composants externes tels que l'
API native , reste évidemment valide, car à en juger par leur modèle, cela n'est pas nécessaire. Pour un
COM de type VK
, il existe un tel besoin jusqu'à présent, mais qu'est-ce qui l'empêche de s'en débarrasser? Dans le même temps, voyons s’ils tiendront compte de ces informations?