Aplicações Externas no AdvPL – Parte 02

Introdução

No tópico anterior, vimos um exemplo de chamada de aplicação externa ao AdvPL via Smartclient, usando uma DLL. As mesmas regras da DLL valem para criar uma biblioteca de objetos compartilhados (Shared Object), para ser usada com o SmartClient Linux. Neste tópico, antes de ver alguns recursos mais avançados de DLL, vamos ver alguns comandos para chamar diretamente uma aplicação externa executável através do SmartClient.

Para a execução de aplicativos na máquina onde está sendo executado o SmartClient, o AdvPL disponibiliza três funções: WinExec(), WaitRun() e ShellExecute(). Cada uma delas possui parâmetros, atributos e comportamentos distintos, aplicáveis a diversas situações. Os detalhes de cada função estão documentados na TDN, nos links disponibilizados nas referências deste post. Neste tópico, o foco vai ser na utilidade, pontos comuns e diferenças de cada uma.

Função WinExec

Dispara a execução de uma aplicação externa ao SmartClient, sem aguardar por retorno ou finalização. Pode ser chamado qualquer aplicação na máquina, sem informar o PATH completo do arquivo, desde que a aplicação esteja em algum diretório especificado na variável de ambiente PATH da máquina onde está sendo executado o SmartClient.

Função WaitRun

Permite a execução de uma aplicação externa ao SmartClient, aguardando pelo término da aplicação. Esta particularidade pode não ser respeitada quando o SmartClient está sendo executado em uma máquina com Windows 10, por exemplo, devido a mudanças entre as versões do sistema operacional. Da mesma forma que a WinExec, podemos ou não informar o PATH completo da aplicação a ser executada, sendo possível omitir o caminho completo da aplicação caso ela esteja na variável de ambiente PATH da máquina onde o SmartClient está sendo executado. Adicionalmente, na função WaitRun(), pode ser especificado um parâmetro para a execução de uma aplicação sem que seja aberta uma janela de interface, ou pode ser iniciada a aplicação em uma janela minimizada.

Função ShellExecute

De modo similar a WinExec, ela apenas dispara uma aplicação. Porém, ela permite vários parâmetros adicionais, e inclusive permite outras operações relacionadas a arquivos, URLs, documentos e afins, onde o sistema operacional pode utilizar diretamente a aplicação default para uma determinada ação. Por exemplo, podemos solicitar a abertura de uma URL de um WebSite, informando o endereço HTTP, e o sistema operacional vai utilizar para esta ação o Browser de Internet default configurado para abrir endereços HTTP. A função ShellExecute não espera pelo término da aplicação iniciada.

Exemplo 01

Um cliente possui um Web Site de venda de seus produtos, onde existe uma página de consulta por código, que pode receber como parâmetro o código do produto desejado pela URL. E, no seu cadastro de produtos no ERP, ele têm o código do produto disponibilizado no site. Ele gostaria de criar uma função em AdvPL, que recebesse um código WEB do Produto, e abrisse uma página do navegador, na estação do usuário do Smartclient, trazendo na tela a consulta do produto no site.

A alternativa mais elegante é usar a função ShellExecute(), montando a URL de acesso usando o código fornecido, e deixando o sistema operacional abrir a página do site no Navegador de Internet padrão da máquina do usuário. Veja o exemplo abaixo:

#include 'shell.ch'

User Function ConsWeb(cCodWeb)
Local cUrl := 'http://meusite.com.br/"
Local cPage := 'consprod.php?cod='+cCodWeb

ShellExecute('open',cUrl+cPage,"","",SW_NORMAL)

Return

Exemplo 02

Vamos partir do primeiro exemplo, porém eu quero que a URL em questão sempre seja aberta pelo Google Chrome, mesmo que ele não seja o navegador default. Para isso, basta endereçarmos a execução do “Chrome.EXE”, e passamos como parâmetro a URL a ser aberta. nem preciso dizer que este é um exemplo didático, e que o sucesso dessa abordagem exige que a máquina onde está sendo executado o SmartClient tenha o Google Chrome instalado.

#include 'shell.ch'

User Function ConsWeb2(cCodWeb)
Local cUrl := 'http://meusite.com.br/"
Local cPage := 'consprod.php?cod='+cCodWeb

ShellExecute('open','chrome.exe',cUrl+cPage,"",SW_NORMAL)

Return

Exemplo 03

Precisamos executar um comando qualquer do sistema operacional, ou um aplicativo de linha de comando, onde não há passagem de parâmetros por interface, apenas por linha de comando, e o resultado deste comando é uma saída de texto em tela. Por exemplo, um comando “dir”, ou um “ifconfig” — para obter detalhes das interfaces de rede. Normalmente, quando executamos um comando assim pelo prompt de comando do sistema operacional, podemos direcionar a saída de tela para um arquivo em disco, colocando no final do comando um sinal de maior “>” , seguido do nome de um arquivo, que será criado em disco na pasta atual.

Porém, este direcionamento em arquivo é tratado apenas pelo interpretador de comandos, isto é, não funciona se você tentar usar em um Winexec(), WaitRun() ou ShellExecute(). Mas não precisa entrar em pânico, existe uma alternativa: Basta montar uma chamada direta para o interpretador de comandos (CMD.EXE), passando o comando original para ser executado através do interpretador de comandos.

Por exemplo, queremos executar um “ifconfig” na máquina onde está o SmartClient, para obter detalhes das interfaces de rede disponíveis. E, como não há como recuperar a string retornada direto pela chamada, fazemos uma saída em arquivo.

ifconfig > netconfig.txt

Porém, para executar esta instrução, e ela realmente gerar o arquivo em disco com o resultado, encapsulamos a chamada com o CMD.EXE, ficando assim:

cmd /c "ifconfig > netconfig.txt"

Agora, vamos ao exemplo prático, para recuperar as informações de configuração de rede da máquina do SmartClient. Caso a função não consiga recuperar as informações por qualquer razão, ela retorna uma string em branco. Assumimos que o arquivo “netconfig.txt” será gerado na pasta de trabalho do executável do SmartClient, então usamos a função GETREMOTEININAME() para descobrir o path completo do SmartClient, para copiar o arquivo gerado e copiar para uma pasta no APPServer, ou mesmo ler o arquivo diretamente usando o AdvPL, e posteriormente removendo o arquivo do disco.

User Function GetNetCfg()
Local cExec := 'cmd /c "ifconfig > netconfig.txt"'
Local cRet := ''
Local cRmtPath

// Identifica o PAth do SmartClient na maquina remota 
// a partir do path do arquivo smartclient.ini
cRmtPath := GETREMOTEININAME()
cRmtPath := left(cRmtPath,rat('\',cRmtPath))

// Executa o comando para chamar o ifconfig 
// e gerar o arquivo "netconfig.txt"
WaitRun(cExec,0)

If file(cRmtPath+'netconfig.txt')
 // O arquivo foi gerado, lê direto com MEMOREAD
 // E remove o arquivo do disco 
 cRet := memoread(cRmtPath+'netconfig.txt')
 ferase(cRmtPath+'netconfig.txt')
Endif

Return cRet

Outros Usos

É claro, existem limitações na passagem de parâmetros para uma aplicação executável ( Se eu não me engano a linha de comando total não pode ser maior que 255 bytes, e se a operação a ser executada é vital para garantir a continuidade do processo, garantir a execução com sucesso ou até descobrir onde houve falha na execução pode se tornar um problema. Para questões desta ordem, você pode fazer uma DLL para encapsular as chamadas e proteger com o que você achar necessário. Para execuções simples, que não precisam desse aparato todo, Shellexecute(), WaitRun() e WinExec() dão conta do recado.

Restrições

Quando voce usa WaitRun() ou WinExec(), se você fizer uma chamada direta a um comando de sistema operacional ou outro aplicativo de linha de comando, que tenha alguma parada de interface para solicitar entrada de dados ou confirmação de usuário, como não será aberta no terminal nenhuma janela de sistema operacional para dar um contexto de entrada para a aplicação, o prompt de comando vai ficar preso na memoria, até que o computador seja reiniciado ou o processo da aplicação seja derrubado da memória pelo Gerenciador de Tarefas do Windows.

Um exemplo clássico é criar um arquivo de lote (extensão “.bat”), e usar dentro dele uma instrução “pause”. Pronto, rodou isso com WaitRun(), seu SmartClient fica esperando pra sempre uma aplicação externa que não vai voltar nunca. Se você usar WinExec(), o programa AdvPL segue a vida, mas o processo iniciado na máquina remota para rodar o arquivo de lote fica preso na memória, até ser derrubado ou o usuário do computador fazer um logoff do sistema operacional ou reiniciar o equipamento. Se você executar isso pelo ShellExecute(), uma janela do interpretador de comandos será aberta, e você terá visão e interação com a aplicação em lote sendo executada.

Existem aplicações como o 7Zip, WinZip, WINSCP e afins, que embora sejam aplicações com interface gráfica, elas podem ser chamadas em modo ‘batch’, isto é, por linha de comando, sem interface. Com isso, por exemplo, você pode usar uma aplicação externa para transferir um arquivo para um servidor, gerar um pacote compactado de arquivos para enviar ao servidor, usar aplicações externas para fins específicos de integração com outras interfaces ou mesmo dispositivos.

Conclusão

Com estes três comandos, já dá pra fazer muita coisa. O modelo de chamada de aplicação externa é muito interessante, pois não compartilha a mesma área de memória do SmartClient, a aplicação é executada diretamente pelo sistema operacional, e nada impede de você criar a sua aplicação externa para atender a sua necessidade. Quando a integração envolve a chamada de diversas funções ou diversas vezes a mesma aplicação em curtos intervalos de tempo, pode ser mais vantajoso gastar um pouco mais de tempo e fazer a integração com DLL.

Espero que este post ajude a todos que desenvolvem em AdvPL a facilitar o processo de integração com outros sistemas, e desejo novamente a todos TERABYTES de SUCESSO 😀

Referências

http://tdn.totvs.com/display/tec/WinExec
http://tdn.totvs.com/display/tec/WaitRun
http://tdn.totvs.com/display/tec/ShellExecute

Aplicações Externas no AdvPL – Parte 01

Introdução

A linguagem AdvPL permite a execução de aplicações externas. E este recurso é muito interessante quando precisamos fazer automação de processos e integrações com sistemas legados ou aplicações externas ao TOTVS Application Server. Nesta série de tópicos, vamos abordar o que o AdvPL nos oferece para realizar estas tarefas, e para abrir esse assunto, este tópico têm um exemplo funcional de DLL para ser usada no SmartClient, que permite capturar a tela da máquina que está executando o SmartClient.

Visão Geral de Aplicações Externas

Podemos definir como “aplicações externas”, qualquer programa executável, com ou sem interface, criado para uma determinada finalidade, que não pertence ao conjunto de ferramentas do Protheus.

Existem inúmeras aplicações externas que o Protheus realiza algum tipo de integração. Por exemplo, um servidor de e-mail ou de FTP. É uma aplicação externa, que pode ser acessada através de uma conexão TCP, onde já existe uma função ou classe em AdvPL para encapsular as funcionalidades desta aplicação, como as classes de FTP e e-MAIL.

Normalmente muitas aplicações externas são sistemas complexos, com múltiplas funcionalidades. Algumas delas oferecem APIS de acesso via rede TCP/IP, mediante troca de mensagens em formato específico ou proprietário, ou usando XML-SOAP sobre HTTP, REST sobre HTTP, etc.

Aplicações Específicas ou Especialistas

Existem alguns tipos de aplicações, normalmente menores e criadas para tarefas especializadas, que não oferecem uma camada de acesso remoto, ou mesmo alguma API, mas sim apenas uma DLL ou um executável sem interface, onde devemos realizar a chamada da operação desejada através do endereço de uma função publicada na DLL, ou mesmo através da linha de comando do sistema operacional. Nesta abordagem de aplicações externas, vamos focar inicialmente nestes casos.

Recursos do AdvPL

Usando determinadas funções do AdvPL, podemos realizar a carga de uma DLL, e a chamada de uma função publicada nesta DLL, desde que a mesma atenda a especificação de parâmetros estabelecida, e podemos chamar programas externos (executáveis ou mesmo comandos do sistema operacional).

Porém, devido a uma (importante) questão de segurança e isolamento, ñenhum destes recursos está disponivel para carga de dlls ou execução de programas na instância do TOTVS | Application Server (serve-side). Elas foram feitas para trabalhar em conjunto com o TOTVS SmartClient.

Desse modo, a sua aplicação AdvPL precisa ser iniciada a partir de um SmarrtClient, para a partir dele carregar uma DLL ou chamar um programa executável disponivel na máquina onde o Smartclient está sendo executado. Deste modo, nenhum processo executado em JOB dentro do Protheus Server consegue carregar uma DLL no servidor, ou iniciar um aplicativo executável externo.

SmartClient – Carga de DLL

Existem 5 (cinco) funções no AdvPL que lidam com a carga e execução de funções em DLL, a seguir :

ExecInDLLOpen() – Carrega a DLL na área de memória do SmartClient
ExecInDLLRun() – Executa uma função pré-definida na DLL carregada
ExeDLLRun2() – Executa uma função pré-definida na DLL carregada
ExeDLLRun3() – Executa uma função pré-definida e diferenciada na DLL carregada
ExecInDLLClose()- Descarrega a DLL da memória do SmartClient

A DLL criada deve conter uma (e apenas uma) das prototipagens abaixo, para ser chamada pelo AdvPL:

extern “C” __declspec(dllexport) void ExecInClientDLL(int , char * , char * , int )
extern “C” __declspec(dllexport) int ExecInClientDLL(int , char * , int , char * , int )

Usamos a primeira prototipagem para realizar chamadas pelas funções AdvPL ExecInDLLRun() e ExeDLLRun2(), e a segunda pode ser chamada apenas pela ExeDLLRun3(). Devemos escolher apenas uma prototipagem a ser usada. Não é possível usar as duas na mesma DLL. Você pode criar uma DLL usando C ou C++, ou Delphi, ou outra linguagem que permita a declaração da função na prototipagem adequada. Quando precisamos encapsular um ou mais funcionalidades dentro de um projeto que dependem de DLLs de terceiros, podemos encapsular a DLL de terceiros, construindo um projeto de uma DLL que faça link dinâmico com a DLL de terceiros a ser encapsulada, fazendo a camada de chamadas dentro da função prototipada.

Uma aplicação externa em uma DLL pode abrir uma interface própria, sobre a interface do SmartClient, ou pode ser usada simplesmente para acessar um dispositivo externo ou realizar qualquer outra tarefa sem interface. Existem limitações sobre os parâmetros e retorno da chamada, estes limites variam de acordo com a função AdvPL utilizada para realizar a chamada.

Parâmetros e Retornos

Basicamente, todas as prototipagens permitem a passagem simultânea de um parâmetro numérico inteiro e uma string, e um retorno de uma string. A diferença entre elas está no tamanho dos parâmetros, e a última prototipagem permite também o retorno simultâneo para o AdvPL de um valor numérico e uma string. A documentação da TDN aborda em detalhes o funcionamento esperado de cada uma, vamos focar aqui em um exemplo prático.

DLL de Captura de Tela

Para mostrar como a carga e execução de uma DLL no TOTVS SmartClient funciona, vamos começar com um projeto em C++, que pode ser compilado a partir do Visual Studio 2005 e versões superiores. Basta criar um “Empty Project” em C++ no Visual Studio, definir que o projeto será uma DLL, e criar o fonte de cabeçalho “GetScreen.hpp” e “GetScreen.cpp”, com os conteúdos abaixo:

Arquivo [getscreen.hpp]

void DoCapture( char * file , char * result );
void ScreenCapture(int x, int y, int width, int height, char *filename, char * result );

Arquivo [getscreen.cpp]

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <windows.h>
#include <gdiplus.h>
#include <memory>

#include "GetScreen.hpp"

/* ------------------------------------------------------------
Programa GetScreen
Autor Júlio Wittwer
Data 07/09/2016
Descrição DLL Win32 para uso com o TOTVS SmartClient
 Permite a captura da tela do computador onde o SmartClient está sendo executado
 gerada como uma imagem .JPEG salva em disco, usando o nome do arquivo fornecido 
 como parâmetro.

Utilização 

 Utilizar a função ExecInClientDLL(), informando os seguintes parâmetros: 

 int ID => Número da operação desejada 
 0 = Obter a versão da API 
 1 = Capturar um ScreenShot em formato JPEG

 char * BufIn => array de char contendo um parâmetro para a opção.
 SE ID = 0, o parâmetro é ignorado e pode ser NULL
 SE ID = 1, deve ser informado o nome do arquivo JPEG no qual a 
 captura de tela deve ser gravada. 

 char * BufOut => Array de char contendo o resultado da chamada, no formato 
 NNN XXXXXXXX, onde NNN é um Código de Status , e XXXXX contém uma informação 
 adicional sobre o status retornado. Em caso de sucesso, o número retornado é "000"
 Qualquer outro número indica uma condição de falha. 

 "001 Encode size failed"
 "002 Memory allocation failed"
 "003 Image Codec not found"
 "004 Image Save Error (%d)"
 "005 Unexpected Error %d
 "010 Unknow Command"

------------------------------------------------------------ */


extern "C" __declspec(dllexport) void ExecInClientDLL( int ID, char * BufIn, char * BufOut, int nSizeOut )
{

 if( ID == 0 )
 {
 // Retorna a versão da DLL de captura
 strcpy(BufOut,"000 GetScreen V 0.160911");
 }
 else if (ID == 1)
 {
 // REaliza a captura da tela
 // Recebe em BuffIn o nome do arquivo a ser salvo 
 // Retona em buffOut o status da operação 
 // Em caso de sucesso, retorno "000 Ok"
 // Em caso de erro, retorno "NNN <error message>"
 DoCapture(BufIn,BufOut);
 }
 else
 {
 // ID não conhecido/inválido 
 strcpy(BufOut,"010 Unknow Command");
 }
}

using namespace Gdiplus;
using namespace std;

// Inicializa GDI para a captura de vídeo
// faz a captura, salva em disco, e finaliza GDI

void DoCapture( char * file , char * result ) 
{

 // Initialize GDI+.
 GdiplusStartupInput gdiplusStartupInput;
 ULONG_PTR gdiplusToken;

 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

 int x1 = 0;
 int y1 = 0;
 int x2 = GetSystemMetrics(SM_CXSCREEN);
 int y2 = GetSystemMetrics(SM_CYSCREEN);

 // Realiza a captura da tela e salva em arquivo 
 ScreenCapture(x1, y1, x2 - x1, y2 - y1, file , result );

 // Shutdown GDI+
 GdiplusShutdown(gdiplusToken);

}


// Retorna o ponteiro do encoder adequado para fazer
// a conversão do BITMAP em memória para o formato desejado


int GetEncoderClsid(const WCHAR* format, CLSID* pClsid )
{
 UINT num = 0; // number of image encoders
 UINT size = 0; // size of the image encoder array in bytes

 ImageCodecInfo* pImageCodecInfo = NULL;

 GetImageEncodersSize(&num, &size);

 if(size == 0)
 {
 // Encode Size Failure
 return -1; 
 }

 pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
 if(pImageCodecInfo == NULL)
 {
 // Memory allocation failure
 return -2; 
 }

 GetImageEncoders(num, size, pImageCodecInfo);

 for(UINT j = 0; j < num; ++j)
 {
 if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
 {
 *pClsid = pImageCodecInfo[j].Clsid;
 free(pImageCodecInfo);
 // Success
 return j; 
 } 
 }

 free(pImageCodecInfo);

 // Image Codec not found
 return -3; 
}

// Encapsula a gravação do BITMAP capturado da tela
// para o formato JPEG em disco 

void BitmapToJpg(HBITMAP hbmpImage, char *filename , char * result )
{

 Status eRC = Ok;

 Bitmap * p_bmp = Bitmap::FromHBITMAP(hbmpImage, NULL);

 CLSID pngClsid;

 int RC = GetEncoderClsid(L"image/jpeg", &pngClsid);

 if( RC >= 0 )
 {
 const size_t cSize = strlen(filename)+1;
 std::wstring wc( cSize, L'#' );
 mbstowcs( &wc[0], filename, cSize );
 eRC = p_bmp->Save(&wc[0], &pngClsid, NULL);
 if ( eRC != Ok)
 RC = -4;
 }

 delete p_bmp;

 if ( RC == -1 )
 sprintf_s(result,255,"001 Encode size failed");
 else if ( RC == -2 )
 sprintf_s(result,255,"002 Memory allocation failed");
 else if ( RC == -3 )
 sprintf_s(result,255,"003 Image Codec not found");
 else if ( RC == -4 )
 sprintf_s(result,255,"004 Image Save Error (%d)",eRC);
 else if ( RC < 0 )
 sprintf_s(result,255,"005 Unexpected Error %d",RC);
 else
 sprintf_s(result,255,"000 Ok");

}


// Funão de captura / snapshot de tela
// Requer o ambiente DGI previamente inicializado

void ScreenCapture(int x, int y, int width, int height, char *filename, char * result )
{
 HDC hDcMemory = CreateCompatibleDC(0);
 HDC hDcScreen = GetDC(0);
 HBITMAP hBmp = CreateCompatibleBitmap(hDcScreen, width, height);

 SelectObject(hDcMemory, hBmp);
 BitBlt(hDcMemory, 0, 0, width, height, hDcScreen, x, y, SRCCOPY);

 // Converte a tela capturada em JPEG e salva em disco 
 BitmapToJpg(hBmp, filename , result );

 // Cleanup
 DeleteObject(hBmp);
 DeleteObject(hDcMemory);
 ReleaseDC(NULL,hDcScreen);
}

Feito isso, basta fazer a “build do Projeto, gerando uma DLL 32 bits, e copiá-la para a pasta do TOTVS SmartClient.

Agora, crie um fonte AdvPL, chamado por exemplo de “GetScreen.PRW”, adicione-o a um projeto no IDE/TDS, com o seguinte conteúdo:

Arquivo [GetScreen.PRW]

/* ------------------------------------------------------------
Funcao U_GetSCR()
Autor Júlio Wittwer
Data 09/2016
Descrição Tira um ScreenShot da tela da máquina onde está 
 sendo executado o TOTVS | SmartClient, e copia 
 o JPEG com a imagem gerada para uma pasta 
 chamada "\images\" a partir do RootPath do 
 ambiente no Servidor
Observação A Pasta \images\ a partir do RootPath deve ser 
 criada antes de executar este programa

------------------------------------------------------------ */

User function GetSCR()

Local hHdl := 0
Local cRet
Local nCode
Local cRmtFile
Local cSrvFile 
Local cImgFile

// Abre a DlL
hHdl := ExecInDLLOpen( "GetScreen.dll" )

IF hHdl == -1 
 UserException("Failed to load [GetScreen.dll]")
Endif

// Pega a versao do GetScreen
cRet := ExecInDllRun( hHdl, 0, "" )
conout(cRet)

// identifica a pasta temporaria da maqina 
// que está rodando o smartclient 
// e usa ela pra criar a imagem do screenshot

cImgFile := 'img_'+cValTochar(Threadid())+'.jpeg'
cRmtFile := GETTEMPPATH() + cImgFile;

Msgrun( "Aguarde",;
 "Executando captura de tela",;
 {|| cRet := ExecInDllRun( hHdl, 1, cRmtFile )} )


If empty(cRet)
 UserException("Unexpected Empty result from [GetScreen.dll]")
Endif

// Verifica o codigo retornado
// 000 = Sucesso 
// <> 0 = Falha

nCode := val(left(cRet,3))
If nCode > 0 
 UserException(cRet)
Endif

// copia o arquivo do terminal para o servidor
CPYT2S(cRmtFile,"\images\")

// Apaga o arquivo na pasta temporária da estação 
Ferase(cRmtFile)

// Informa a operação realizada
MsgInfo("Imagem salva no servidor em \images\"+cImgFile)

// ----------------------------------------------------------------
// Fecha a DLL
ExecInDllClose( hHdl )

Return 

Execução

Após gerar a DLL 32 bits no Visual Studio, copiá-la para a pasta do SmartClient, e compilar o fonte AdvPL “GetScreen.PRW”, e criada uma pasta chamada “\images\” a partir do RootPath do seu ambiente do Protheus, basta iniciar um Totvs SmartClient, e executar o programa “U_GetSCR”. Ele mostrará rapidamente uma caixa de diálogo da função MsgRun(), a partir de onde será feita a captura da tela na estação onde está sendo executado o TOTVS SmartClient, e logo depois uma caixa de diálogo, informando o nome do arquivo que contém a imagem capturada na pasta \images\ no ambiente do Protheus Server.

O Fonte em AdvPL determina qual é o nome da pasta temporária da máquina onde o SmartClient está sendo executado, cria um nome para salvar a imagem, chama a DLL pra fazer a captura da imagem, informando o nome do arquivo onde a imagem deve ser salva, e depois apenas copia o arquivo gerado no disco da estação onde o SmartClient está sendo executado para a pasta “\images\” do Protheus Server, e apaga o arquivo temporário da estação.

Estendendo as funcionalidades

O modelo proposto permite que você implemente literalmente milhares de instruções dentro de uma DLL, mesmo tendo apenas uma função prototipada, pois podemos criar uma funcionalidade com tratamento diferenciado de parâmetros e retorno para cada nID utilizado. Usando o ID 0 (zero) para controlar a versão da API, quando você implementar uma funcionalidade nova nesta DLL, basta atualizar o número da versão, e no seu programa em Advpl, basta verificar qual é a versão em uso, para saber se ela já possui a funcionalidade desejada. Por exemplo, baseado neste programa, você pode implementar o ID=2 para pegar a quantidade de monitores que existe na máquina Client, ou a resolução atual de tela, ou qualquer outra coisa que lhe seja útil.

Fontes e DLL

Para você que quer compilar, testar e usar este recurso, ou estendê-lo, basta pegar os fontes no https://github.com/siga0984/Blog — todos os arquivos que começam com “GetScreen.*”. Inclusive, também disponibilizei o arquivo getscreen.dll, compilado para Windows 32 Bits. Basta copiar ele para a pasta do SmartClient, compilar o programa AdvPL GetScreen.PRW no repositório do ambiente Protheus e utilizá-lo. Segue abaixo links do GitHub para cada um deles.

https://github.com/siga0984/Blog/blob/master/GetScreen.hpp
https://github.com/siga0984/Blog/blob/master/GetScreen.cpp
https://github.com/siga0984/Blog/blob/master/GetScreen.prw
https://github.com/siga0984/Blog/blob/master/GetScreen.dll

Conclusão

Esse é um exemplo bem arroz-com-feijão, apenas pra dar um gostinho do que podemos fazer com esta integração, e ainda nem chegamos na camada de chamar aplicações externas — que também é uma “mão na roda”. No próximo post, vou entrar um pouco mais em alguns aspectos e propriedades desta integração com DLL, que abrem um amplo leque de possibilidades !!!

Agradeço a todos(as) pela audiência, e lhes desejo TERABYTES de SUCESSO 😀

Informações Adicionais

RELEASE 2016/09/14 23:32 – Meus agradecimentos ao meu chapa Pedro Scarapicchia, por localizar e me apontar 3 pontos de leak na função ScreenCapture() 😀 Post e GitHub atualizados 😉 Pedro, obrigado por me lembrar das boas práticas — a pressa de colocar o post no ar foi maior que a atenção no código ..rs…

Referências

http://tdn.totvs.com/display/tec/ExecInDLLOpen
http://tdn.totvs.com/display/tec/ExecInDLLRun
http://tdn.totvs.com/display/tec/ExeDLLRun2
http://tdn.totvs.com/display/tec/ExeDLLRun3
http://tdn.totvs.com/display/tec/ExecInDLLClose