Havia uma ideia aqui para parabenizar nossa contadora-chefe de uma maneira mais ou menos original, por exemplo, com a ajuda de seu programa 1C favorito? Mas como
Depois de pensar um pouco, a idéia veio para usar como parabéns em segundo plano a imagem de segundo plano na área do cliente de formulários convencionais para configurações em 1C77-1C82 ou em uma janela externa para os formulários gerenciados 1C82 e em todos os casos para 1C83. Nele, exiba a mensagem desejada e forneça links para o vídeo de felicitações, conforme mostrado na figura.

Parte Um - Resultante
Obviamente, essa ideia não é nova. Portanto, em 2011, uma
solução semelhante foi proposta com base no
FormEx.dll, por Aleksey Fedorov, também conhecido como ALF . E perguntas sobre
como conseguir isso foram feitas em 2008.
Ao mesmo tempo, também usamos esse componente para carregar a imagem de plano de fundo no 1C77. Mas o carregamento de arquivos bmp grandes (e outros não podiam ser usados) era lento (por causa disso, pequenas imagens colocadas com ladrilhos foram usadas); portanto, havia um desejo de escrever seu próprio componente externo (VK), que só baixaria as imagens necessárias e nada mais, a menos que o que mais pode ser um campo de testes para experimentos.
Esse componente foi gravado (também, apenas para arquivos bmp, usando, se necessário, lado a lado). A função
WinAPI LoadImage () foi usada lá. Esta DLL não entrou em conflito com FormEx.dll, era simples, rápido o suficiente e serviu por um longo tempo.
Tudo isso foi maravilhoso, mas estava na hora de expandir suas capacidades, e aqui era necessária uma abordagem diferente.
Neste artigo, não abordamos os problemas de criação de arquivos multimídia. Esta não é a nossa especialidade. Nós nos restringimos apenas a algumas nuances da programação de componentes externos para 1C.
1C77
Como as versões da plataforma 1C podem ser diferentes, pode haver várias soluções. No nosso caso, essas eram configurações em 1C77 (Fig. 1).

Fig. 1. Imagem de felicitações na configuração de teste em 1C77.
O vídeo aqui, embora seja seu, mas a idéia de sua criação é
colhida em Anna Shiyanova, sob o apelido de "Caso especial" . Essa garota tem talento, pode ser imitada, mas dificilmente é possível repetir completamente o estilo. Nesse caso, eu só queria pelo menos algum elemento de criatividade.
Se um dos colegas já estiver cansado de olhar para os parabéns de outras pessoas, poderá sobrecarregar a imagem
pressionando “
Alt + I ” (Fig. 2-3).

Fig. 2. Selecionando uma imagem de plano de fundo diferente no menu "Arquivo / Selecionar plano de fundo" ou em "Alt + I".
E, ao mesmo tempo, veja as informações sobre o módulo usado por "
Alt + L " (Fig. 3).

Fig. 3. Imagem de plano de fundo sobrecarregada, juntamente com informações sobre o programa ("Ajuda / Sobre o módulo LionExt32.dll" ou "Alt + L").
Formas convencionais 1C82
Naturalmente, a maioria agora está voltada para o G8 (1C8x). No entanto, trabalhar com a imagem de plano de fundo em 1C é possível apenas em formulários comuns na versão 8.2 e menos, e se você não usar nenhum processamento iniciado no modo "área de trabalho", que simplesmente se sobrepõe completamente ao plano de fundo (Fig. 4).

Fig. 4. Imagem de congratulações na configuração de teste nos formulários usuais 1C82.
Observe que os links para a Fig. 4 não indicam nosso vídeo. Eles são mostrados apenas para o teste.
Em formas comuns, o 1C82 não fornece mais a maneira padrão de acessar o menu, pois não é sistêmico lá, como nos "sete", mas "próprio" (embora o sistema possa ser criado, mas por que precisamos de dois menus principais?). No entanto, teclas de atalho podem ser usadas. Da mesma forma, “Alt + I”, em nosso componente, chamamos uma caixa de diálogo, como na Fig. 2, e carregamos outro fundo (Fig. 5).

Fig. 5. Imagem de fundo sobrecarregada em formulários 1C82 "espessos".
Da mesma forma, você pode obter informações sobre o módulo pressionando a tecla "Alt + L", como na fig. 3)
Formulários gerenciados 1C82
Para formulários gerenciados em 1C82, você ainda pode encontrar a janela que precisamos no sétimo nível de aninhamento, como "
V8FormElement ", e desenhar nela, mas de alguma forma não é interessante.
Para nós, a partir dessas considerações, conclui-se que é mais fácil criar uma janela externa com uma mensagem de felicitações (Fig. 6) do que lidar com cada caso individual. A janela em si pode ser fechada, mais precisamente, minimizada por "
Esc ", "
Ctrl + F4 ", "
Alt + F4 " ou clicando na "
cruz ".

Fig. 6. Imagem de congratulações em uma configuração de teste nos formulários gerenciados 1C82.
Além disso, a janela minimizada (Fig. 7) pode ser expandida novamente.

Fig. 7. Uma imagem minimizada da janela externa nos formulários gerenciados 1C82.
As dimensões e a posição relativa da janela externa podem ser alteradas, tudo é como de costume aqui (veja imagens ampliadas das janelas externas nas Fig. 6 e Fig. 10). Observe que as teclas de atalho funcionam apenas se a janela externa estiver ativa.
Formas convencionais 1C83
No 1C83, não há mais janelas filho, o que pode servir como critério para a versão 1C em nossa DLL. Além disso, as formas “grossas” são uma janela de quadro (Fig. 8), e as formas gerenciadas são sem moldura (Fig. 9). Ou seja, tudo o que não é um quadro pode ser redesenhado. Um quadro também pode ser redesenhado, mas apenas como um elemento do sistema.
Aqui, criamos uma janela de teste usando uma biblioteca dinâmica e a subordinamos à janela 1C principal. A diferença de comportamento é vista nas figuras.
Formulários gerenciados 1C83
No caso de 1C83, como nos formulários gerenciados 1C82, chamaremos nossos parabéns não contra o pano de fundo, mas em uma janela separada, cujo protótipo é mostrado na Fig. 8-9. Como resultado, o componente desejado (
LionExt32.dll ou
LionExt64.dll ) fornecerá o seguinte resultado (Fig. 10-12).

Fig. 10. A imagem de fundo na janela externa para os formulários convencionais 1C83.

Fig. 11. A imagem de plano de fundo na janela externa dos formulários gerenciados 1C83, versão 14, versão de 64 bits.

Fig. 12. A imagem de plano de fundo na janela externa dos formulários gerenciados 1C83, versão 15, versão de 64 bits.
Conclusões preliminares
Esse componente foi realmente usado na prática (fig. 1), o contador-chefe ficou satisfeito, tudo correu maravilhosamente bem. Ao longo do caminho, descobriu-se que os usuários gostam de escolher suas próprias imagens de fundo, neste caso, para trabalhar nos "sete". Para o G8, nosso componente é adaptado com uma reserva para o futuro, embora deva ser considerado como uma versão demo.
O interesse aqui foi que
esse componente não exigisse conformidade com a tecnologia de criação de componentes externos a partir de 1C . Talvez surjam idéias adicionais para expandir suas capacidades. Por exemplo, para configurações totalmente suportadas, você não deseja fazer alterações no código 1C sem necessidade especial. Nesse caso, pode-se oferecer a opção de carregar externamente uma dll arbitrária no espaço de endereço 1C. Mas este é o tópico de outro artigo.
Das inovações técnicas, um bloqueio foi usado para descarregar nosso componente com a plataforma 1C (uma vez que não está em conformidade com o formato VK). Além disso, outro truque tornou possível atribuir um
menu local à janela filho, pois o sistema operacional Windows bloqueia a criação desse menu para janelas subordinadas. Portanto, você não verá menus locais no mesmo
MDI (Multi Document Interface) em nenhum lugar. Ele é substituído por painéis de comando, barras de ferramentas e um menu de contexto. Ainda há um momento para atualizar o Windows. Às vezes, acontece que nem
UpdateWindow () nem
InvalidateRect () funcionam corretamente. Mas algumas funções neste caso são bem-sucedidas:
ShowWindow(hWnd, SW_HIDE); ShowWindow(hWnd, SW_SHOW);
Observe também que nosso componente pode entrar em conflito com outras pessoas, por exemplo, com o FormEx.dll para 1C77. Nesse caso, ele precisa ser carregado por último.
A propósito, note-se que, se você criar uma configuração na versão 1C-8.3.14 e superior, o componente não será carregado de maneira regular. Mas se o banco de dados foi criado em uma versão anterior do 1C e aberto nas versões mais recentes, não há problemas ao carregar nosso VK. Isso mais uma vez sugere a necessidade de criar um carregador de inicialização externo.
Este projeto usa o subsistema
WinAPI GDI + . Com ele, você pode exibir imagens de vários formatos:
bmp, jpg, gif, png, tif e outros. Na mesma ordem, o componente tenta carregar o primeiro arquivo
Main. * Disponível no diretório local do
Pics na configuração atual. Se nenhum desses arquivos for encontrado, uma imagem de plano de fundo simples dos recursos do componente será usada. Na fig. A Figura 13 mostra essa imagem de plano de fundo para as formas usuais do 1C83 de 64 bits, versão 15. Para uma mudança, a janela externa da gíria foi ampliada e outra imagem do arquivo
Main1.png , que foi “lado a lado”, foi adicionada ao plano de fundo.

Fig. 13. A imagem de plano de fundo padrão para as formas usuais do 1C83 de 64 bits, versão 15. Além disso, foi adicionada outra imagem do arquivo Main1.png, colocada em mosaico.
Não há diferença na operação do componente em diferentes modos de bits.
Também é possível observar que nosso componente subclasse a janela principal 1C e seu cliente MDI, se houver. Aparentemente, isso serve como fonte de conflito com o FormEx.dll quando ele é carregado pela última vez (em 1C77).
Parte Dois - Técnico
O projeto em si pode ser encontrado nos seguintes links:
Um projeto
C ++ pode ser facilmente adaptado para a versão
10 se a string "
v120 " for substituída por "
v100 " e "
ToolsVersion =" 12.0 " " por "
ToolsVersion =" 4.0 " nos arquivos de configuração.
O código para as versões de
32 bits e
64 bits do
1C é o mesmo e pode ser compilado ao mesmo tempo.
A versão 1C77 é determinada no componente externo pelo
identificador da função GetMenu
() diferente de zero e a versão 1C83, pela ausência de janelas filho na janela principal, cujo identificador é determinado pela função
GetForegroundWindow () .
Sobre a tecnologia de criação de componentes externos para 1C
Nos discos ITS da empresa 1C e na Internet, é possível encontrar facilmente informações sobre a criação do VC e os modelos correspondentes em diferentes linguagens de programação. No entanto, nos tempos de 1C77, esses padrões satisfaziam "não apenas todos".
Se você observar alguns componentes amplamente utilizados, especialmente para 1C77, verá que seus autores costumavam usar métodos de programação especiais para expandir os recursos de seus projetos.
Talvez um dos primeiros componentes externos tenha sido
"RAINBOW ADDIN 2000 for 1C: Enterprise 7.7" . Talvez o mais importante aqui tenha sido uma penetração mais profunda nas entranhas dos "sete" do que a tecnologia oficial da VK permitia, embora seguisse o formato da VK. Isso foi alcançado devido aos métodos recebidos, possivelmente não padronizados, dos cabeçalhos (arquivos * .h) dos arquivos de biblioteca 1C77 usados em outros projetos amplamente conhecidos.
De fato, se funções 1C como
LoadExternalComponent () e
ConnectExternalComponent () permitem incorporar
DLLs externas em seu próprio espaço de endereço (antes de tudo, que satisfaçam o formato da tecnologia VK), por que os programas do usuário não sucumbem à tentação e tentam acessar outros ocultos? procedimentos, e outros objetos da plataforma de destino? Essa abordagem foi demonstrada com êxito pelo componente
Rainbow.dll .
Posteriormente, um mecanismo semelhante foi adotado por outros autores do componente 1C versão 7.7. De nota particular é o componente para o "seven"
1C ++. Dll e, por assim dizer, um caso especial de
FormEx.dll .
Mas a abordagem não trivial ao design de componentes externos para 1C77 não terminou aí. Aparentemente, alguém deveria ter dito: “Por que precisamos de um ferreiro? Nós não precisamos de um ferreiro! " Aqui, por "ferreiro", entendemos a tecnologia COM da MicroSoft, que, em certo sentido, foi seguida pela tecnologia VK para os "sete". Não, bem, sério, por que precisamos de um registro se baixarmos nosso VK diretamente? Isso pode fazer sentido para navegadores da Web que funcionam com a Internet, mas para operações locais, o uso do registro é claramente redundante. No mínimo, isso não deve ser um pré-requisito. Além disso, para editar o registro, você precisa de direitos administrativos.
Observe que 1C gostava muito dessa tecnologia (pelo menos até portar 1C para Linux). Nós a tratamos bem legal. O COM é conveniente para o uso do componente ActiveX, e isso é natural, pois os últimos foram originalmente desenvolvidos para a Internet.
No entanto, nas versões mais recentes, o 1C adicionou a capacidade de usar a tecnologia
API nativa , o que elimina a necessidade de um registro. Em princípio, é disso que precisamos, exceto que essa tecnologia não é aplicável nos "sete" e, para alguns, ainda é relevante.
Às vezes, porém, tarefas relativamente simples surgem quando você não deseja usar um monte de código padrão para o VK e é aconselhável trabalhar com 1C apenas do lado do componente externo. Como, digamos, no nosso caso, a demonstração de uma imagem de congratulações na área do cliente ou, se necessário, em uma janela separada, a configuração 1C.
Em outras palavras, se não vamos trocar dados diretamente entre 1C e VK, ficaremos muito felizes com uma versão mais simples e universal do componente externo para 1C. A simplicidade aqui será alcançada devido à falta de código padrão.
Tecnologia alternativa para a criação de VK para 1C
Como o VK for 1C é um caso especial de
servidor COM (antes da tecnologia
Native API ), houve desenvolvedores de VK que disseram: "COM - não!". A atividade nessa direção de
Alexander Orefkov é especialmente notável. Seus componentes "
1sqlite.dll ", "
TurboMD.dll " e possivelmente outros não usam COM da palavra "completamente". O componente
Yoksel ("
SpreadSheet.dll ") também se desenvolve nesse caminho.
Mas como então o carregador VK de 1C77 carrega esses componentes? Afinal, eles nem tentam imitar algum tipo de COM lá. De fato, se tentarmos colocar uma dll padrão gerada pelo assistente do
MS VC ++ sem rodeios, digamos, na função
LoadExternalComponent () , teremos uma chatice.
Nos "sete", recebemos uma mensagem como:
Ocorreu um erro ao criar um objeto do componente .dll <Caminho completo \ Nome do componente>. (Falta o CLSID)
No "grosso" cliente de 32 bits da mensagem "oito" será semelhante. A mesma DLL causará um palavrão semelhante (Fig. 15):
Erro ao chamar o método de contexto (Carregar componente externo): Erro ao carregar o componente externo
Ainda assim, como as bibliotecas mencionadas resolvem esse problema? Estudando os textos dos programas Orefkov e Yoksel, finalmente concluímos que as seguintes "
linhas mágicas " no arquivo de recursos (* .rc ou * .rc2) são "responsáveis":
STRINGTABLE DISCARDABLE BEGIN 100 "\0sd" // 1sqlite.dll 100 "\0tmd" // TurboMD.dll 100 "\0f" // SpreadSheet.dll END
I.e. sem falha, nos recursos do programa há uma linha com o identificador
100 e algum valor de sequência, cujo primeiro caractere é zero. Você pode experimentar variações dessas cadeias, mas a cadeia "
\ 0L " está bem comigo. Assim, criamos um arquivo de recurso e escrevemos linhas como esta:
STRINGTABLE DISCARDABLE BEGIN 100 "\0L" // 1 ! END
Conectamos esse arquivo ao nosso projeto dll mais simples gerado pelo assistente do MS C ++, adicione o 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; }
e observe (Fig. 14).

Fig. 14. Uso do "VK" mais simples em 1C82.
Sem "linhas mágicas" no arquivo de recursos, nossa DLL, após mostrar o MessageBox, descarrega imediatamente com uma maldição de 1C (Fig. 15).

Fig. 15. Erro ao carregar a dll regular em 1C82.
Ou seja, essas linhas realmente têm um efeito mágico no carregador de componentes externos 1C.
A primeira, ao que parece, “linhas mágicas” foi descrita em seu antigo artigo por
Alexei Fedorov (ALF) , mas o link para ele não está mais disponível e o autor não vê o ponto em sua republicação. Além disso,
Alexander Orefkov os usou com mais intensidade e, aparentemente, a partir de sua submissão, o autor era
Yoksel . Portanto, falaremos sobre as
linhas "mágicas" de Fedorov-Orefkov . Seu significado é bloquear o descarregamento de arquivos dll não padrão (do ponto de vista de 1C) pela função
LoadExternalComponent () . Além disso, como vemos, essa técnica funciona não apenas em 1C77, mas também em formas 1C82 "grossas".
No entanto, nos formulários gerenciados 1C82 e em todas as versões do 1C83, esse recurso já foi completamente quebrado (outro carregador também apareceu -
ConnectExternalComponent () ).
Assim, nas versões modernas de 1C, você precisa procurar outras alternativas simples para as linhas "mágicas" de Fedorov-Orefkov.
E essa alternativa é fácil de oferecer. O ponto é simples. O carregador 1C descarrega o componente “errado” se lançar uma exceção ao tentar acessá-lo usando o protocolo especificado, por exemplo, ao solicitar a versão do componente. Naturalmente, não temos nada desse tipo, que serve como base para descarregar uma dll não padrão. Mas o requisito de 1C para o sistema operacional descarregar essa biblioteca dinâmica pode ser ignorado pelo sistema se esse VK ainda for usado em algum lugar. Em vez da exclusão, o sistema simplesmente reduz o contador de uso do módulo desejado. E exclua fisicamente não antes que esse contador seja redefinido. Portanto, nossa tarefa é aumentar artificialmente esse contador.
Para fazer isso, você pode chamar nossa função DLL WinAPI
LoadLibrary () novamente na seção
DLL_THREAD_ATTACH BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { switch(dwReason) { case DLL_PROCESS_ATTACH: { WCHAR szDllName[_MAX_PATH] = {0};
Isso é tudo! O problema está resolvido. A recuperação da mesma biblioteca dinâmica aumentará seu contador de uso em um e o descarregamento (com acesso preliminar à seção
DLL_THREAD_DETACH ) diminuirá em um. No total, temos
2 - 1 = 1> 0 , portanto, o sistema operacional não descarregará nossa DLL. Além disso, o que é importante, a reinicialização da seção
DLL_PROCESS_ATTACH não ocorrerá.
A partir disso, a propósito, pode-se ver como o 1C pode lidar com um truque semelhante em suas versões mais recentes (e, aparentemente, ele já faz isso nas configurações criadas em 1C-8.3.14 e superior). Ele pode usar a função
LoadLibraryEx () com um parâmetro que bloqueia a execução da seção de inicialização
DLL_PROCESS_ATTACH , após o qual chamará imediatamente as funções exportadas necessárias. E, de fato, se você olhar o código do exemplo da VK para a API nativa, poderá ver que não há necessidade de chamar o código de inicialização, pois ele deve estar vazio pelo formato VK.
Em relação aos exemplos de uso da tecnologia COM, é óbvio que a execução da seção de inicialização
DLL_PROCESS_ATTACH é necessária lá, portanto, em versões não muito novas de 1C, mais precisamente, nas configurações feitas em 1C-8.3.13 e abaixo, o carregador 1C é adequado para nós:
(, , .COM);
Aqui o último parâmetro pode ser removido, pois está implícito por padrão. Ao mesmo tempo, eles podem abrir normalmente em qualquer versão superior. Nas versões 1C83, o carregador de inicialização anterior
LoadExternalComponent (Endereço do componente) não é mais adequado para nós (respectivamente, as “linhas mágicas” de Fedorov-Orefkov não funcionam lá).
No caso geral, como já mencionado, o problema pode ser resolvido usando um carregador de inicialização externo. Ou, o que é bastante natural, observar, de uma maneira ou de outra, a tecnologia dos componentes externos de 1C.
Deve-se notar também que os experimentos que realizamos nas versões de arquivo de 1C com diferentes profundidades de bits. Para baixar nosso componente, pode ser necessário definir a propriedade "
Modo de uso de chamada síncrona " como "
Usar " na configuração.
Também deve ser entendido que você realiza o uso de tal técnica por seu próprio risco, experimenta previamente configurações de teste ou cópias de trabalhadores para evitar possíveis problemas nos programas principais.
Atualização de 11/09/2019
Acontece que fiquei em vão preocupado que: “nas versões 1C-8.3.14 e superior, a seção de inicialização no componente externo não é mais executada a partir da palavra“ completamente ”.”
Acontece que apenas a mensagem de retorno na função
ConnectExternalComponent () não precisa ser processada. Além disso, independentemente do tipo de componente especificado:
COM ou
API nativa .
Assim, você pode criar uma configuração em todas as versões atualmente disponíveis do 1C, nosso componente deve funcionar bem em qualquer lugar e a criação de um gerenciador de inicialização externo será relevante, a menos que você não queira alterar a configuração, que é totalmente suportada.
Nesse sentido, o código nas configurações de teste para 1C82 e 1C83 é ligeiramente alterado, embora as diferenças entre eles não sejam mais fundamentais.
Ao mesmo tempo, nossa observação de que a empresa 1C pode bloquear facilmente a execução do código de inicialização em qualquer VK, pelo menos para componentes externos como a
API nativa , obviamente permanece válida, porque, a julgar pelo modelo, isso não é necessário. Para um
COM do tipo VK
, existe uma necessidade até agora, mas o que impede que ele se livre? Ao mesmo tempo, vamos ver se eles levarão essas informações em consideração?