21 09 2016
Resolvendo o erro do Crystal Reports FileNotFoundException crdb_adoplus.dll
Um erro clássico que acontece ao tentarmos exibir um relatório do Crystal Reports em aplicações que utilizam o .NET Framework 4 ou superior é o “FileNotFoundException” relacionado ao arquivo “crdb_adoplus.dll“. Como é que esse erro pode ser resolvido? A maneira mais simples e mais utilizada é fazermos um pequeno ajuste no arquivo app.config da nossa aplicação. Porém, essa não é a única maneira. Existe uma outra alternativa que faz muito sentido em alguns casos. No artigo de hoje você verá essas duas maneiras de resolver o erro do Crystal Reports FileNotFoundException crdb_adoplus.dll.
Entendendo o problema
Para entendermos esse problema, vamos criar um novo projeto do tipo “Windows Forms Application“. Dentro desse projeto, vamos adicionar um relatório do Crystal Reports em branco (vamos dar o nome de “Relatorio” para esse novo relatório que está sendo criado). Em seguida, no formulário, temos que arrastar um controle do Crystal Reports e selecionar o relatório que foi criado no passo anterior.
Ao executarmos essa aplicação, não recebemos erro algum:
Porém, vamos combinar que uma aplicação com um relatório em branco sem fonte de dados não tem nenhuma utilidade. Dessa forma, vamos criar uma classe muito simples que servirá de fonte de dados para o relatório. Essa classe se chamará “QualquerClasse” e terá somente uma propriedade (chamada “Propriedade“):
// C# public class QualquerClasse { public int Propriedade { get; set; } }
' VB.NET Public Class QualquerClasse Public Property Propriedade As Integer End Class
Feito isso, no relatório, vamos até o Database Expert para arrastarmos essa classe para dentro do relatório:
A partir desse momento, ao executarmos novamente a nossa aplicação, o Crystal Reports mostrará a típica caixa de login que aparece quando não passamos a fonte de dados para o nosso relatório:
Isso faz todo o sentido, uma vez que temos uma tabela definida no nosso relatório e nós não estamos alimentando essa tabela ao exibirmos o relatório. OK, então vamos alimentar essa tabela com uma coleção de “QualquerClasse“. Essa coleção conterá somente uma instância dessa classe:
// C# public FormRelatorio() { InitializeComponent(); Relatorio1.SetDataSource(new List<QualquerClasse>(new QualquerClasse[] { new QualquerClasse() { Propriedade = 1 } })); }
' VB.NET Public Sub New() ' This call is required by the designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. Relatorio1.SetDataSource(New List(Of QualquerClasse)(New QualquerClasse() {New QualquerClasse() With {.Propriedade = 1}})) End Sub
E é aí que surge o problema. Nesse ponto, ao executarmos novamente a nossa aplicação, receberemos esse belo erro:
An unhandled exception of type ‘System.IO.FileNotFoundException’ occurred in mscorlib.dll
Additional information: Could not load file or assembly ‘file:///C:\Program Files (x86)\SAP BusinessObjects\Crystal Reports for .NET Framework 4.0\Common\SAP BusinessObjects Enterprise XI 4.0\win32_x86\dotnet1\crdb_adoplus.dll’ or one of its dependencies. The system cannot find the file specified.
Como mencionei no início do artigo, esse erro pode ser consertado de duas maneiras. Vamos conferir as duas opções?
Opção 1: useLegacyV2RuntimeActivationPolicy no app.config
A primeira opção é bem simples. Nós só temos que fazer um pequeno ajuste no arquivo app.config da nossa aplicação. Na tag “startup“, temos que definir o elemento “useLegacyV2RuntimeActivationPolicy” como “true“:
Se você quiser copiar e colar, segue o código da tag “startup” já com o ajuste desse elemento:
<startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> </startup>
Execute novamente a aplicação e veja que o erro foi resolvido.
Opção 2: RuntimePolicyHelper
Em algumas situações, adicionarmos essa tag no arquivo app.config acaba sendo muito complicado. Isso acontece principalmente quando desmembramos a exibição dos relatórios em uma biblioteca (dll) separada. Nesse caso, seria o app.config da aplicação consumidora que deveria ser alterado, e isso é definitivamente algo que deve ser evitado ao disponibilizarmos uma biblioteca que pode ser consumida por várias aplicações consumidoras.
Na empresa onde eu trabalho nós tivemos exatamente esse problema. A lógica “genérica” de exibição de relatórios do Crystal Reports fica em uma dll separada, que é consumida por “N” aplicações. Nós queríamos evitar que o app.config de cada aplicação consumidora tivesse que ser alterado ao utilizar o Crystal Reports. Ao invés disso, nós queríamos fazer essa alteração do “legacy runtime” automaticamente quando a aplicação fosse executada.
Depois de pesquisar um pouquinho, encontramos uma classe “mágica“ que faz com que esse elemento seja adicionado em tempo de execução, evitando que o app.config tenha que ser alterado nas aplicações consumidoras. Vamos ver como essa classe funciona?
Primeiramente, vamos adicionar uma nova classe ao nosso projeto, dando o nome de “RuntimePolicyHelper“. Aqui vai o código dessa classe:
// C# public static class RuntimePolicyHelper { public static bool LegacyV2RuntimeEnabledSuccessfully { get; private set; } static RuntimePolicyHelper() { ICLRRuntimeInfo clrRuntimeInfo = (ICLRRuntimeInfo)System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeInterfaceAsObject( Guid.Empty, typeof(ICLRRuntimeInfo).GUID); try { clrRuntimeInfo.BindAsLegacyV2Runtime(); LegacyV2RuntimeEnabledSuccessfully = true; } catch (System.Runtime.InteropServices.COMException) { // This occurs with an HRESULT meaning // "A different runtime was already bound to the legacy CLR version 2 activation policy." LegacyV2RuntimeEnabledSuccessfully = false; } } [System.Runtime.InteropServices.ComImport] [System.Runtime.InteropServices.InterfaceType(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)] [System.Runtime.InteropServices.Guid("BD39D1D2-BA2F-486A-89B0-B4B0CB466891")] private interface ICLRRuntimeInfo { void xGetVersionString(); void xGetRuntimeDirectory(); void xIsLoaded(); void xIsLoadable(); void xLoadErrorString(); void xLoadLibrary(); void xGetProcAddress(); void xGetInterface(); void xSetDefaultStartupFlags(); void xGetDefaultStartupFlags(); [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.InternalCall, MethodCodeType = System.Runtime.CompilerServices.MethodCodeType.Runtime)] void BindAsLegacyV2Runtime(); } }
' VB.NET Public NotInheritable Class RuntimePolicyHelper Public Shared Property LegacyV2RuntimeEnabledSuccessfully() As Boolean Get Return m_LegacyV2RuntimeEnabledSuccessfully End Get Private Set m_LegacyV2RuntimeEnabledSuccessfully = Value End Set End Property Private Shared m_LegacyV2RuntimeEnabledSuccessfully As Boolean Shared Sub New() Dim clrRuntimeInfo As ICLRRuntimeInfo = DirectCast(System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeInterfaceAsObject(Guid.Empty, GetType(ICLRRuntimeInfo).GUID), ICLRRuntimeInfo) Try clrRuntimeInfo.BindAsLegacyV2Runtime() LegacyV2RuntimeEnabledSuccessfully = True Catch generatedExceptionName As System.Runtime.InteropServices.COMException ' This occurs with an HRESULT meaning ' "A different runtime was already bound to the legacy CLR version 2 activation policy." LegacyV2RuntimeEnabledSuccessfully = False End Try End Sub <System.Runtime.InteropServices.ComImport> <System.Runtime.InteropServices.InterfaceType(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)> <System.Runtime.InteropServices.Guid("BD39D1D2-BA2F-486A-89B0-B4B0CB466891")> Private Interface ICLRRuntimeInfo Sub xGetVersionString() Sub xGetRuntimeDirectory() Sub xIsLoaded() Sub xIsLoadable() Sub xLoadErrorString() Sub xLoadLibrary() Sub xGetProcAddress() Sub xGetInterface() Sub xSetDefaultStartupFlags() Sub xGetDefaultStartupFlags() <System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.InternalCall, MethodCodeType:=System.Runtime.CompilerServices.MethodCodeType.Runtime)> Sub BindAsLegacyV2Runtime() End Interface End Class
A utilização dessa classe é muito simples. Nós só temos que acessar a propriedade “LegacyV2RuntimeEnabledSuccessfully“, que retornará “true” caso o elemento tenha sido adicionado no startup com sucesso e “false” caso contrário. No nosso exemplo, como a exibição dos relatórios está sendo feita diretamente no projeto da aplicação, o lugar ideal para adicionarmos essa chamada seria no método “Main“:
// C# [STAThread] static void Main() { if (RuntimePolicyHelper.LegacyV2RuntimeEnabledSuccessfully) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new FormRelatorio()); } else { MessageBox.Show("Não foi possível ativar o LegacyV2Runtime para a aplicação.", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
No VB.NET, por padrão, você não encontrará um método “Main” em projetos do tipo “Windows Forms Application“. Nesse caso, ou você altera as propriedades do projeto de forma que ele tenha um método “Main” (como descrito nesta thread do StackOverflow) ou você coloca a chamada dessa propriedade no construtor do formulário onde o relatório está sendo exibido:
' VB.NET Public Sub New() If (Not RuntimePolicyHelper.LegacyV2RuntimeEnabledSuccessfully) Then MessageBox.Show("Não foi possível ativar o LegacyV2Runtime para a aplicação.", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error) End If ' This call is required by the designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. Relatorio1.SetDataSource(New List(Of QualquerClasse)(New QualquerClasse() {New QualquerClasse() With {.Propriedade = 1}})) End Sub
Pronto! Com essas alterações, você não precisa mais definir o elemento no arquivo app.config. Ele será automaticamente adicionado em tempo de execução.
Concluindo
O disparo de uma “FileNotFoundException” é algo comum ao utilizarmos o controle do Crystal Reports em aplicações desenvolvidas com o .NET Framework 4 ou superior. Esse erro pode ser facilmente corrigido de duas maneiras: fazendo um ajuste no arquivo app.config ou adicionando dinamicamente um elemento nas políticas de runtime em tempo de execução. Neste artigo você conferiu as duas maneiras de resolver esse problema.
E você, já passou por esse problema com o Crystal Reports? Conhecia a segunda metodologia de resolução? Qual das duas opções você achou melhor? Conte-nos mais detalhes na caixa de comentários!
Por fim, convido você a inscrever-se na minha newsletter. Ao fazer isso, você receberá um e-mail toda semana sobre o artigo publicado e ficará sabendo também em primeira mão sobre o artigo da próxima semana, além de receber dicas “bônus” que eu só compartilho por e-mail. Inscreva-se utilizando o formulário logo abaixo.
Até a próxima!
André Lima
Quebra de página condicional no Report Viewer Trabalhando com SQLite no C# e VB.NET
Cara muito obrigado pelo artigo, eu usava outras versões do Visual studio e crystal, pois comprei um computador novo e isntalei o package 2015 e passei quase dois dias para descobrir oque era. Usei a opção e ja ta rodando. Achei melhor pois ela guia todos os outros processos, tendo em vista que isso foi bom para minha situação.
Olá Paolo, muito obrigado pelo comentário! Fico feliz que você tenha conseguido resolver o problema com ajuda do meu tutorial.. :)
Qualquer dúvida é só entrar em contato..
Abraço!
André Lima
Parceiro muito boa a matéria parabéns, porem não entendi bem onde colocar essa segunda parte do código, abraço.
Olá Ivson! Obrigado pelo comentário! Uma parte você coloca em uma nova classe do seu projeto (é só criar uma nova) e a outra parte você coloca na classe “main”.. O seu projeto é C# ou VB.NET? Se for C#, é no arquivo “Program.cs”.. Entendeu? Se não tiver entendido, me manda mais detalhes de qual parte exatamente você não entendeu..
Abraço!
André Lima
Boa tarde André.
Não há palavras para expressar o quão grato eu estou por sua ajuda, o mundo precisa de mais pessoas como você que estão disposta a compartilhar o conhecimento.
Abração
Olá Joel!
Poxa, muito obrigado por essas belas palavras.. :)
Fico feliz por ter conseguido ajudar.. Qualquer coisa estamos aí..
Abraço!
André Lima
André… e esse erro?!
Could not load file or assembly ‘log4net, Version=1.2.10.0, Culture=neutral, PublicKeyToken=692fbea5521e1304’ or one of its dependencies. The system cannot find the file specified.
Olá Leonardo!
O erro do log4net está vindo provavelmente do Crystal Reports.. Acredito que você não tenha instalado a versão correta da runtime no computador do cliente.. Você verificou isso?
Abraço!
André Lima
Gostei da segunda Opção consertou o meu erro.~
veleu.
Olá, obrigado pelo comentário! Fico feliz por ter conseguido ajudar.. :)
Abraço!
André Lima