25 de junho de 2009

Comunicação entre processos com C++ Builder: File Mapping

Hoje em dia, é muito difícil que um programa seja autosuficiente, isto é, que suas funcionalidades sejam completamente isoladas de outros programas. Por exemplo, se um programa usa banco de dados, certamente ele precisará de um gerenciador para esse banco (SQL Server, Oracle, etc.) ou no mínimo um outro pedaço de software (OleDB, ODBC) para poder se comunicar com o banco.

Há uma porção de formas de fazer dois programas se comunicarem, desde a simples troca de mensagens nativas do sistema operacional (ex: SendMessage, no Windows), passando por componentes (tecnologia COM, como no caso do OleDB) e chegando mais recentemente nos WebServices.

Escolher qual das tecnologias melhor se aplica depende do cenário que se tem. Se ambos os programas se encontram em execução no mesmo computador ou não, se você tem acesso ao código fonte de ambos, etc..

Na ABC71 temos a seguinte situação: o programa principal do ERP apenas carrega e exibe o painel de navegação apropriado para cada usuário. Quando o usuário seleciona uma atividade, um novo programa entra em execução e fica ativo até que o usuário encerre a atividade. O programa principal, então, fica desimpedido para que o usuário possa ativar outra tarefa simultaneamente.

Neste cenário, como estamos no mesmo computador e temos os códigos fontes de ambos os programas, a melhor solução que encontramos foi o File Mapping. File Mapping é um recurso que permite mapear estruturas em memória e compatilhar o acesso a essas estruturas entre processos.

Para trabalhar com File Mapping, o primeiro passo é criar um handle para esse recurso. O código do exemplo abaixo em C++ foi extraído do nosso programa principal, que controla o mapeamento. O mesmo conceito pode ser usado em Delphi :
HANDLE FFileMappingHandle;
FFileMappingHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(TWMyStruct), "Omega@");
if ( FFileMappingHandle && (GetLastError() == ERROR_ALREADY_EXISTS) )
{
CloseHandle(FFileMappingHandle);
FFileMappingHandle = NULL;
}
Na chamada à função do Windows CreateFileMapping, o valor INVALID_HANDLE_VALUE no primeiro parâmetro faz com que seja usado o arquivo de paginação de memória do Windows para criar o mapeamento. O valor PAGE_READWRITE dá acesso a leitura e gravação na memória compartilhada. O tamanho da memória a ser usada é estipulado pelo sizeof(TWMyStruct), que é o tamanho da minha estrutura. Veja na documentação de CreateFileMapping que o tamanho da memória é determinado pela combinação dos valores de 2 parâmetros e o código mostrado aqui só é valido porque o tamanho de minha estrutura cabe num DWORD. O último parâmetro é o nome que dei ao mapeamento; é através desse nome que o programa auxiliar consegue enxergar a memória compartilhada.

O próximo passo é criar uma "visão" dessa memória. Parece complicado mas na verdade, é um conceito bastante simples. Como a memória para minha estrutura foi alocada pelo próprio CreateFileMapping, criar uma visão é apenas obter o endereço dessa memória e atribuir a uma variável que representa a estrutura. Para isso, usamos a função MapViewOfFile do Windows:
TWMyStruct *FMyStrc;
FMyStrc = (TWMyStruct *)MapViewOfFile(FOmegaMapHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0);
Desse ponto em diante, todo conteúdo que for gravado em FMyStrc será compartilhado por qualquer programa que abrir o FileMapping chamado "Omega@".

Quando não for mais usar essa memória, encerre a "visão" e o mapeamento:
if ( FMyStrc )
UnmapViewOfFile(FMyStrc);
if ( FOmegaMapHandle )
CloseHandle(FOmegaMapHandle);

Até aqui, esses procedimentos dizem respeito ao programa principal, que cria o mapeamento. E quanto ao(s) outro(s) programa(s) que também fará(ão) uso dessa memória? Praticamente a mesma coisa ... A única diferença é que o File Mapping já está criado, então basta abrí-lo através do OpenFileMapping ao invés de usar CreateFileMapping.
FOmegaMapHandle = OpenFileMapping(FILE_MAP_ALL_ACCESS, true, "Omega@");

Com isso, consigo fazer com que o programa principal envie ao programa auxiliar as informações necessárias para iniciar a tarefa solicitada pelo usuário. Quando o programa auxiliar se encerra, ele também consegue sinalizar isso ao programa principal e retornar a ele dados relativos à tarefa executada.

Nenhum comentário :

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.