Excel e C#: Abrir uma planilha e ler células
Autor: _ivan | Publicado: 2011-07-18T16:46:00-03:00

Vou começar este post de um ponto um pouco avançado.
Vou fazer isso na verdade porque há outro post no blog que fala sobre criaçao de planilhas do Excel.
Então, caso não entendas alguma parte, sugiro ver o post Criar planilha do Excel em C#, antes de perguntar quaisquer coisas. Se ainda assim houverem dúvidas, é só comentar.


A ideia hoje é fazer duas coisas basicamente. A primeira está descrita no título: abrir um planilha (e possivelmente ler alguma informação). A segunda é fazer algumas observações importantes sobre a leitura de células no Excel.

Vamos começar pelo código para abrir uma planilha.
Quero deixar claro que para que o código abaixo funcione é preciso primeiro adicionar a biblioteca do Microsoft Office Excel às referências. Não é o foco deste post descrever esta parte passo a passo, então, caso não saibas como fazer, sugiro olhar o outro post que comentei ali em cima.
Vamos ao que interessa.

Para abrir uma planilha é preciso primeiramente iniciar o Excel, criando uma nova instância. Depois basta usar o método Open da classe Workbooks de dentro da classe Application.
O quê? Oo
=] 
Melhor mostrar no código.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Excel = Microsoft.Office.Interop.Excel;

namespace TesteExcel

{
&nbsp;&nbsp;&nbsp; public partial class Form1 : Form
&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private Excel.Application excelApp = null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private Excel.Workbook arquivoDeTrabalho = null;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private Excel.Worksheet planilha = null;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Form1()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InitializeComponent();

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string celula = "";
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List lista = new List();








&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*Iniciando o Excel e desligando os alertas.*/

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; excelApp = new Excel.Application();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; excelApp.DisplayAlerts = false;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*Abrindo arquivo.*/
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arquivoDeTrabalho = (Excel.Workbook)excelApp.Workbooks.Open(

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "C:\\ExemploTeste.xlsx", 0, true, 5, "", "",&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; true, Excel.XlPlatform.xlWindows, "\t", false,&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; false, 0, true, null, null);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*Selecionando a primeira planilha como planilha de trabalho.*/
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; planilha = (Excel.Worksheet)arquivoDeTrabalho.Sheets[1];
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /*Lendo os valores das 25 primeiras células e exibindo em uma MessageBox.*/
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int i = 1; i &lt; 6; i++)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (int j = 1; j &lt; 6; j++)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; celula = ((Excel.Range)planilha.Cells[i, j]).Value2.ToString();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (Exception e)

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; celula = "Erro: " + e.Message;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lista.Add(celula);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (string s in lista)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MessageBox.Show(s);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; arquivoDeTrabalho.Close(false, "", false);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; excelApp.Quit();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp; }
}




Mini nota 1: Observem que eu estou fazendo a leitura no próprio construtor da Form, na qual eu não coloquei componente algum. Não deem atenção a isso, foi apenas para exemplificar o uso do método Open.

Mini nota 2: Fiz a leitura dos valores nas 25 primeiras células somente como exemplificação para falar sobre o próximo tópico do post.

Super Mini Nota Gigante: Sobre os parâmetros do método Open. Como podem ver, há uma infinidade de parâmetros no método Open e eu não tenho certeza sobre todos eles, mas tentarei fazer algumas observações. O primeiro parâmetro é o caminho completo do arquivo e não cuidar isso pode acarretar problemas. O segundo parâmetro está relacionado com UpdateLinks e não tenho certeza exata do uso disso no momento. O terceiro parâmetro define se o arquivo é "somente leitura", que é o meu caso. O quarto está relacionado com o formato e o quinto e o sexto estão relacionados a senhas. O sétimo parâmetro é chamado IgnoreReadOnlyRecommended e o oitavo é chamado Origin (é o que está com Excel.XlPlatform.xlWindows, no meu caso). O nono parâmetro é o delimitador. O décimo define como editável e o décimo primeiro define notificações, sobre este último eu não tenho muita certeza a que tipo de notificações ele se refere. O décimo segundo é chamado Converter e o décimo terceiro é chamado AddToMru. Agora sobre os dois últimos parâmetros, eles são, seguindo a sequência, Local e CorruptLoad. Esses dois últimos parâmetros foram os únicos para os quais eu encontrei valores diferentes nos exemplos pela Internet. Eu usei null em ambos, mas havia um caso em que eram usados 1 e 0, respectivamente, como valores para estes parâmetros. Não fiz testes e nem encontrei muitas descrições sobre estes dois parâmestros, apenas estou fazendo uma observação sobre o que vi por aí, caso alguém se interesse em pesquisar mais. Ah, sim. O CorruptLoad pode ser útil em casos de arquivos corrompidos. Normalmente a tentativa de abrir um arquivo corrompido de alguma forma geraria uma exceção, mas às vezes é possível abri-lo mesmo havendo algum problema. Nesse caso o último parâmetro deve ser true ou 1, acredito eu, embora não tenha certeza por que é um object.
Uma última observação: todos os parâmetros são object, assim como na maioria dos métodos da biblioteca do Excel.




Sobre a leitura do valor de uma célula em uma planilha

Há duas observações importantes a serem feitas aqui.

Primeira: no exemplo ali em cima, quando faço a leitura, é possível ver que inicio os dois laços for em 1. Diferente da maioria das coleções em programação, os índices aqui não começam em 0, mas sim em 1. Na verdade eu acredito que os valores em 0 se refiram às células que contém as letras e os números que identificam as células no Excel, e por isso a forma de trabalho com elas seria diferente (se é que há uma forma). Digo isso porque ao tentar ler o valor de células com o índice 0 não era gerado um ArgumentOutOfRangeException, mas apenas um erro que me retornava Exceção de HRESULT: 0x800A03EC. De qualquer forma a informação importante aqui é iniciar os índices em 1, pois iniciando em 0 estaria apenas percorrendo células desnecessárias.

Segunda: é sempre importante ter estruturas try-catch em leituras de arquivos de Excel, a menos que seja um arquivo preparado de tal forma que se tenha certeza de que elas não serão necessárias. Por quê? Bom, basicamente, no Excel, só porque as células estão visíveis, não quer dizer que elas estejam instanciadas. Ou seja, na hora de percorrer várias células, a probabilidade de acabar passando por alguma referência nula é grande e então será gerada uma NullReferenceException, que deve ser corretamente tratada.
Outro problema é que frequentemente, mesmo que a célula esteja instanciada, o valor dela pode não estar, ou seja, embora para quem olha pareça que o retorno, ao tentar ler Value2 (como eu fiz no exemplo lá em cima), deva ser uma string vazia, na verdade Value2 pode ter uma referência nula, e então novamente será gerada uma NullReferenceException, embora por uma razão diferente da citada no caso anterior. Esse segundo caso é comum em células que foram, por exemplo, configuradas com umas certa aparência (bordas, cor de fundo, etc), mas não tiveram nenhum valor atribuído a elas, portanto a célula está instanciada, mas não tem valor associado.


Uma última observação: já trabalhei bastante com Excel em programação e acho importante ressaltar que a execução dele é um processo normalmente lento. Por isso ao se pensar em percorrer um arquivo Excel deve-se procurar a melhor forma de se fazer isso. Ainda assim, sugiro que isso seja feito dessa forma somente se necessário e somente em uma primeira execução com o intuito de capturar os dados e colocá-los, então, em um banco de dados (sugiro mySql), para que na execução do software de fato a leitura seja feita a partir do banco de dados.



Por hoje era só. Dúvidas ou sugestões, é só comentar.


Alguns links que foram úteis:
http://sousatiago.blogspot.com/2007/12/importar-dados-do-excel-com-c.html
http://csharp.net-informations.com/excel/csharp-open-excel.htm
http://bitterolives.blogspot.com/2009/03/excel-interop-comexception-hresult.html