Sanando o "The request was aborted: The request was canceled." no upload

Nome do post meio estranho, mas vamos lá!
Esses dias, fazendo um uploadzinho maroto, o cliente precisou enviar um arquivo "grande" só que na hospedagem dele, mesmo adicionando os parâmetros de configuração no web.config como executionTimeoutmaxRequestLengthmaxAllowedContentLength, etc, continuava o erro. Dando uma vasculhada na net algumas pessoas resolveram colocando o KeepAlive como false mas eu estava usando o WebClient que não tem o atributo e não queria reprogramar o que já havia feito. Eis que encontro uma solução que caiu como uma luva mesmo não usando o atributo.
Adicione ao projeto a classe ExtendedWebClient conforme o código abaixo:

public class ExtendedWebClient : WebClient
{
    public int Timeout { get; set; }
    public new bool AllowWriteStreamBuffering { get; set; }

    protected override WebRequest GetWebRequest(Uri address)
    {
        var request = base.GetWebRequest(address);
        if (request != null)
        {
            request.Timeout = Timeout;
            var httpRequest = request as HttpWebRequest;
            if (httpRequest != null)
            {
                httpRequest.AllowWriteStreamBuffering = AllowWriteStreamBuffering;
            }
        }
        return request;
    }

    public ExtendedWebClient()
    {
        Timeout = 100000;
    }
}

Então, agora é apenas substituir o WebClient por ExtendedWebClient ficando assim:

ExtendedWebClient ftp = new ExtendedWebClient();
ftp.Timeout = Timeout.Infinite;
ftp.AllowWriteStreamBuffering = false;

E o resto segue! 
Pode utilizar o KeepAlive? Pode! Basta fazer da mesma forma que o AllowWriteStreamBuffering.

Implementação do ASP.NET (C#) com o Mikrotik (Hotspot)

Esse projeto até hoje tem demanda e, a pedidos de vocês, coloquei um pequeno projeto no GitHub para que possamos ir incrementando pouco a pouco com nossas idéias e sugestões. Hoje está bem básico e fiz o mais rápido possível para dar aquela forcinha.
Está em WebForm para que fique bem entendível e sirva de base para utilizar com outras linguagens de programação (PHP, Java, etc).
Lembrando que no post anterior, http://thiagomarcal.blogspot.com.br/2011/09/adicionando-uma-pagina-de-cadastro-de.html, tem toda a configuração inicial e muitos de vocês manjam melhor do que eu com o Mikrotik. Beleza?
Dúvidas, sugestões, correções, etc deixem nos comentários. Se quiserem algo muito específico e queiram me presentear com um Mikrotik, eu aceito! :)

Download: https://github.com/tmarcal/Mikrotik

No feriado, vamos resolver problemas

Dica rápida para que está tendo o erro "Could not create _isconfig.xml for use with InstallUtilLib.dll" ao compilar um projeto de instalação (InstallShield). O nome do projeto que está sendo instalado não pode ser igual ao nome do projeto de instalação (e vice-versa). :D

Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.

Retornando das cinzas com dicas, macetes e artigos depois de tanto tempo e inúmeros e-mails o bom filho a casa torna. Restaurando os e-mails de alguns internautas duvidosos, muitos se depararam o erro:

"Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information."

Geralmente sempre acontece quando tentam publicar uma aplicação em um servidor externo e dizem: na minha máquina estava funcionando! Não é? Se identificou? Adicionem no web.config o nível de segurança:

<system.web>    
     <trust level="Full"/> 
</system.web>

E se não der para adicionar ou se não funcionar? Rapaz, esse assunto é polêmico (envolve segurança, bla bla bla). Tem servidores e servidores e não vale a dor de cabeça de ter adaptar demais sua aplicação para o que o provedor te oferece. Acredito que para uma boa relação ambos tem que caminhar de mão dadas.

Convertendo uma pasta em aplicação no IIS via código (C#)

Somente pelo IIS é possível converter uma pasta em aplicação? Mais ou menos. Na versão 7 do IIS já dispõe de módulos que simplificam o processo de criação de site, diretório virtual, pools de aplicação e as aplicações em si. Nesse artigo mostrarei bem rápido como converter uma pasta em uma aplicação.


Crie sua aplicação e adicione, como referência, o binário Microsoft.Web.Administration.dll que fica na pasta C:\Windows\System32\inetsrv . Agora adicione o seguinte código:

using Microsoft.Web.Administration;



private void CriaEntradaIIS(string diretorio)
    {
        try
        {
            // Cria a pasta do site
            if (!Directory.Exists(diretorio))
                Directory.CreateDirectory(diretorio);


            // Servidor de Aplicação
            ServerManager sm = new ServerManager();


            // Captura o site raiz onde irá adicionar a aplicação
            Site st = sm.Sites["Default Web Site"];


            // Cria a aplicação no diretório criado apontando o caminho, ex: aplicacao
            Application app = st.Applications.Add("/aplicacao", diretorio);
            app.ApplicationPoolName = "ASP.NET v4.0";
            sm.CommitChanges();


            // Libera
            sm.Dispose();
        }
        catch (Exception erro)
        {
            return;
        }
    }


Se tentar executar dará erro... Antes dê permissão total ao grupo Serviço de Rede (Network Service) à pasta C:\Windows\System32\inetsrv\config . Agora sim irá criar. Para mais exemplos veja nesse post aqui.

Converter PDF em Imagem em C# com GhostScript

Esse é um artigo bem old, mas que muita gente me enviou e-mails pedindo como fazer isso. Então criei um artigo bem resumido baseado no de Lord TaGoH. Primeiramente, você precisa ter a DLL do GhostScript em seu projeto: ou você instala ou pega de alguém que já tenha instalado. O arquivo que me refiro é o gsdll32.dll. Obs: a conversão só funciona para as versões mais recentes então pegue alguma a partir da versão 9 como garantia.

Lembra do meu último post de scaneamento? Adicionei um botão de anexar um PDF e converter em imagem, então irei aproveitar a mesma aplicação adicionando essa funcionalidade. Copie a DLL para seu projeto e, em Propriedades, deixe configurado o atributo Copiar sempre para quando for compilá-la caso seja um Windows Form. Se for um Web Form, adicione em algum local onde a classe conversora possa encontrá-la.


Na página do CodeProject dispõe de uma classe (binária) já pronta para uso uso da DLL chamada de PdfToImage. Então você podem baixar de  ou aqui. Baixado a DLL, você pode adicionar como referência ao seu projeto/página. Agora é só código...

using PdfToImage;

// Objeto de conversão
PDFConvert converter = new PDFConvert();

// Arquivo PDF selecionado de um OpenFileDialog
string arquivo = FileUploadSelect.FileName;

// Local de saída do arquivo convertido
string output = _directoryForImages + "LJ_" + _loja + "_Scan_" + DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss") + ".jpg";

// Configurações de conversão
converter.OutputToMultipleFile = false;
converter.TextAlphaBit = 4;
converter.FirstPageToConvert = 1;
converter.LastPageToConvert = 1;
converter.FitPage = false;
converter.JPEGQuality = 10;
converter.OutputFormat = "png16m";

// Faz a conversão e retorna true se estiver tudo OK
bool resultado = converter.Convert(arquivo, output);

Pronto! Mais fácil do que isso impossível. Fique atento às configurações de conversão, senão não funciona.

Scanear um documento com C#

Um dos melhores componentes de scaneamento que já utilizei é o da VintaSoft. Ele não é free, mas pode ser utilizado normalmente. O único empecilho é que, para cada scan que fizer, ele irá solicitar que clique em Evaluate. Quem comprar, vale a pena pois é muito bom e seu suporte sempre está a disposição. Chega de elogios e vamos ao que interessa.

O componente permite tanto utilizar em Windows Form quando Web Form e no site há todos os exemplos necessários. Baixe o componente e instale. Para nosso exemplo, trabalharemos com um Windows Form. Fiz uma pequena aplicação de scanear nota fiscal.


Vá na pasta de instalação do componente, copie o arquivo Vintasoft.Twain.dll para sua aplicação e adicione como referência. No Code-Behind do formulário a ser trabalhado, adicione o seguinte código para inicializar o componente:

using Vintasoft.Twain;


// Carrega o gerenciador de dispositivo
DeviceManager _deviceManager = new DeviceManager();
Device _device;


// Avalia se é um dispositivo útil
if (!_deviceManager.IsTwainAvailable)
{
           MessageBox.Show("Não é um dispositivo TWAIN!", "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
           return;
}


try
{
           // Abre o gerenciador de dispositivo
           _deviceManager.Open();
           // Seta o dispositivo corrente
           _device = _deviceManager.Devices.Current;
           lblDipositivo.Text = _deviceManager.Devices.Current.Info.ProductName;
}
catch (TwainDeviceManagerException ex)
{
           MessageBox.Show(ex.Message, "Gerenciador de Dispositivos");
           return;
}


Agora, ao clicar no botão Scannear, adicionamos o seguinte método:

private void btnScanear_Click(object sender, EventArgs e)
        {
            try
            {
                // Seta as configurações
                _device.TransferMode = TransferMode.File;
                try
                {
                    _device.ShowUI = false;
                    _device.ShowIndicators = false;
                    _device.DisableAfterAcquire = true;
                }
                catch { }


                // Abre o dispositivo
                _device.Open();


                // Propriedades do scaneamento
                try
                {
                    _device.UnitOfMeasure = UnitOfMeasure.Inches;
                    _device.Resolution = new Resolution(float.Parse(resolucaoX.SelectedItem.ToString()), float.Parse(resolucaoY.SelectedItem.ToString()));
                }
                catch { }


                // Informação sobre o arquivo
                _arquivo = "LJ_" + _loja + "_Scan_" + DateTime.Now.ToString("yyyy_MM_dd_HH_mm_ss") + ".jpg";
                _device.FileName = _directoryForImages + _arquivo;
                //_device.FileFormat = TwainImageFileFormat.Jpeg;


                // Captura a imagem
                AcquireModalState acquireModalState = AcquireModalState.None;
                do
                {
                    acquireModalState = _device.AcquireModal();
                    switch (acquireModalState)
                    {
                        case AcquireModalState.ImageAcquired:


                            _device.FileName = _directoryForImages + _arquivo;
                            //_device.FileFormat = TwainImageFileFormat.Jpeg;
                            break;


                        case AcquireModalState.ScanCompleted:


                            CloseDevice();


                            break;


                        case AcquireModalState.ScanCanceled:


                            CloseDevice();


                            break;


                        case AcquireModalState.ScanFailed:


                            CloseDevice();


                            break;


                        case AcquireModalState.UserInterfaceClosed:


                            CloseDevice();


                            break;
                    }
                }
                while (acquireModalState != AcquireModalState.None);
            }
            catch (TwainException ex)
            {
                MessageBox.Show(ex.Message + " - " + ex.StackTrace, "Erro", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
        }


E outros métodos são necessários adicionar ao form, como:



        /// Fecha o dispositivo e a gerência

        private void CloseDeviceAndDeviceManager()
        {
            // Verifica de há dispositivo
            if (_device != null)
            {
                if (_device.State != DeviceState.Closed)
                    _device.Close();
            }


            // Fecha a gerência
            if (_deviceManager.State != DeviceManagerState.Closed)
                _deviceManager.Close();
        }




        /// Fecha o dispositivo

        private void CloseDevice()
        {
            // Desabilita o serviço
            if (_device.State == DeviceState.Enabled)
            {
                if (!_device.ShowUI)
                    _device.Disable();
            }


            // Fecha
            if (_device.State == DeviceState.Opened)
                _device.Close();
        }




        /// Ao fechar o formulário, libera o uso

        private void Scan_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (_device != null)
            {
                // Verifica se está em uso
                if (_device.State == DeviceState.Transferring ||
                    _device.State == DeviceState.TransferReady)
                {
                    // Envia a solicitação de cancelamento
                    _device.CancelTransfer();
                    // Aguarda finalizar
                    e.Cancel = true;
                    MessageBox.Show("O dispositivo está em uso! Aguarde alguns instantes enquanto é solicitado a liberação do scanner.", "Informação");
                    return;
                }
            }


            // Fecha o dispositivo e a gerência
            CloseDeviceAndDeviceManager();
        }

Pronto! O scaneamento é realizado e é gerado um arquivo JPG. Reparem que está comentado o atributo _device.FileFormat porque nem todo scanner dá a possibilidade de configuração. Alguns aceitam e outros não. Se utilizar alguma configuração que o scanner não suporta, deve aparecer a mensagem: "Failed to get/get to capability". Então você precisa deixar apenas as configurações que o scanner suporte.
Bons scans!

Recortando uma Imagem em Tempo de Execução (Image Crop) em C#

Lembram do post de Redimensionar Imagem? Esse post será bem similar e explicarei em poucas palavras o que ele faz. O que é Crop? Segundo a WikipediaCrop (Recorte) refere-se à remoção da parte exterior de uma imagem para melhorar o seu enquadramento. Um exemplo mais entendível: temos uma galeria de fotos nas quais todas as imagens possuem largura e altura diferentes que não são proporcionais; o redimensionamento de imagem para gerar um thumb não irá funcionar pois o produto final ou ficará distorcido ou não ficarão com tamanhos iguais. É aí que o recorte entra...

Veja o exemplo abaixo (retirado da Wikipedia):
No caso em questão era desejado obter um thumb quadrado da imagem. O enquadramento desejado não é proporcional à image original, logo é feito o recorte.

Dessa vez não irei colocar o código pois é um pouco grandinho, mas, como disse, é bem similar ao código do post de Redimensionar Imagem bem como seu uso (em forma de handler.ashx). Esse código é bem velhinho e não é meu. Ele está aí na net a muito tempo e já o vi em vários blogs e artigos. A última vez que o vi estava no subversion de Assembla.

Logo, baixem aqui e usem-o como desejar.

Usando o ScripManager em sequência

Esse artigo será apenas uma dica para os desatentos. Quando você usa o ScriptManager para exibir um alerta, por exemplo, e em outro trecho do código fazer a mesma coisa, verá que apenas o primeiro alerta é demonstrado e o segundo não. O porquê disso é se estiver reaproveitando o mesmo script e não diferenciar o bloco de execução dele, sempre será exibido apenas o primeiro. Logo, o certo, é ter algo do tipo:


// Exibe o primeiro alerta
ScriptManager.RegisterStartupScript(Page, typeof(Page), "script1", "alert('Alerta 1');", true);


...



// Exibe o segundo alerta
ScriptManager.RegisterStartupScript(Page, typeof(Page), "script2", "alert('Alerta 2');", true);

Reparou no bloco script1 e script2? É isso.

Adicionando uma página de cadastro de usuários (Hotspot) em ASP.NET no Mikrotik

Cada projeto que pego, é uma aventura nova a ser encarada! Dessa vez me adentrei no universo de Linux, rede, SSH, infra e tudo o que é direito. Quem não conhece, a Mikrotik é uma empresa de grande referência no que tange em equipamentos para redes de computadores. Seu principal destaque é o RouterOS que é sistema operacional que torna o roteador um poderoso gerente de rede. Dentro do Mikrotik (irei me referenciar sempre dessa forma generalizando como um produto), há o HotSpot. Com ele pode-se gerenciar quem irá autenticar na rede para usar a internet (ou apenas a intranet), banda usada, taxa de transferência e outras funcionalidades por usuário ou de forma geral. Um exemplo de uso desse sistema é de quando você vai a uma rede corporativa onde precisa autenticar-se para usar a internet... é isso!


Qual é o objetivo desse artigo então? A princípio é apenas obter as páginas originais de hotspot do Mikrotik, colocar um redirecionador para uma página externa, criar uma página externa de cadastro de usuário (para que possa acessar a rede) e fazer funcionar. Não ensinarei como configurar ou criar o hotspot. Pela net há vários tutoriais muito bons sobre o assunto e também não é de minha área. Recomendo fortemente o fórum da Under-Linux que lá o pessoal manja muito no assunto.

Nesse artigo usei o Mikrotik MKBR100 que é bem fácil de usar: basta plugar o cabo de rede da internet, do servidor e da energia. Pronto!

Para acessar seu SO, deve-se utilizar o Winbox como se fosse um daqueles players de máquina virtual ou OpenSSH. Vamos lá colocar a mão na massa!

Conecte o cabo de rede de seu PC (ou servidor) na porta (interface) ether2 do Mikrotik. Agora conecte outro cabo na ether1 para os outros computadores da rede (ou roteador simples). Porquê na ether1? Estou considerando que o hotspot está configurado para monitorar essa interface enquanto a ether2 será utilizado para que o servidor de comunique com o aparelho. Seria algo do tipo:



Vá em IP > Address no Mikrotik para que sejam configuradas as interfaces e sejam endereçados conforme os exemplos abaixo:

  • ether1: 192.168.88.1
  • ether2: 192.168.88.2

Somente através de IP que será possível enviar os arquivos do hotspot. Se ficar conectando, via MAC, pelo Winbox, você terá a mensagem de erro: "router has been disconnected" (logo, conecte-se usando a ether2 - 192.168.88.2). E outra coisa é: se não seguir o esquema acima, outros erros você terá, como:

could not connect to ... no response
could not connect to ... connection refused

could not connect to ... network is unreachable
could not fetch index from ... not found


Tudo isso por causa de configuração apenas! É bom deixar o IP do servidor fixo para que possa dar permissão de porta, firewall, etc. No caso deixei como 192.168.88.30 (que no caso era minha própria máquina). Uma dica: às vezes dê um reboot no aparelho!

O próximo passo é ativar a porta da API que o Mikrotik dispõe para integração com linguagens de programação. É através dela que iremos conectar e fazer a comunicação. Só que precisamos ativá-la. Vá em IP > Services e ative a API na porta 8728.


Também libere, no Firewall, as portas para conexão tanto do sistema quanto do Winbox (pelo IP), senão quando estiver acessando pela página de cadastro, irá surgir o erro: "nenhuma conexão pôde ser feita porque a máquina de destino as recusou ativamente ..:8728.".


Para permitir a navegação e envio de informações, adicione as permissões no Walled Garden.


Vejam que até o momento tudo é configuração e nada de programação. Continuemos...

Agora vamos em Files e selecione todos os arquivos da pasta hotspot, arraste para sua área de trabalho que será copiado. Para enviar faça o processo inverso... Se estiver acessando pelo IP vai dar certo, senão cairá a conexão (lembra que disse acima?).

Abra o HTML da página login.html e acione um link para a página de cadastro que ficará hospedada no servidor. Adicionei o link <a href="http://192.168.88.30/mikrotik/cadastro.aspx">cadastre-se</a> . Customize as demais páginas como desejar e as envie de volta. Praticamente terminamos de trabalhar no Mikrotik.

Agora vem o mais simples: criar a página ASP.NET. Você pode utilizar qualquer linguagem de programação que possa usar TCP na comunicação e que a API suporte: PHP, Delphi, C, C++, C#, Flash, Ruby on Rails, Java, Python, VB.NET, etc.

Faça o download da classe MK, em C# (eles já dispõe de classes prontas para outras linguagens também), nesse link aqui e adicione no seu projeto. Agora crie um Web Form adicionando os campos que queira trabalhar. No meu exemplo, só quis login (username) e senha. Adicione um Button e um método para executar o cadastro do usuário conforme exemplo abaixo:


             // Autenticação
            MK mikrotik = new MK("192.168.88.2");
            if (!mikrotik.Login("admin", ""))
            {
                ScriptManager.RegisterStartupScript(Page, typeof(Page), "alert", "alert('Houve um problema de comunicação com o Hotspot! Por favor, tente mais tarde.');", true);
                mikrotik.Close();
                return;
            }

            // Requisição
            mikrotik.Send("/ip/hotspot/user/add");
            mikrotik.Send("=name=" + nome.Text);
            mikrotik.Send("=password=" + senha.Text, true);

            //Retorno
            string retorno = string.Empty;
            foreach (string h in mikrotik.Read())
            {
                if (retorno != string.Empty)
                    retorno += ", ";
                retorno += h;
            }

            // Fecha objeto
            mikrotik.Close();

            // Validação
            if (retorno.ToLower().Contains("!done=ret="))
                ScriptManager.RegisterStartupScript(Page, typeof(Page), "alert", "alert('Cadastro realizado com sucesso!'); location.href='http://192.168.88.1/login.html';", true);
            else if (retorno.ToLower().Contains("message=failure: already have user with this name for this server"))
                ScriptManager.RegisterStartupScript(Page, typeof(Page), "alert", "alert('Já existe um usuário com esse nome!');", true);
            else
                ScriptManager.RegisterStartupScript(Page, typeof(Page), "alert", "alert('Falha no cadastro: " + retorno + "!');", true);


Você poderia não só fazer o cadastro de usuário no hotspot como também executar qualquer comando (SSH) no Mikrotik. Lembrando que eu atropelei muita coisa aí no que diz respeito a configuração do dispositivo na qual disse logo no início que não era minha finalidade. Vocês observaram que a parte de programação é bem simples do que configuração... e com certeza é! Agora nunca irá sair de minha cabeça a palavra Mikrotik Mikrotik Mikrotik Mikrotik Mikrotik ... Por favor, qualquer erro ou informação adicional me avise!

[]'s

UPDATE: disponibilizei uma implementação no post http://thiagomarcal.blogspot.com.br/2016/09/implementacao-do-aspnet-c-com-o.html