NUNCA MAIS PASSE RAIVA POR NÃO CONSEGUIR RESOLVER UM PROBLEMA COM O EXCEL - GARANTIDO!

UNIVERSIDADE DO VBA - Domine o VBA no Excel Criando Sistemas Completos - Passo a Passo - CLIQUE AQUI

Você está em: PrincipalArtigosASP.NET › Capítulo 16 : 02
Quer receber novidades e e-books gratuitos?
« Lição anterior Δ Página principal ¤ Capítulos Próxima lição »
ASP.NET - CURSO COMPLETO
Autor: Júlio Battisti
Lição 131 - Capítulo 16 - Tratamento de erros no Framework .NET

O tão sonhado “Aplicativo Livre de Erros” ainda é um sonho distante. Aplicações são projetadas, concebidas, implementadas, testadas, distribuídas e utilizadas por seres humanos. Seres humanos não são perfeitos, cometem erros logo, é mais do que natural que por maiores que sejam os cuidados, que os aplicativos contenham erros.

Determinados erros são possíveis de serem tratados. Tratar um erro significa fazer com que um aplicativo ou página ASP.NET “comporte-se” de uma maneira elegante quando o erro ocorrer. O exemplo clássico é o erro que é gerado quando mandamos o aplicativo ler um arquivo no disquete, porém não colocamos um disquete no drive. Uma maneira “deselegante” do programa comportar-se seria, simplesmente emitir uma mensagem de erro e encerrar o aplicativo. Uma maneira mais “elegante” seria detectar que ocorreu um erro e, pelo código do erro, informar que deve ser inserido um drive no disquete. O usuário coloca o disquete no drive e clica em um botão OK e pronto, o erro foi contornado sem maiores traumas.

Com ASP.NET tivemos muitas melhorias no tratamento de erros, em relação as versões anteriores do ASP. Os objetos do Framework .NET, para tratamento de erros são mais poderosos e fáceis de utilizar. Além disso podemos fazer o tratamento estruturado de erros, utilizando as estruturas try...catch...finally.

Para maiores informações sobre as estruturas Try...Catch...Finally, consulte o Capítulo 5.

Uma das melhorias mais significativas é que o tratamento de erros é implementado pelo próprio CLR – Common Language Runtime, parte integrante do Framework .NET. Com isso as técnicas e comandos de tratamento de erros são independentes da linguagem utilizada. Por exemplo, podemos passar uma exceção gerada em uma página ASP.NET, codificada em C#, para um componente de tratamento de erros criado com VB.NET. As estruturas, mensagens e códigos de erro são os mesmos, independente da linguagem, pois estes elementos são parte integrando do Framework .NET. O CLR usa o mecanismo de exceções para fazer o tratamento de erros. Toda vez que um erro acontece durante a execução de um programa ou de uma página ASP.NET, uma exceção é disparada. O tipo de exceção gerada, depende do erro ocorrido. Neste capítulo aprenderemos a detectar a ocorrência de exceções e a tratá-las.

Exceções e a Classe Exception.

Quando uma exceção é gerada devido a um erro em um programa ou em uma página ASP.NET, um objeto derivado da classe Exception é criado. Através das propriedades da classe Exception, podemos acessar uma série de informações sobre a origem do erro que gerou a exceção.

A classe Exception faz parte do namespace System – System.Exception. Esta classe á a classe base, da qual são derivadas todas as outras classes que fazem o tratamento de exceções.

Uma exceção é uma resposta do sistema a ocorrências de condições anormais geradas devido a erros, durante o processamento de um programa ou página ASP.NET. O CLR (Common Language Runtime) nos fornece um modelo para o tratamento de exceções. Este modelo é baseado na criação de objetos de exceção baseados na classe Exception ou nas classes derivadas de Exception. Outro princípio básico do modelo de tratamento de exceções do CLR é a separação entre o código que contém a lógica do programa e o código para o tratamento de exceções, através da utilização das estruturas try...catch...finally, descritas e exemplificadas no Capítulo 5.

Quando uma exceção ocorre em um bloco try, o controle de execução é passado para o bloco catch corresp0ndente, para que seja feito o tratamento da exceção. Se o caminho de execução possui vários métodos, cada um chamando o outro em seqüência, a exceção será repassada até que o CLR encontre um tratamento para a exceção. Se nenhum dos métodos chamados contiver um tratamento para a exceção, o tratador default será chamado pelo CLR para exibir o nome da exceção, uma mensagem de erro e informações sobre o método onde a exceção foi gerada.

Temos duas categorias de exceções baseadas na classe Exception:

  • SystemException: São classes de exceções predefinidas, baseadas na classe SystemException.
  • ApplicationException : São exceções a nível de aplicação, que são definidas pelo usuários, sendo baseadas na classe ApplicationException.

Diversas informações sobre uma exceção podem ser obtidas a partir das propriedades da classe na qual se baseia o objeto de exceção que foi criado. Normalmente este objeto é baseado na classe Exception.

Na Tabela 16.1 temos a descrição das principais propriedades da classe Exception.

Propriedade Descrição
HelpLink Define ou retorna um link para um arquivo de ajuda associado com a exceção.
InnerException Obtém uma referência ao objeto que representa a exceção que ocorreu em primeiro lugar, ou seja, a primeira exceção gerada na pilha de chamada dos métodos relacionados com o erro que disparou a exceção. Conforme descrevemos anteriormente, uma exceção pode ter sido gerada em um método, porém tratada em um outro método chamado diretamente pelo método que gerou a exceção ou chamado por um método que foi chamado pelo método original e assim por diante.
MessageProperty Retorna a mensagem associada com a exceção.
TargetSite Retorna o nome do método original, onde foi inicialmente disparada a exceção.
ToString Retorna o nome completo da exceção, a mensagem de erro, e outras informações sobre a exceção.

Tabela 16.1 Propriedades da classe System.Exception.

Veremos exemplos dos usos destas propriedades nos exemplos práticos, mais adiante neste capítulo.

Uma revisão rápida da estrutura Try...Catch...Finally para o tratamento estruturado de exceções:

A seguir coloco, resumidamente, o funcionamento e exemplos dos comandos Try...Catch...Finally, utilizados para o tratamento estruturado de exceções.

Tratar exceções é de fundamental importância para que um programa não seja encerrado inesperadamente. Uma exceção pode acontecer durante o processamento do programa, quando algo inesperado acontece e deve ser tratado pelo programa, para que este não seja encerrado sem que o usuário saiba o que está acontecendo.
Dois exemplos típicos de exceções:

  • O programa tenta fazer uma leitura no disquete e não existe disquete no drive.
  • Uma divisão por zero.

As exceções devem ser detectadas e opções devem ser oferecidas para o usuário do programa. Por exemplo, no caso do disquete que não está no drive, a exceção deve ser detectada e o programa deve exibir uma mensagem solicitando que o usuário insira um disquete no drive. Este procedimento é muito mais amigável do que simplesmente encerrar o programa.
Outra grande vantagem do Framework .NET é que o tratamento de exceções é padronizado, independentemente da linguagem que está sendo utilizada. Uma exceção gerada em um componente escrito em C++ pode ser tratada em um cliente escrito em C# e vice-versa.

Neste tópico veremos como tratar exceções com a linguagem C#.

Utilizando “try” e “catch”.

Para definir o tratamento de exceções em nossos programas precisamos organizar os códigos em um bloco try e um bloco catch. Dentro do bloco try colocamos o código que pode gerar uma exceção – por exemplo os comandos que farão a leitura de um arquivo no disquete e que, se o disquete não estiver no drive, será gerada uma exceção. O código para o tratamento da exceção é colocado dentro do bloco catch.

Vamos apresentar um exemplo onde utilizamos try e catch para fazer o tratamento de exceções. O nosso programa solicita que o usuário digite dois números. Depois o programa faz a divisão dos números e exibe o resultado. Para forçar uma exceção vamos fornecer um valor zero para o segundo número, de tal forma que o programa, ao tentar fazer uma divisão por zero, irá gerar uma exceção.

Considere o exemplo da Listagem 16.1

Listagem 16.1 – Tratamento de exceções com try e catch –  ex1cap16.cs

using System;
class ex1cap16
                   {
               // Exemplo 1 - Capítulo 16.
               // Tratamento de exceções com try e  catch.
               //  Por: Júlio Battisti
               //  MCP, MCP+I, MCSE, MCSE+I, MCSE, MCDBA
            public  static void Main()
               {
            // Início do bloco try.
               // Contém o código que pode gerar a  exceção.
            try
                {
                           // Declaração das  variáveis.
                        int divisao;
                        // Entrada dos valores  de x e y
                        Console.Write("Digite  o NUMERADOR ->");
                           String  Aux1=Console.ReadLine();
                        Console.Write("Digite  o DENOMINADOR ->");
                           String  Aux2=Console.ReadLine();
                        // Cálculo da divisão.
                           divisao  = Convert.ToInt32(Aux1) / Convert.ToInt32(Aux2);
                        // Exibição dos resultados.
                           Console.WriteLine("O  valor da DIVISÃO    é ->  {0}",divisao);
                   }
                           // Final do bloco try.
                         // Início do bloco catch.
                           // Código que será  executado se uma exceção
                           // for gerada no bloco  try.
                  
               catch  (Exception e)
               {
                      Console.WriteLine("FOI GERADA A  SEGUINTE EXCEÇÃO: " + e.Message);
                   }                               
                        // Final do bloco catch.
               }
                   }

Digite o exemplo da listagem 16.1 e salve-o em um arquivo chamado ex1cap16.cs, na pasta C:\Meus documentos. Compile e execute o exemplo da listagem 16.1. Digite 10 para o numerador e 2 para o denominador. Você obterá os resultados indicados na Figura 16.1.


Figura 16.1 Executando, sem exceções,  o programa ex1cap16.exe.

Observe que o programa executa normalmente. Agora vamos forçar uma exceção, para isso digitaremos 0 para o segundo valor, forçando uma divisão por zero. Vamos executar novamente o programa. Digite 10 para o primeiro valor e 0 para o segundo. Você obterá os resultados indicados na Figura 16.2.


Figura 16.2 Executando, forçando uma exceção,  o programa ex1cap16.exe.

Neste segundo caso ao tentar fazer uma divisão por zero, uma exceção será gerada. Ao ser gerada a execução o código do bloco catch será executado. O bloco catch recebe um parâmetro do tipo Exception. Exception é uma classe do namespace System – System.Exception, conforme descrito anteriormente. Uma das propriedades desta classe é Message, a qual contém a mensagem associada com a exceção, conforme descrito na Tabela 16.1. No nosso exemplo a mensagem é: “Attempted to divide by zero”, o que confirma a nossa tentativa de fazer uma divisão por zero.

Existem classes que tratam exceções mais específicas, como por exemplo:

  • System.OutOfMemoryException
  • System.OverFlowException
  • System.NullReferenceException
  • System.NotSupportedException
  • System.NotImplementedException
  • System.NotFiniteNumberException
  • System.MissingMethodException
  • System.MissingMemberException
  • System.MissingFieldException
  • System.MethodAccessException
  • System.MemberAccessException
  • System.InvalidProgramException
  • System.InvalidOperationException
  • System.InvalidCastException
  • System.IndexOutOfRangeException
  • System.FormatException
  • System.FieldAccessException
  • System.ExecutionEngineException
  • System.EntryPointNotFoundException
  • System.DuplicateWaitObjectException
  • System.DllNotFoundException
  • System.DivideByZeroException

Utilizando “try” e “finally”

Em algumas situações podemos querer que um determinado bloco de código seja executado, mesmo que não tenha sido gerada nenhuma exceção. Para que isso seja possível podemos utilizar finally ao invés de catch ou em conjunto com catch; desta forma se ocorrer a exceção, o bloco catch será executado e o bloco finally será sempre executado, quer tenha ocorrido ou não uma exceção. O código dentro do bloco finally é sempre executado, mesmo que não tenha sido gerada nenhuma exceção.

Vamos modificar um pouco o exemplo anterior.

Considere o exemplo da Listagem 16.2

Listagem 16.2 – Tratamento de exceções com try e finally –  ex2cap16.cs

using System;
class ex2cap16
                   {
               // Exemplo 2 - Capítulo 16.
               // Tratamento de exceções com try e  finally.
               //  Por: Júlio Battisti
               //  MCP, MCP+I, MCSE, MCSE+I, MCSE, MCDBA
            public  static void Main()
               {
               // Bloco try.
               // Contém o código que pode gerar a  exceção.
            try
                {
                        // Declaração das  variáveis.
                        int divisao;
                        // Entrada dos valores  de x e y
                        Console.Write("Digite  o NUMERADOR ->");
                           String  Aux1=Console.ReadLine();
                        Console.Write("Digite  o DENOMINADOR ->");
                           String  Aux2=Console.ReadLine();
                        // Cálculo da divisão.
                        divisao  = Convert.ToInt32(Aux1) / Convert.ToInt32(Aux2);
                        // Exibição dos resultados.
                           
                           Console.WriteLine("O  valor da DIVISÃO    é ->  {0}",divisao);
                   }
                           // Final do bloco try.
                         // Início do bloco  finally.
                           // Código que será  executado mesmo que nenhuma exceção
                           // seja gerada no bloco  try.
                  
                  finally
                {
       Console.WriteLine("CÓDIGO EXECUTADO TENHA OU NÃO SIDO GERADA UMA EXCEÇÃO");
                }                               
                        // Final do bloco  finally.
               }
                   }

Digite o exemplo da listagem 16.2 e salve-o em um arquivo chamado ex2cap16.cs, na pasta C:Meus documentos. Compile e execute o exemplo da listagem 16.2. Digite 10 para o numerador e 2 para o denominador. Você obterá os resultados indicados na Figura 16.3. Observe que o código do bloco finally foi executado, mesmo sem ter sido gerada nenhuma exceção.


Figura 16.3 Executando, sem exceções,  o programa ex2cap16.exe.

Agora vamos forçar uma exceção, para isso digitaremos 0 para o segundo valor, forçando uma divisão por zero. Vamos executar novamente o programa. Digite 10 para o primeiro valor e 0 para o segundo. Na Figura 16.4 é aberta uma janela indicando que ocorreu uma exceção no programa. Esta janela é aberta porque não temos um bloco catch para fazer o tratamento da exceção.


Figura 16.4 Aviso que uma exceção foi gerada.

Neste caso, como não havia um bloco catch, a exceção não foi tratada. Por isso que surgiu a janela indicada na Figura 16.4. Dê um clique em OK para fechar a janela de aviso e observe que o código do bloco finally foi executado, mesmo tendo sido gerado uma exceção, conforme indicado pela Figura 16.5:


Figura 16.5 O código do bloco finally é sempre executado.

Com essa breve revisão (os comandos Try...Catch...Finally foram detalhadamente explicados no Capítulo 5), podemos apresentar alguns exemplos de tratamento de exceções em páginas ASP.NET.

Múlitiplos blocos Catch e tratamento de exceções específicas.

Para um bloco Try, podemos ter múltiplos blocos Catch. Por exemplo, se em uma página ASP.NET prevemos a possibilidade de serem gerados três diferentes tipos de Exceções, podemos criar um bloco Try, com três blocos Catch, um para tratar cada um dos tipos de Exceções previstos. Por exemplo, podemos criar um bloco Catch para tratar uma exceção do tipo System.OutOfMemoryException, um para tratar uma exceção do tipo System.OverFlowException e uma Terceira para tratar uma exceção do tipo System.NullReferenceException.

Nota: Para uma referência completa, a todos os objetos para tratamentos de Exceções disponíveis no Framework .NET, consulte a documentação do produto ou o site www.msdn.com/net.

Além dos blocos para tratar exceções específicas, podemos e devemos colocar um bloco Catch, bem no final, para tratar exceção genérica – System.Exception. Desta forma não corremos o risco de termos uma exceção não tratada, a qual fará com que a página deixe de ser carregada ou que o programa termine de maneira inesperada. Considere o exemplo genérico da Listagem 16.3:

Listagem 16.3 – Múltiplos blocos Catch.

try
                   Comando 1
                   Comando 2
                   ...
                   Comando n
//  Bloco Catch para tratar uma exceção do tipo OutOfMemoryException.
 
catch  Exceção1 As OutOfMemoryException
                   Comando 1
                   Comando 2
                   ...
                   Comando n
//  Bloco Catch para tratar uma exceção do tipo FileNotFoundException.
catch Exceção2 As FileNotFoundException
                   Comando 1
                   Comando 2
                   ...
                   Comando n
//  Bloco Catch para tratar qualquer tipo de exceção, ou seja, uma exceção  Genérica.
                   //  Este bloco tratará qualquer exceção que ocorra neste programa, com exceção das
                   //  Exceções dos tipos: OutOfMemoryException e FileNotFoundException, já tratadas
                   //  nos blocos Catch anteriormente.
catch ExceçãoGenérica As Exception
                   Comando 1
                   Comando 2
                   ...
                   Comando n

Um exemplo de tratamento de exceção do tipo SqlException.

Vamos apresentar uma página ASP.NET onde tentamos fazer uma conexão com um servidor SQL Server 2000 que não existe. Esta tentativa irá gerar um exceção do tipo SqlException, derivada da classe System.Data.SqlClient.SqlException. Iremos criar um bloco Try com dois blocos Catch: Um para tratar a exceção específica, do tipo SqlException e outro para tratar uma exceção genérica, do tipo Exception.

O exemplo proposto está demonstrado na Listagem 16.4.

Listagem 16.4 – Usando Try...Catch...Finally em um página ASP.NET.

<%@  Import Namespace="System.Data" %>
   <%@  Import Namespace="System.Data.SqlClient" %>
<html>
<script  language="C#" runat="server">
protected  void Page_Load(Object Src, EventArgs E ) 
                   {
//Início do bloco Try.
       
               try
               {
            //  Crio uma conexão com o banco de dados pubs localizado no servidor local.
               //  Vamos acessar a instância SERVIDORXYZ\NETSDK.
               //  O Servidor: SERVIDORXYZ não existe. 
                   // Isso fará com que seja  gerada uma exceção do tipo SqlException, 
                   // a qual será tratada em um  bloco Catch específico para o tratamento desta exceção.
 
String strCon =  "server=SERVIDORXYZ\\NETSDK;uid=sa;pwd=;database=pubs";
               SqlConnection  MinhaConexão = new SqlConnection(strCon);
               MinhaConexão.Open();
// Declaro uma variável do  tipo String: auxPropriedades.
               // A variável auxPropriedades irá conter o  valor das propriedades 
               // da conexão minhaConexão.
            String auxPropriedades;
               auxPropriedades = "Propriedades da  conexão:";
            auxPropriedades = auxPropriedades +  "\n\n" + "ConnectionString: " + 
               MinhaConexão.ConnectionString.ToString();
            auxPropriedades =  auxPropriedades + "\n\n" + "Database: " +                                  
                   MinhaConexão.Database.ToString();
 
auxPropriedades =  auxPropriedades + "\n\n" + "DataSource: " + 
                                      MinhaConexão.DataSource.ToString();
            auxPropriedades = auxPropriedades +  "\n\n" + "State: " + 
                                                  MinhaConexão.State.ToString();
            ExibePropriedades.Font.Bold=true;
               ExibePropriedades.Text=auxPropriedades;
         
      // Final do  Bloco Try.
      }
   // Início  do bloco Catch para tratamento da exceção do tipo SqlException.
   catch (SqlException SqlEx) 
      {
               Response.Write("<b>  Iniciando o tratamento da exceção do tipo  SqlException</B><br>");
                   Response.Write(SqlEx.ToString() + "<p>");
               
                   // Corrijo o nome do  Servidor SQL Server e estabeleço a conexão corretamente.
            String strCon =  "server=SERVIDOR\\NETSDK;uid=sa;pwd=;database=pubs";
               SqlConnection  MinhaConexão = new            SqlConnection(strCon);
            MinhaConexão.Open();
            // Declaro uma variável do tipo String:  auxPropriedades.
               // A variável auxPropriedades irá conter o  valor das propriedades 
               // da conexão minhaConexão.
            String auxPropriedades;
               auxPropriedades = "Propriedades da  conexão:";
            auxPropriedades = auxPropriedades +  "\n\n" + "ConnectionString: " + 
                                      MinhaConexão.ConnectionString.ToString();
            auxPropriedades =  auxPropriedades + "\n\n" + "Database: " + 
                                      MinhaConexão.Database.ToString();
            auxPropriedades = auxPropriedades +  "\n\n" + "DataSource: " + 
                                      MinhaConexão.DataSource.ToString();
            auxPropriedades  = auxPropriedades + "\n\n" + "State: " + 
                                      MinhaConexão.State.ToString();
            ExibePropriedades.Font.Bold=true;
               ExibePropriedades.Text=auxPropriedades;
            // Final do Bloco Catch para tratamento  da exceção do tipo SqlException.
      }
   // Início  do bloco Catch para tratamento da exceção do tipo Exception.
      catch (Exception ex) 
      {
            Response.Write("Tratando uma exceção  genérica<p>");
   }
      
      // Bloco  Finally. É sempre executado, mesmo que nenhuma exceção tenha sido gerada.
   finally 
      {
       Response.Write("<b>Código do bloco Finally sendo executado  !!!</b><p>");
   }
   }
</script>
<body>
      <h3><font  face="Verdana">Classe SqlConnection!!!</font></h3>
 
            <asp:TextBox 
                     runat=server
                     id="ExibePropriedades"
                    Text=""
                    Rows="10"
                     Cols="70"
                     Font_Face="Arial" 
                     Font_Size="3"
                     BackColor="lightblue"
                     TextMode="MultiLine"
               />
    
   </body>
   </html>

Digite o código da Listagem 16.4 e salve-o em um arquivo chamado chap16ex1.aspx, na pasta chap16, dentro da pasta wwwroot, conforme descrito no item: “Check List para acompanhar os exemplos deste livro”, no Capítulo 6.

Para acessar esta página utilize o seguinte endereço:

http://localhost/chap16/chap16ex1.aspx

Ao carregar a página você irá obter uma página semelhante a página indicada na Figura 16.6.


Figura 16.6 Tratamento de Exceções em uma página ASP.NET.

Comentários sobre o código do exemplo – Chap16Ex1.aspx.

No bloco Try tentamos fazer a conexão com um servidor SQL Server 2000 que não existe:

      String strCon =  "server=SERVIDORXYZ\\NETSDK;uid=sa;pwd=;database=pubs";
      SqlConnection MinhaConexão = new SqlConnection(strCon);

O servidor SERVIDORXYZ não existe. Neste caso será gerada uma exceção do tipo SqlException, a qual será tratada pelo primeiro bloco Catch. Este bloco é disparado em resposta a uma exceção do tipo SqlException, que é o tipo de exceção que é disparada, quando tentamos conectar com um servidor SQL Server que não existe.

No primeiro bloco Catch retornamos as seguintes mensagens:

1.         Iniciando o tratamento da exceção do tipo SqlException
2.         System.Data.SqlClient.SqlException: SQL Server does not exist or access denied. at System.Data.SqlClient.SqlConnection.Open() at ASP.chap16ex1_aspx.Page_Load(Object Src, EventArgs E)

A primeira mensagem é simplesmente um texto retornado por um comendo Response.Write.
A segunda mensagem é retornada a partir do método ToString do objeto SqlEx (que é do tipo SqlException). O método ToString simplesmente retorna um texto descritivo da exceção. A seguir temos o comando que retorna esta mensagem:

Response.Write(SqlEx.ToString() + "<p>");

No restante deste primeiro bloco Catch repetimos os comandos para estabelecer uma conexão com o servidor SQL Server, porém agora utilizamos o nome correto:

            String strCon = "server=SERVIDOR\\NETSDK;uid=sa;pwd=;database=pubs";
            SqlConnection MinhaConexão = new SqlConnection(strCon);

Com isso a conexão é estabelecida e as propriedades da conexão são exibidas, no corpo da página em um Web Server control do tipo TextBox com múltiplas linhas.

Para maiores informações sobre Web Server Controls, consulte os Capítulos 7, 8 e 9.

Colocamos um outro bloco Catch, o qual será disparado em resposta a qualquer exceção que não seja do tipo SqlException. Por exemplo, se tivéssemos uma operação de divisão na página e houvesse uma tentativa de divisão por zero (operação não permitida), uma exceção seria gerada. Como esta exceção não seria do tipo SqlException, ela seria tratada pelo bloco Catch para tratamento de exceções genéricas – exceções do tipo Exception:

            // Início do bloco Catch para  tratamento da exceção do tipo Exception.
               catch  (Exception ex) 
               {
                           Response.Write("Tratando  uma exceção genérica<p>");
                   }

O bloco Finally será sempre executado, independente de ter sido, ou não, gerada alguma exceção.

« Lição anterior Δ Página principal ¤ Capítulos Próxima lição »
Quer receber novidades e e-books gratuitos?

 
 

Contato: Telefone: (51) 3717-3796 | E-mail: webmaster@juliobattisti.com.br | Whatsapp: (51) 99627-3434

Júlio Battisti Livros e Cursos Ltda | CNPJ: 08.916.484/0001-25 | Rua Vereador Ivo Cláudio Weigel, 537 - Universitário, Santa Cruz do Sul/RS, CEP: 96816-208

Todos os direitos reservados, Júlio Battisti 2001-2024 ®

LIVRO: MACROS E PROGRAMAÇÃO VBA NO EXCEL 2016 - CURSO COMPLETO E PRÁTICO

DOMINE A PROGRAMAÇÃO VBA NO EXCEL - 878 PÁGINAS - CLIQUE AQUI