19 de julho de 2010

Integrando programas Delphi com o Excel - parte I

Já mostrei aqui no Blog quais são as principais classes disponíveis para se automatizar tarefas envolvendo o Excel, além de exemplos em VBScript de como usar essas classes para ler dados de uma planilha ou criar e formatar planilhas. Mas, como realizar essas mesmas tarefas diretamente em um programa feito em Delphi ? Esta questão é pertinente se você já tem programas Delphi pois, para o usuário, é mais prático (e elegante) manipular dados que já se tenha à mão. Caso contrário, o usuário terá que sair do programa para executar um script externo.

Também já abordei em outras ocasiões que o Delphi é capaz de gerar códigos fontes para permitir que um programa trabalhe com classes COM publicadas por qualquer aplicativo e que o Excel é um aplicativo que publica suas funções com essa tecnologia. Mas, para facilitar, o Delphi traz uma paleta de componentes chamada Servers que já traz encapsuladas as classes do Office (Word, Excel, Outlook, etc.), poupando-nos o trabalho de gerá-las manualmente.

No entanto, esses componentes foram criados de uma forma na qual não é possível determinar visualmente a relação precisa entre eles, exigindo do programador um conhecimento prévio dessas relações para fazê-las funcionar corretamente. No caso do Excel, o ponto de entrada para a integração é a classe Application, representada pelo componente TExcelApplication. Outros dois componentes importantes são TExcelWorkbook (uma planilha) e TExcelWorksheet (uma folha ou tabela dentro da planilha). Portanto, construir um programa Delphi que crie uma planilha exigirá que incluamos os três em um Form:
TForm1 = class(TForm)
_AppExcel: TExcelApplication;
_Book: TExcelWorkbook;
_Sheet: TExcelWorksheet;
{ ... }
Os 3 componentes possuem poucas propriedades publicadas: AutoConnect indica se a conexão com o servidor COM (neste caso, o Excel) será ou não feita automaticamente – o padrão é que o programa estabeleça a conexão somente quando for precisar do servidor, ficando mais fácil de tratar eventuais erros de conexão, principalmente se o servidor não existir (Excel não está instalado).
ConnectKind estabelece como será feita a conexão do componente com o Excel (servidor COM), sendo padrão o valor ckRunningOrNew. Com esse valor, a conexão é feita com um Excel que já esteja em execução. Se não houver nenhum, o programa o executa automaticamente antes de se conectar. Para esse exemplo, vou manter as propriedades desses componentes com seus valores padrões.

O segredo para utilizar esses componentes é conectá-los corretamente entre si, preparando o ambiente em que eles atuarão. Basicamente, o código para preparar o ambiente é o seguinte:
procedure TForm1.InitExcel;
var lExcelWorkBook : _WorkBook;
lSheetInterface : IDispatch;
begin
try
_AppExcel.Connect;
_AppExcel.DisplayAlerts[[0] := false;

lExcelWorkBook := _AppExcel.Workbooks.Add(xlWBATWorksheet, 0);
_Book.ConnectTo(lExcelWorkBook);

lSheetInterface := _Book.Worksheets.Item[1];
_Sheet.ConnectTo(lSheetInterface As _Worksheet);
_Sheet.Name := 'Minha Planilha';
except
on Exc: Exception do
_Erro := 'Não foi possível iniciar o Excel.'#13''#10'' + Exc.Message;
end;
end;
O que acontece no código acima é o seguinte :
Como o ponto de partida para a integração é o componente TExcelApplication, primeiro conecto-o ao servidor COM, isto é, ao próprio Excel. O componente "sabe" quem é o servidor correto porque essa informação está embutida nele, bastando que se chame o método Connect.
Por achar mais apropriado, configuro a propriedade DisplayAlerts para evitar que o Excel mostre suas caixas de diálogo pedindo confirmações ao usuário do meu programa.
Uso a propriedade Workbooks para criar uma nova planilha vazia, o que é feito através do método Add. O valor retornado pelo Add representa a planilha (Workbook) que foi adicionada.
O valor representando a nova planilha é recuperado num variável e, em seguida, usado para conectar ao componente TExcelWorkbook que agora representa exatamente a mesma planilha, podendo ser usado para manipulá-la ao longo do programa.
Um Workbook é composto de uma ou mais folhas de trabalho, que é quem mantem as células, seus valores, fórmulas, formatações, etc. Por isso, o passo seguinte é recuperar a primeira dessas folhas e conectá-la ao componente TExcelWorksheet, disponibilizando as informações contidas.
A última operação renomeia a folha de trabalho, ajustando-lhe o título para 'Minha Planilha'.

Essa é a "receita de bolo" ! Agora, basta utilizar esses 3 componentes para alimentar os dados numa planilha, formatá-la, etc. Quando tiver terminado as operações com a planilha e com o Excel, é importante fazer a desconexão e encerrar a aplicação para devolver ao Windows os recursos utilizados:
var vip: Variant;
begin
_Book.SaveAs ('Planilha.xls', xlExcel7, vip, vip, false, false, xlNoChange, false, false, vip, vip, vip, 0);
_Sheet.Disconnect ();
_Book.Disconnect ();
_AppExcel.Quit ();
_AppExcel.Disconnect ();
A primeira parte do código anterior salva a nova planilha com o nome de Planilha.xls. Repare ainda o uso da variável chamada vip, do tipo Variant : ela não é iniciada com valor algum. Desse modo, os parâmetros não-obrigatórios da função SaveAs são forçados a assumir seus valores padrões, livrando-nos de ter que providenciá-los.

Volto em outro post para mostrar como trabalhar com uma célula específica ou uma faixa de células, seus valores, formatações e fórmulas de dentro de um programa Delphi.

11 comentários :

Anônimo disse...

Muito bom esse Artigo.

Me ajudou um monte.

Parabéns!!

Anônimo disse...

Como usar uma instancia do excel já aberta? conectei com o ConnectKind mas não consigo inserir planilhas...

Luís Gustavo Fabbro disse...

O correto é mesmo usar o ConnectKind com ckRunningInstance ou com ckRunningOrNew para que seu programa se conecte ao Excel que já está aberto. O que acontece qdo vc tenta incluir uma nova planilha? Há mensagens de erro? A planilha é nova, criada do zero, ou vc está tentando abrir uma pré existente ?

[]s

Anônimo disse...

É possivel usar Sem o Excel estar instalado?

Luís Gustavo Fabbro disse...

Os procedimentos descritos no post usam recursos do próprio Excel de modo que ele tem mesmo que estar instalado para funcionar. Pode ser que haja alguma biblioteca gratuita no SourceForge que crie planilhas no formato do Excel sem necessidade de instalá-lo.

[]s

Anônimo disse...

agradeço. Vou verificar...

Anônimo disse...

Boa noite, Luís!

Tenho acompanhado seu blog e os seus artigos com muito interesse e admiração. Seus textos são bem escritos e muito explicativos, o que os torna uma importante fonte de consulta e aprendizado.
Aproveitando sua boa vontade e seus sólidos conhecimentos, gostaria que me orientasse ou sugerisse no seguinte problema:
Tenho alguns arquivos em html cujo conteúdo consiste num mix de texto e uma tabela com uma série histórica de alguns anos (2003 a 2011) e algumas linhas. Preciso ler apenas a tabela, extraindo dela os valores das celulas da coluna correspondente ao último ano da série e também a coluna correspondente aos ítens da linha.

Você teria alguma sugestão de como eu fazer isso?

Desde já eu agradeço a sua atenção e colaboração.

Um abraço, parabéns e muito sucesso!

[]s
Vandinei
vandinei@oi.com.br

Luís Gustavo Fabbro disse...

Vandinei

Como HTML é um XML (com um conjunto de tags pré definido), você pode carregar o conteúdo do HTML numa classe capaz de interpretar XML (ex: TDomDocument) e navegar normalmente pela hierarquia obtida, procurando pelas tags que te interessarem.

[]s

Anônimo disse...

Luís, muito bom seu artigo, mas com este exemplo, não estou conseguindo criar mais de uma planilha... Poderia me ajudar? Grato!

Luís Gustavo Fabbro disse...

Que tipo de problema ocorre? Há alguma mensagem de erro?

O exemplo mostra o uso de apenas uma planilha por vez mas você pode trabalhar com outras criando instâncias separadas de workbook (a planilha em si) e de sheet (uma folha dentro da planilha).

[]s

Anônimo disse...

Criei outras instâncias de workbook e sheet e funcionou! Obrigado!

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.