¿Es posible en 1C no observar la tecnología de los componentes externos? O ¿Cómo felicitar a los colegas que usan 1C?

¿Hubo una idea aquí para felicitar a nuestro contador principal de una manera más o menos original, por ejemplo, con la ayuda de su programa favorito de 1C? Pero como?

Después de pensarlo un poco, la idea llegó a usarse para felicitaciones de fondo en la imagen de fondo en el área del cliente de formularios convencionales para configuraciones en 1C77-1C82 o en una ventana externa para formularios administrados 1C82 y en todos los casos para 1C83. En él, muestre el mensaje deseado y proporcione enlaces al video de felicitación, como se muestra en la figura.

Enhorabuena en 1C

Primera parte: resultante


Obviamente, esta idea no es nueva. Entonces, en 2011, Aleksey Fedorov, alias ALF, propuso una solución similar basada en FormEx.dll . Y las preguntas sobre cómo lograr esto se hicieron en 2008.

Hubo un tiempo en que también utilizamos este componente para cargar la imagen de fondo en 1C77. Pero la descarga de archivos bmp grandes (y otros no se pudieron usar) fue lenta (debido a esto, se usaron pequeñas imágenes con mosaicos), por lo que hubo un deseo de escribir su propio componente externo (VK), que solo descargará las imágenes necesarias y nada más, a menos que qué más ser un terreno de prueba para experimentos.

Tal componente fue escrito (también, solo para archivos bmp, usando, si es necesario, mosaico). Allí se utilizó la función WinAPI LoadImage () . Este dll no entró en conflicto con FormEx.dll, fue simple, lo suficientemente rápido y sirvió durante mucho tiempo.

Todo esto fue maravilloso, pero era hora de ampliar sus capacidades, y aquí se necesitaba un enfoque diferente.

En este artículo, no abordamos los problemas de la creación de archivos multimedia. Esta no es nuestra especialidad. Nos restringimos solo a algunos matices de programación de componentes externos para 1C.

1C77


Dado que las versiones de la plataforma 1C pueden ser diferentes, puede haber varias soluciones. En nuestro caso, estas fueron configuraciones en 1C77 (Fig. 1).

Fig. 1. Imagen de felicitación en la configuración de prueba en 1C77
Fig. 1. Imagen de felicitación en la configuración de prueba en 1C77.

El video aquí, aunque es propio, pero la idea de su creación se obtuvo de Anna Shiyanova, bajo el apodo de "Caso especial" . Esta chica tiene talento, puede ser imitada, pero es casi imposible repetir completamente el estilo. En este caso, solo quería al menos algún elemento de creatividad.

Si uno de los colegas ya está cansado de mirar las felicitaciones de otras personas, puede sobrecargar la imagen con " Alt + I " (Fig. 2-3).

Fig. 2. Seleccionar otra imagen de fondo en el menú "Archivo / Seleccionar fondo" o mediante "Alt + I"
Fig. 2. Seleccionando una imagen de fondo diferente en el menú "Archivo / Seleccionar fondo" o con "Alt + I".

Y al mismo tiempo, vea la información sobre el módulo utilizado por " Alt + L " (Fig. 3).

Fig. 3. Una imagen de fondo sobrecargada junto con información sobre el programa ("Ayuda / Acerca del módulo LionExt32.dll" o "Alt + L")
Fig. 3. Imagen de fondo sobrecargada junto con información sobre el programa ("Ayuda / Acerca del módulo LionExt32.dll" o "Alt + L").

1C82 formas convencionales


Naturalmente, la mayoría ahora está orientada hacia el G8 (1C8x). Sin embargo, trabajar con la imagen de fondo en 1C solo es posible en formularios normales en la versión 8.2 y anteriores, y si no utiliza ningún procesamiento que comience en el modo "escritorio", que simplemente se superpondrá completamente a nuestro fondo (Fig. 4).

Fig. 4. Imagen de felicitación en la configuración de prueba en formularios ordinarios 1C82
Fig. 4. Imagen de felicitación en la configuración de prueba en los formularios habituales 1C82.

Tenga en cuenta que los enlaces a la Fig. 4 indican que no es nuestro video. Se muestran solo para la prueba.

En formas ordinarias, 1C82 ya no proporciona la forma estándar de acceder al menú, ya que no es sistémico allí, como en "siete", sino "propio" (aunque el sistema se puede crear, pero ¿por qué necesitamos dos menús principales?). Sin embargo, se pueden usar teclas de acceso rápido. Del mismo modo, "Alt + I", en nuestro componente, invocamos un diálogo, como en la Fig. 2, y cargamos otro fondo (Fig. 5).

Fig. 5. Imagen de fondo sobrecargada en formas "gruesas" 1C82
Fig. 5. Imagen de fondo sobrecargada en formas "gruesas" 1C82.

Del mismo modo, puede obtener información sobre el módulo presionando la tecla "Alt + L", como en la fig. 3)

1C82 formularios gestionados


Para los formularios administrados en 1C82, aún puede encontrar la ventana que necesitamos en el séptimo nivel de anidación, como " V8FormElement " y dibujar en ella, pero de alguna manera no es interesante.

Para nosotros, de estas consideraciones se deduce que es más fácil crear una ventana externa con un mensaje de felicitación (Fig. 6) que manejar cada caso individual. La ventana en sí puede cerrarse, o más bien minimizarse con " Esc ", " Ctrl + F4 ", " Alt + F4 " o haciendo clic en la " cruz ".

Fig. 6. Imagen de felicitación en una configuración de prueba en formularios administrados 1C82
Fig. 6. Imagen de felicitación en una configuración de prueba en formularios administrados 1C82.

Además, la ventana minimizada (Fig. 7) puede expandirse nuevamente.

Fig. 7. La imagen minimizada de la ventana externa en los formularios administrados 1C82
Fig. 7. Una imagen minimizada de la ventana externa en los formularios administrados 1C82.

Las dimensiones y la posición relativa de la ventana externa se pueden cambiar, todo está como de costumbre aquí (ver imágenes ampliadas de ventanas externas en la Fig. 6 y Fig. 10). Tenga en cuenta que las teclas de acceso rápido solo funcionan si la ventana externa está activa.

1C83 formas convencionales


En 1C83 no hay más ventanas secundarias, lo que puede servir como criterio para la versión 1C en nuestro dll. Además, los formularios "gruesos" son una ventana de marco (Fig. 8), y los formularios administrados no tienen marco (Fig. 9). Es decir, todo lo que no sea un marco se puede volver a dibujar. Un marco también se puede volver a dibujar, pero solo como un elemento del sistema.
Fig. 8. Ventana de marco en formas "gruesas" 1C83Fig. 9. Ventana sin marco en formularios gestionados 1C83
Fig. 8. Ventana de marco en formas "gruesas" 1C83.Fig. 9. Ventana sin marco en formas controladas 1C83.
Aquí creamos una ventana de prueba usando una biblioteca dinámica y la subordinamos a la ventana principal 1C. La diferencia de comportamiento se ve en las figuras.

1C83 formularios gestionados


En el caso de 1C83, como en los formularios administrados 1C82, dibujaremos nuestras felicitaciones no en el fondo, sino en una ventana separada, cuyo prototipo se muestra en la Fig. 8-9. Como resultado, el componente deseado ( LionExt32.dll o LionExt64.dll ) dará el siguiente resultado (Fig. 10-12).

Fig. 10. La imagen de fondo en la ventana externa para formularios convencionales 1C83
Fig. 10. La imagen de fondo en la ventana externa para los formularios convencionales 1C83.

Fig. 11. Imagen de fondo en la ventana externa de los formularios administrados 1C83, versión 14, versión de 64 bits
Fig. 11. La imagen de fondo en la ventana externa de los formularios administrados 1C83, versión 14, versión de 64 bits.

Fig. 12. Imagen de fondo en la ventana externa de los formularios administrados 1C83, versión 15, versión de 64 bits
Fig. 12. La imagen de fondo en la ventana externa de los formularios administrados 1C83, versión 15, versión de 64 bits.

Hallazgos preliminares


Este componente se usó realmente en la práctica (Fig. 1), el contador principal estaba satisfecho, todo salió maravillosamente. En el camino, resultó que a los usuarios les gusta elegir sus propias imágenes de fondo, en este caso, para trabajar en el "siete". Para el G8, nuestro componente está adaptado con una reserva para el futuro, mientras que debe considerarse como una versión demo.

El interés aquí era que este componente no requería conformidad con la tecnología de creación de componentes externos a partir de 1C . Quizás surjan ideas adicionales para ampliar sus capacidades. Por ejemplo, para configuraciones que son totalmente compatibles, no desea realizar cambios en el código 1C sin necesidad especial. En este caso, se podría ofrecer la opción de cargar externamente una dll arbitraria en el espacio de direcciones 1C. Pero este es el tema de otro artículo.

De las innovaciones técnicas, se utilizó un bloqueo para descargar nuestro componente con la plataforma 1C (ya que no cumple con el formato VK). Además, otro truco permitió asignar un menú local a la ventana secundaria, ya que el sistema operativo Windows bloquea la creación de dicho menú para ventanas subordinadas. Por lo tanto, no verá menús locales en el mismo MDI (Interfaz de documentos múltiples) en ningún lado. Es reemplazado por paneles de comandos, barras de herramientas y un menú contextual. Todavía hay un momento para actualizar Windows. A veces sucede que ni UpdateWindow () ni InvalidateRect () funcionan correctamente. Pero un par de funciones en este caso son exitosas:

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

También debe tenerse en cuenta que nuestro componente puede entrar en conflicto con otros, por ejemplo, con FormEx.dll para 1C77. En este caso, debe cargarse en último lugar.

Por cierto, se observa que si crea una configuración en la versión 1C-8.3.14 y superior, entonces el componente no se carga de manera regular. Pero si la base de datos se creó en una versión anterior de 1C y se abre en las últimas versiones, entonces no hay problemas para cargar nuestro VK. Una vez más, esto sugiere la necesidad de crear un gestor de arranque externo.

Este proyecto utiliza el subsistema WinAPI GDI + . Al usarlo, puede mostrar imágenes de varios formatos: bmp, jpg, gif, png, tif y otros. En el mismo orden, el componente intenta cargar el primer archivo Main. * Disponible desde el directorio local de fotos en la configuración actual. Si no se encuentra ninguno de estos archivos, se utiliza una imagen de fondo simple de los recursos del componente. En la fig. La Figura 13 muestra esta imagen de fondo para las formas habituales de 1C83 de 64 bits, versión 15. Para variar, la ventana externa de la jerga se ha ampliado y otra imagen del archivo Main1.png , que se ha "mosaico", se ha agregado a su fondo.

Fig. 13. El fondo de pantalla predeterminado para formularios regulares 1C83 de 64 bits, versión 15
Fig. 13. La imagen de fondo predeterminada para las formas habituales de 1C83 de 64 bits, versión 15. Además, se ha agregado otra imagen del archivo Main1.png, "mosaico".

No hay diferencia en la operación del componente en diferentes modos de bit.

También se puede observar que nuestro componente subclasifica la ventana principal 1C y su cliente MDI, si corresponde. Esto, aparentemente, sirve como una fuente de conflicto con FormEx.dll cuando se carga por última vez (en 1C77).

Segunda parte: técnica


El proyecto en sí se puede encontrar en los siguientes enlaces:


Un proyecto C ++ se puede adaptar fácilmente para la versión 10 si la cadena “ v120 ” se reemplaza por “ v100 ” y “ ToolsVersion =“ 12.0 ” ” por “ ToolsVersion =“ 4.0 ” en los archivos de configuración.

El código para las versiones de 32 bits y 64 bits de 1C es el mismo y puede compilarse al mismo tiempo.

La versión 1C77 está determinada en el componente externo por el controlador de función GetMenu () distinto de cero, y la versión 1C83, por la ausencia de ventanas secundarias en la ventana principal, cuyo controlador está determinado por la función GetForegroundWindow () .

Sobre la tecnología de creación de componentes externos para 1C


En los discos ITS de la compañía 1C, y en Internet, se puede encontrar fácilmente información sobre la creación de VC y las plantillas correspondientes en diferentes lenguajes de programación. Sin embargo, en los tiempos del 1C77, estos patrones satisfacían "no solo a todos".

Si observa algunos componentes ampliamente utilizados, especialmente para 1C77, verá que sus autores a menudo utilizan métodos de programación especiales para ampliar las capacidades de sus diseños.

Quizás uno de los primeros componentes externos fue "RAINBOW ADDIN 2000 para 1C: Enterprise 7.7" . Quizás lo más importante aquí fue una penetración más profunda en los intestinos de los "siete" que la tecnología VK oficial permitida, aunque siguió el formato VK. Esto se logró debido a los encabezados recibidos, posiblemente posiblemente no estándar, encabezados (archivos * .h) de los archivos de la biblioteca 1C77 utilizados en otros proyectos ampliamente conocidos.

De hecho, si las funciones 1C como LoadExternalComponent () y ConnectExternalComponent () le permiten incrustar archivos dlls externos en su propio espacio de direcciones (en primer lugar, que satisfacen el formato de tecnología VK), entonces ¿por qué los programas de usuario no sucumben a la tentación e intentan acceder a otros ocultos? ellos, procedimientos y otros objetos de la plataforma de destino? Este enfoque ha sido demostrado con éxito por el componente Rainbow.dll .

Posteriormente, otros autores del componente 1C versión 7.7 adoptaron un mecanismo similar. De particular interés es el componente para el "siete" 1C ++. Dll y su, por así decirlo , un caso especial de FormEx.dll .

Pero el enfoque no trivial para el diseño de componentes externos para 1C77 no terminó ahí. Aparentemente, alguien debería haber dicho: “¿Por qué necesitamos un herrero? ¡No necesitamos un herrero! " Aquí, por "herrero" nos referimos a la tecnología COM de MicroSoft, que, en cierto sentido, fue seguida por la tecnología VK para los "siete". No, bueno, realmente, ¿por qué necesitamos un registro si descargamos nuestro VK directamente? Esto puede tener sentido para los navegadores web que funcionan con Internet, pero para la operación local, el uso del registro es claramente redundante. Por lo menos, esto no debería ser un requisito previo. Además, para editar el registro necesita derechos administrativos.

Tenga en cuenta que 1C era muy aficionado a esta tecnología (al menos hasta que se transfirió 1C a Linux). La tratamos muy bien. COM es conveniente para usar el componente ActiveX y esto es natural, ya que este último se desarrolló originalmente para Internet.

Sin embargo, en las últimas versiones, 1C agregó la capacidad de usar la tecnología Native API , lo que elimina la necesidad de un registro. En principio, esto es lo que necesitamos, excepto que esta tecnología no es aplicable en los "siete" y, para algunos, sigue siendo relevante.

Pero a veces surgen tareas relativamente simples cuando no desea utilizar un montón de código repetitivo para VK y es recomendable trabajar con 1C solo desde el lado del componente externo. Como, digamos, en nuestro caso, la demostración de una imagen de felicitación en el área del cliente o, si es necesario, en una ventana separada, la configuración 1C.

En otras palabras, si no vamos a intercambiar datos directamente entre 1C y VK, entonces estaremos bastante contentos con una versión más simple y más universal del componente externo para 1C. La simplicidad aquí se logrará debido a la falta de código repetitivo.

Tecnología alternativa para crear VK para 1C


Dado que VK para 1C es un caso especial de un servidor COM (antes de la tecnología API nativa ), hubo desarrolladores de VK que dijeron: "¡COM - no!". La actividad en esta dirección de Alexander Orefkov es especialmente notable. Sus componentes " 1sqlite.dll ", " TurboMD.dll ", y posiblemente otros, no usan COM de la palabra "completamente". El componente Yoksel (" SpreadSheet.dll ") también se desarrolla a lo largo de este camino.

Pero, ¿cómo carga entonces el cargador VK de 1C77 estos componentes? Después de todo, ni siquiera intentan imitar algún tipo de COM allí. De hecho, si tratamos de deslizar sin rodeos algunos dll estándar generados por, por ejemplo, el asistente MS VC ++ en la función LoadExternalComponent () , entonces tendremos un fastidio.

En el "siete" recibimos un mensaje como:
Se produjo un error al crear un objeto desde el componente <Ruta completa \ Nombre del componente> .dll (falta CLSID)

En el cliente "grueso" de 32 bits del mensaje "ocho" será similar. El mismo dll causará un juramento similar (Fig. 15):
Error al llamar al método de contexto (Cargar componente externo): Error al cargar componente externo

Aún así, ¿cómo resuelven este problema las bibliotecas mencionadas? Al estudiar los textos de los programas Orefkov y Yoksel, finalmente concluimos que las siguientes " líneas mágicas " en el archivo de recursos (* .rc o * .rc2) son "culpables":

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

Es decir sin falta, en los recursos del programa hay una línea con el identificador 100 y algún valor de cadena, cuyo primer carácter es cero. Puede experimentar con variaciones de tales cadenas, pero la cadena " \ 0L " está bien conmigo. Por lo tanto, creamos un archivo de recursos y escribimos líneas como esta:

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

Conectamos este archivo a nuestro proyecto dll más simple generado por el asistente de MS C ++, agregue el código:

 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() 

y observar (Fig. 14).

Fig. 14. Uso del "VK" más simple en 1C82
Fig. 14. Uso del "VK" más simple en 1C82.

Sin "líneas mágicas" en el archivo de recursos, nuestro dll, después de mostrar MessageBox, se descarga inmediatamente con una maldición de 1C (Fig. 15).

Fig. 15. Error al cargar dll regular en 1C82
Fig. 15. Error al cargar dll regular en 1C82.

Es decir, estas líneas realmente tienen un efecto mágico en el cargador de componentes externos 1C.

Parece que las primeras "líneas mágicas" fueron descritas en su antiguo artículo por Alexei Fedorov (ALF) , pero el enlace ya no está disponible, y el autor no ve el punto en su nueva publicación. Además, Alexander Orefkov los utilizó con mayor intensidad, y aparentemente, según su presentación, el autor fue Yoksel . Por lo tanto, hablaremos sobre las líneas "mágicas" de Fedorov-Orefkov . Su significado es bloquear la descarga de archivos dll no estándar (desde el punto de vista de 1C) mediante la función LoadExternalComponent () . Además, como vemos, esta técnica funciona no solo en 1C77, sino también en formas "gruesas" 1C82.

Sin embargo, en los formularios administrados 1C82 y en todas las versiones de 1C83, esta característica ya está completamente bloqueada (apareció otro cargador: Conectar componente externo () ).

Por lo tanto, en las versiones modernas de 1C, debe buscar otras alternativas simples a las líneas "mágicas" de Fedorov-Orefkov.

Y esa alternativa es fácil de ofrecer. El punto es simple. El cargador 1C descarga el componente "incorrecto" si arroja una excepción al intentar acceder a él utilizando el protocolo especificado, por ejemplo, al solicitar la versión del componente. Naturalmente, no tenemos nada de este tipo, que sirve como base para descargar un archivo DLL no estándar. Pero el sistema puede ignorar el requisito de 1C para que el sistema operativo descargue esta biblioteca dinámica si este VK todavía se usa en alguna parte. En lugar de la eliminación en sí, el sistema simplemente reduce el contador de uso del módulo deseado. Y eliminar físicamente no antes de que se restablezca este contador. Por lo tanto, nuestra tarea es aumentar artificialmente este contador.

Para hacer esto, puede llamar a nuestra función dll WinAPI LoadLibrary () nuevamente en la sección DLL_THREAD_ATTACH

 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() 

Eso es todo! El problema está resuelto. Recuperar la misma biblioteca dinámica aumentará su contador de uso en uno, y la descarga (con acceso preliminar a la sección DLL_THREAD_DETACH ) disminuirá en uno. Total tenemos 2 - 1 = 1> 0 , por lo tanto, el sistema operativo no descargará nuestro dll. Además, lo que es importante, no se reiniciará la sección DLL_PROCESS_ATTACH .

A partir de esto, por cierto, uno puede ver cómo 1C puede lidiar con un truco similar en sus últimas versiones (y, aparentemente, ya lo hace en las configuraciones creadas en 1C-8.3.14 y superior). Puede usar la función LoadLibraryEx () con un parámetro que bloquea la ejecución de la sección de inicialización DLL_PROCESS_ATTACH , después de lo cual llamará inmediatamente a las funciones exportadas necesarias. Y, de hecho, si observa el código del ejemplo de VK para la API nativa, puede ver que no es necesario llamar al código de inicialización, ya que debe estar vacío por el formato VK.

Con respecto a los ejemplos de uso de la tecnología COM, es obvio que la ejecución de la sección de inicialización DLL_PROCESS_ATTACH es necesaria allí, por lo tanto, en versiones no demasiado nuevas de 1C, más precisamente, en las configuraciones realizadas en 1C-8.3.13 y siguientes, el cargador 1C es adecuado para nosotros:

 (, , .COM); 

Aquí se puede eliminar el último parámetro, ya que está implícito de forma predeterminada. Al mismo tiempo, se pueden abrir normalmente en cualquier versión superior. En las versiones 1C83, el cargador de arranque anterior LoadExternalComponent (Dirección del componente) ya no nos conviene (respectivamente, las "líneas mágicas" de Fedorov-Orefkov no funcionan allí).

En el caso general, como ya se mencionó, el problema se puede resolver utilizando un gestor de arranque externo. O, lo cual es bastante natural, observar, en una medida u otra, la tecnología de los componentes externos de 1C.

También debe tenerse en cuenta que los experimentos que realizamos en versiones de archivo de 1C con diferentes profundidades de bits. Para descargar nuestro componente, es posible que deba configurar la propiedad " Modo de uso de llamadas síncronas " en " Usar " en la configuración.

También debe entenderse que lleva a cabo el uso de dicha técnica bajo su propio riesgo, experimente de antemano las configuraciones de prueba o copias de los trabajadores para evitar posibles problemas en los programas principales.

Actualización del 11/09/2019


Resultó que me preocupaba en vano que: "en las versiones 1C-8.3.14 y superiores, la sección de inicialización en el componente externo ya no se realiza a partir de la palabra" completamente "".

Resulta que solo el mensaje de retorno en la función ConnectExternalComponent () no necesita ser procesado. Además, no importa qué tipo de componente especifiquemos: COM o Native API .

Por lo tanto, puede crear una configuración en todas las versiones actualmente disponibles de 1C, nuestro componente debería funcionar bien en todas partes, y la creación de un gestor de arranque externo será relevante, a menos que no desee cambiar la configuración, que es totalmente compatible.

A este respecto, el código en las configuraciones de prueba para 1C82 y 1C83 se modifica ligeramente, aunque las diferencias entre ellos ya no son fundamentales.

Al mismo tiempo, nuestra observación de que la compañía 1C puede bloquear fácilmente la ejecución del código de inicialización en cualquier VK, al menos para componentes externos como la API nativa , obviamente sigue siendo válida, porque a juzgar por su plantilla, esto no es necesario. Para un COM tipo VK , existe tal necesidad hasta ahora, pero ¿qué le impide deshacerse de él? Al mismo tiempo, ¿veremos si tienen en cuenta esta información?

Source: https://habr.com/ru/post/466713/


All Articles