Corrigindo dois erros em uma tacada só: O provedor “Microsoft.Jet.OLEDB.4.0” não está registrado na máquina local e O inicializador de tipo de 'CrystalDecisions.CrystalReports.Engine.ReportDocument' acionou uma exceção.

Problema: Porque o sistema operacional é 64bits.
Solução: Modo de compatibilidade em 32bits.

Bom se todo post fosse assim tão rápido para solucionar nossos problemas. Agora vamos alongar mais as respostas e enriquecer nosso conteúdo:

1) O provedor “Microsoft.Jet.OLEDB.4.0” não está registrado na máquina local

Se por ventura você recebeu esse erro ao executar uma query em um banco de dados usando OleDb mesmo que tenha o Microsoft Office instalado corretamente é porque provavelmente a versão do driver instalado é para 32bits. Para corrigir isso, sua aplicação deve rodar em 32bits (x86). Nesse post aqui é mostrado como compilar sua aplicação desktop em x86 exclusivamente solucionando o problema. Mas para quem usa aplicações desktop terá que mudar a configuração do pool de aplicação no IIS. Para isso vá no IIS, Pools de Aplicativos e selecione o Pool usado pelas suas aplicações. Clique em Configurações Avançadas e no item Habilitar Aplicativos 32bits configure para True.


2) O inicializador de tipo de 'CrystalDecisions.CrystalReports.Engine.ReportDocument' acionou uma exceção

Caso bem semelhante ao anterior ocorre se sua máquina é 64bits e instalar o Crystal Reports 32bits (x86). O modo de compatibilidade que o Windows coloca para aplicações instaladas não funciona no caso. Para resolver isso basta instalar a versão 64bits do Crystal Reports (x64). Mas se você realizou o passo acima e está habilitado o modo de compatibilidade de 32bits no IIS então terá que usar a versão 32bits do Crystal de qualquer jeito.

Refresh automático ou execução de script em ciclos

Durante o desenvolvimento de muitos sistemas web nos deparamos com as seguintes situações:
  • Página dar refresh automaticamente a cada x segundos;
  • Executar um bloco de script (JavaScript) a cada x segundos;
  • Executar um bloco de código (ASP.NET) a cada x segundos sem refresh na página.
Creio que são essas as principais situações que são mais comumentes buscadas afim de atingir o objetivo de atualização automática. Pois bem, nesse artigo irei demonstrar como solucioná-las de maneira rápida e eficaz.

Situação 1: Atualizar página automacatimente

Para essa situação, que é a mais simples, basta inserir a seguinte tag no head da página especificando a quantidade de segundos que deve aguardar para dar o refresh:

<META HTTP-EQUIV="Refresh"
CONTENT="10;URL=http://thiagomarcal.blogspot.com">

Situação 2: Executar uma função JavaScript a cada x segundos

Se derem uma olhada no post anterior do chat, verão que uso uma função chamada setTimeout() que irá registrar qual função irá executar e qual o tempo de espera. Então, coloque o seguinte código JS:

window.onload = function() {
    setTimeout("FuncaoJS()", 10000);
}
 
function FuncaoJS(){
    // Instruções
    setTimeout("FuncaoJS()", 10000);
}

De forma similar, pode-se usar também a função setInterval().

Situação 3: Executar um método ASP.NET a cada x segundos sem dar refresh

Já para o ASP.NET podemos usar o AJAX. Para isso utilizaremos um Timer e um UpdatePanel. Na página, adicione o ScriptManager e insira o Timer dentro do ContentTemplate do UpdatePanel. Atribua um método no evento OnTick do Timer ficando da seguinte forma:

<Ajax:ScriptManager ID="ScriptManagerAjax" runat="server">
</Ajax:ScriptManager>
<Ajax:UpdatePanel ID="Painel" runat="server">
<ContentTemplate>
<Ajax:Timer ID="Temporizador" runat="server" Interval="10000" OnTick="ExecutaScript_Tick">
</Ajax:Timer>
</ContentTemplate>
</Ajax:UpdatePanel>

Se você deixar o Timer fora do UpdatePanel ele irá realizar a execução do bloco de script, mas irá dar PostBack e, consequentemente, refresh na página. Por isso, e mais amigável, deve-se deixar dentro.
Agora basta criar o método no Code-Behind contendo o código a ser executado:

protected void ExecutaScript_Tick(object sender, EventArgs e)
{
}

Tranquilo, né?

Criando um Chat em ASP.NET em 1 minuto

Para quem quer ter um chat em seu web site, esse componente é uma mão na roda. Muito fácil de ser utilizado e configurado, apesar de seus bugs (irei solucioná-los no fim do post). Em poucos segundos você tem um chat funcional! A Web Furbish desenvolveu esse componente e pode ser encontrado tanto aqui quanto aqui e em vários mirros pela net. Vamos ao desenvolvimento...
Extraia os arquivos em um local onde poderá buscá-los. Agora crie um novo Web Site. Se for sua primeira vez usando o componente, primeiramente, deve-se adicionar o novo controle ao ToolBox do Visual Studio. Para isso, clique com o botão direito em algum local onde é exibido os itens e escolha a opção Choose Items:


Quando surgir a caixa de seleção de componentes, na aba .NET Framework Components, clique no botão Browse... para selecionar a DLL JaxterChat.dll na pasta onde extraiu os arquivos a princípio. Clique em OK para carregá-la. Verá que ela foi adicionada ao ToolBox. Esse passo foi o mais difícil! Em seguida copie a pasta JaxterChat (que havia junto à DLL) e incorpore-o a seu projeto. Nessa pasta contêm arquivos de imagem, som, etc. Agora é simples: arraste-o para a página para que ele seja adicionado. Pronto! Você já tem seu chat...
O resto é apenas configuração: tema, tempo de atualização, usar som, nome dos labels, etc. Veja como ficou o meu!


Tranquilo e prático, não? Antes dos bugs, vamos a alguns questionamentos?

1) Como faço para ter várias salas?

Você não precisar criar várias páginas para a cada sala. Basta que, no Page_Load, atribua o nome da sala ao controle. Exemplo: Chat.RoomName = "Blog"; O controle irá se encarregar de fazer a separação dos usuários de acordo com o nome da sala.

2) Como gravar o log das conversas?

Crie um método para o OnMessagePosted do chat. Você pode gravar em banco ou em arquivo. Ficaria mais ou menos assim:

protected void Grava_Log(object sender, WebFurbish.JaxterMessageEventArgs e)
{
      string sala = e.RoomName;
      string usuario = e.UserName;
      string mensagemExibida = e.Message;
      string mensagemHtml = e.MessageHTML;
      string hora = DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss");
      // Grava o log em banco ou em arquivo
}
 
3) Como enviar mensagens às salas (avisos de administração, etc) igual a um Broadcast?
 
Esse é um pouco mais complicado e demorado, então irei explicar em linhas gerais como seria:
  • Pegaria todas as sala usando o método GetRoomList();
  • E para cada sala faz-se o envio através do método SendCustomMessage().
Um exemplo seria: Chat.SendCustomMessage("Nova notícia no blog!", "Administração", "Blog", false);
O parâmetro false indica que essa mensagem não será processada em log (ou seja, não parassá pelo método do OnMessagePosted).
 
E n formas que há de customizar o chat para trabalhar da forma como desejar. Pois bem, agora vamos aos bugs encontrados. Quando levantei esses bugs enviei um e-mail para eles mas não obtive resposta até o momento (também nem tentei mais... :P).

Bug 1: A cada mensagem entrada, a barra de rolagem ao invés de acompanhar as mensagens, ela fica sempre apontada para o topo. Ou seja, ela não desce...

Notamos isso quando começa a encher de mensagens. A barra ao invés de rolar automaticamente para baixo, fica estagnada no topo. Analisando o JavaScript que ele gera, verifiquei que há um problema na função WF_goToBottomScroll. Daí, como não posso apagar ela, criei uma outra com o mesmo nome. Como há sobrecarga ela será executada também. É armengue, mas é uma solução. Como funciona essa solução? A cada 0 segundos verifico se o foco está no campo de texto digitado: se estiver, a barra da div rola automaticamente para baixo. Para que isso funcione, baixe esse script aqui para fazer uma varredura de controles HTML. E adicione o seguinte bloco de código JS na página que contêm o chat:

window.onload = function() {
    setFocusTracking();
    sizeMsgOut = document.getElementById('WF_msgOut').scrollHeight;
    setTimeout("WF_goToBottomScroll('WF_msgOut');", 0);
}


function WF_goToBottomScroll(elemento) {
    var msgOut = document.getElementById(elemento);
    if (document.hasFocus) {
        if (document.hasFocus.id == 'WF_textInput')
            msgOut.scrollTop = msgOut.scrollHeight;
    }
    setTimeout("WF_goToBottomScroll('" + elemento + "');", 0);
}

Bug 2: O som não funciona!

Quando vi que iria ter som, pensei: "Bala!". Mas daí ele exige Java para rodar. Pensei novamente: "Tranquilo! Hoje na net, em sites de banco, tem que ter Java, logo pro chat vai ser pré-requisito ter também.". Instalei a versão mais recente e não funcionou! Porque, eu não sei. Como o componente é fechado, a única coisa que deu para ver é que ele chama uma classe Java SoundApplet.class já compilada. Enfim, providenciei outra solução: coloquei a propriedade EnableSound como false e adicionei algumas linhas ao script WF_goToBottomScroll para tocar um som a cada vez que é dado uma entrada de mensagem. Esse som é puro JavaScript! Primeiro, adicione uma tag de som em background em qualquer local da página, assim:

<BGSOUND id="sound" src="">

Depois crie uma variável global var sizeMsgOut = 0; antes do window.onload . Ou seja, estou criando uma variável global que irá armazenar o tamanho de minha div a cada momento e quando entrar na página pela primeira vez terá valor 0. Agora, adicione as linhas abaixo na função WF_goToBottomScroll antes do setTimeout:
if (document.getElementById('WF_msgOut').scrollHeight != sizeMsgOut) {
    document.all.sound.src = "JaxterChat/Sounds/alert.au";
    sizeMsgOut = document.getElementById('WF_msgOut').scrollHeight;
}

A cada iteração da função, verifico se o tamanho da div que contêm as mensagens é diferente do valor armazenado na variável. Se for é porque entrou nova mensagem logo deve tocar o som.

Esses foram dois bugs que encontrei quando estava usando-o. Pode ser que haja outros que não encontrei. Se não tiver é bom, pois é um chat bem legalzinho e fácil e manipular. Até mais!

Gerando o Código de Verificação (CODVER) para o Komerci em ASP.NET ou em Qualquer Linguagem

Quem está começando a fazer a integração de sua loja virtual com o Komerci e está lendo o manual da versão Integrada, se deparou com a parte da geração do código de verificação. Eles disponibilizam uma classe Java denominada CodVer.class. Daí você pensa: "Para eles disponibilizarem uma classe específica em Java deve ser porque é algo muito específico ou cabuloso!". Mas não é... Não sei porque no próprio manual eles não colocam um pseudo-algoritmo ou diagramas que possam facilitar para a elaboração nas demais linguagens. No mesmo manual eles dizem que disponibilizam um ActiveX para quem usa ASP Clássico. É de dar risada! Bem, o código é bem simples. Já converti do Java para C# e, para quem está familiarizado com C/Java, vai entender o algoritmo rapidinho e elaborar o seu também:

public string GeraCodigoVerificao(string _numfil, string _valor)
{
// Início da codificação
string codigo = string.Empty;
// Número da filiação
int numfil = 0;
numfil = int.Parse(_numfil);
// Total
int total = 0;
string parteInteira = _valor.Substring(0, _valor.IndexOf('.'));
total = int.Parse(parteInteira);
// Codificação a hora
int segundosAgora = DateTime.Now.Second;
int segundosAgoraCodificados = 0;
switch (segundosAgora)
{
case 0:
segundosAgoraCodificados = 11;
break;
case 1:
segundosAgoraCodificados = 17;
break;
case 2:
segundosAgoraCodificados = 21;
break;

case 3:
segundosAgoraCodificados = 31;
break;
case 4:
segundosAgoraCodificados = 56;
break;
case 5:
segundosAgoraCodificados = 34;
break;
case 6:
segundosAgoraCodificados = 42;
break;
case 7:
segundosAgoraCodificados = 3;
break;
case 8:
segundosAgoraCodificados = 18;
break;
case 9:
segundosAgoraCodificados = 13;
break;
case 10:
segundosAgoraCodificados = 12;
break;
case 11:
segundosAgoraCodificados = 18;
break;
case 12:

segundosAgoraCodificados = 22;
break;
case 13:
segundosAgoraCodificados = 32;
break;
case 14:
segundosAgoraCodificados = 57;
break;
case 15:
segundosAgoraCodificados = 35;
break;
case 16:
segundosAgoraCodificados = 43;
break;
case 17:
segundosAgoraCodificados = 4;
break;
case 18:
segundosAgoraCodificados = 19;
break;
case 19:
segundosAgoraCodificados = 14;
break;
case 20:
segundosAgoraCodificados = 9;
break;
case 21:
segundosAgoraCodificados = 20;
break;
case 22:
segundosAgoraCodificados = 23;
break;
case 23:
segundosAgoraCodificados = 33;
break;
case 24:
segundosAgoraCodificados = 58;
break;
case 25:
segundosAgoraCodificados = 36;
break;
case 26:
segundosAgoraCodificados = 44;
break;
case 27:
segundosAgoraCodificados = 5;
break;
case 28:
segundosAgoraCodificados = 24;
break;
case 29:
segundosAgoraCodificados = 15;
break;
case 30:
segundosAgoraCodificados = 62;
break;
case 31:
segundosAgoraCodificados = 25;
break;
case 32:
segundosAgoraCodificados = 34;
break;
case 33:
segundosAgoraCodificados = 59;
break;
case 34:
segundosAgoraCodificados = 37;
break;
case 35:
segundosAgoraCodificados = 45;
break;
case 36:
segundosAgoraCodificados = 6;
break;
case 37:
segundosAgoraCodificados = 25;
break;
case 38:
segundosAgoraCodificados = 16;
break;
case 39:
segundosAgoraCodificados = 27;
break;
case 40:
segundosAgoraCodificados = 63;
break;
case 41:
segundosAgoraCodificados = 26;
break;
case 42:
segundosAgoraCodificados = 35;
break;
case 43:
segundosAgoraCodificados = 60;
break;
case 44:
segundosAgoraCodificados = 38;
break;
case 45:
segundosAgoraCodificados = 46;
break;
case 46:
segundosAgoraCodificados = 7;
break;
case 47:
segundosAgoraCodificados = 26;
break;
case 48:
segundosAgoraCodificados = 17;
break;
case 49:
segundosAgoraCodificados = 28;
break;
case 50:
segundosAgoraCodificados = 14;
break;
case 51:
segundosAgoraCodificados = 36;
break;
case 52:
segundosAgoraCodificados = 2;
break;
case 53:
segundosAgoraCodificados = 39;
break;
case 54:
segundosAgoraCodificados = 47;
break;
case 55:
segundosAgoraCodificados = 8;
break;
case 56:
segundosAgoraCodificados = 29;
break;
case 57:
segundosAgoraCodificados = 22;
break;
case 58:
segundosAgoraCodificados = 55;
break;
case 59:
segundosAgoraCodificados = 33;
break;
}
// Cálculos e atribuições
segundosAgora = segundosAgoraCodificados;
string pad = string.Empty;
if (segundosAgora < 10)
pad = "0";
else
pad = "";
string ipremoto = Request.ServerVariables["REMOTE_ADDR"];
int tamIP = ipremoto.Length;
int i_5_ = total + segundosAgora;
int i_6_ = segundosAgora + tamIP;
int i_7_ = segundosAgora * numfil;
int i_8_ = i_7_.ToString().Length;
// Monta o código
codigo = i_7_.ToString() + i_5_.ToString() + i_6_.ToString() + "-" + i_8_.ToString() + pad + segundosAgora.ToString();
// Retorna
return codigo;
}

Dar POST em um Form e Redirecionar para Página Externa com ASP.NET

Recentemente estava programando o envio de informações de pagamento para o Komerci (estrutura de pagamentos da Redecard) e em certo momento precisaria realizar um POST a um endereço passando parâmetros/variáveis a ela. Antes de partir para a solução, vou revisar aqui alguns conceitos básicos que podem ser usados para esse tipo de envio:
  • Responde.Redirect: Por esse método só podemos passar os parêmetros via GET;
  • Server.Transfer: Por esse método podemos usar o POST mas se o destino estiver no mesmo domínio da aplicação; 
  • HttpWebRequest: Por esse método você pode fazer uma requisição através de uma conversão dos dados em bytes passando via POST/GET, contudo não há como fazer um redirecionamento;
  • WebClient: Por esse método pode-se realizar a passagem de parâmetros, mas também não consegue fazer o redirecionamento após o envio.
Analisando a melhor forma de fazer isso, encontrei um artigo no Code Project na qual baseei-me para fazer o meu e simplifiquei/facilitei mais o processo. Quando criamos um Web Form ele já vem com uma tag form. Adicionei dentro dele um Literal para adicionarmos meus parâmetros que serão gerados via Code-Behind.


Agora vamos criar um método que irá criar os inputs com atributo hidden e seus respectivos valores, montar um JavaScript que força o POST e "startar" o processo:

private void PostRedirectAspNet()
{
StringBuilder formulario = new StringBuilder();
// Adiciono todos os campos e seus valores que devem ser passados via POST
formulario.Append("<input type=\"hidden\" name=\"TRANSACAO\" value=\"04\">");
// Crio um JavaScript para forçar o POST na página corrente
formulario.Append("<script language=\"javascript\">");
formulario.Append("var theForm = document.forms['formAsp']; if (!theForm) { theForm = document.formAsp; } ");
formulario.Append("theForm.action = \"http://thiagomarcal.blogspot.com\";");
formulario.Append("theForm.method = \"post\";");
formulario.Append("theForm.submit();");
formulario.Append("</script>");
// Aplica o script gerado no Literal para iniciar o processo
FormularioPost.Text = formulario.ToString();
}
 
Assim, basta chamá-lo onde quiser que ele irá gerar as tags HTML contendo as informações desejadas,  realizará o POST e, consequentemente, seu redirecionamento. Bem simples, não? Esse método (de fazer) pode ser aproveitado para quaisquer linguagem de programação (PHP, Java, ASP, .NET, CodFusion, etc). O importante é como providenciar seu funcionamento de acordo com a lógica. 

NfseUtil - Utilitário e Código-fonte de Aplicativo que Assina e Envia os XMLs para emissão da Nota Fiscal Eletrônica

Depois de nos matarmos para entender o funcionamento, noites em claro para testes e mais testes, a SEFAZ - Salvador disponibilizou uma DLL e um conjunto de códigos-fonte para realizar todo o processo de assinatura e envio. Agora é mão na roda para quem irá começar a implementar. Para quem já passou por esse sofrimento agora é só descanço... Para mais informações acesse aqui. Abaixo tem os links dos principais arquivos para o utilitário, documentos e XMLs de exemplo:

Usando Certificado Digital para Nota Fiscal Eletrônica no Windows Server 2003/2008

Desde que lançaram a nota fiscal eletrônica, nós programadores ficamos com receio do uso das leitoras e cartões nos sistemas operacionais para a assinatura do XML usando o certificado digital. Geralmente os servidores usufruem de alguma versão do Windows Server para acomodar as aplicações web (que realizam o processo de assinatura) além de proporcionar mais robustez e qualidade diante dos trabalhos. Mas as leitoras e cartões simplesmente não funcionam como deveriam! Incompatibilidade... dizem.
Enfim... como fazer? Simplesmente é só usar outro sistema operacional!

"Mas como vou usar se meu servidor é Windows Server e não posso instalar outro SO?" (Você se perguntando)

Resposta: Use os dois através de uma máquina virtual! Há vários tipos e para vários gostos ficando a critério do que você for utilizar. Recomendo o VMware! Ele dispõe de duas versões (há outras que você pode optar também) que são ótimas para uso: Player e Server. Em resumo, a Player é limitada pois seu uso é simples mas não deixa de ser útil.

Vamos ao processo que será bem simples:
  1. Instale uma das versões do VMware;
  2. Instale um Windows 2000/XP/Vista nela contanto que seja compatível com sua leitora/cartão (a instalação é bem simples e sem dificuldades - é mais fácil do que quando vai instalar no computador "de verdade" bastando seguir o Wizard dela);
  3. Agora conecte a leitora do cartão no PC. Note que no Windows Server não acontecerá nada, mas no Windows interno começará a instalação;
  4. Em seguida espete o cartão na leitora;
  5. Abra o utilitário da leitora (através do Windows da máquina virtual) e verá que o cartão foi lido corretamente.
Pronto! Daí basta compartilhar uma pasta entre o Windows Server e a máquina virtual (através do VMWare se faz isso) e, enquanto no Windows Server vai jogando os XMLs nessa pasta, o Windows MV (da máquina virtual) vai assinando/enviando. Bem louco, não? Mas funciona! Pode até ter o Linux virtualizando um Windows que a leitora irá funcionar.