31 de maio de 2010

Fazendo aplicações ASP tradicionais funcionarem em servidores 64 Bits

Já tem algum tempo que os sistemas operacionais de 64 bits da Microsoft estão no mercado - Windows Vista, Seven, Windows Server - mas parece que agora é que as empresas estão de fato atualizando seus servidores e adotando em massa essa tecnologia. Pelo menos entre os Clientes da ABC71 essa afirmação vem se mostrando verdadeira.

Embora aplicações 32 bits funcionem normalmente nos sistemas de 64 bits (usando automaticamente a emulação do ambiente 32 bits), sempre aparece alguma situação problemática. É o caso da biblioteca que a ABC71 desenvolveu para que seus Clientes possam construir sites de e-Vendas integrados com o ERP Omega, alimentando diretamente suas tabelas com pedidos de venda on-line de uma maneira que respeita as regras de negócio vigentes no ERP. Essa biblioteca disponibiliza suas funções através de COM mas foi construída para uso com o ASP tradicional (não com o ASP.NET), sendo portanto uma tecnologia 32 bits.

Quando um Cliente nosso trocou seu servidor por um com sistema de 64 bits, o site dele simplesmente parou de funcionar. A razão é que as hospedagem do site utiliza o IIS 7 (Internet Information Services), versão de 64 bits e não é possível fazer um software de 64 bits carregar e executar um de 32 bits diretamente. Para fazer isso, teríamos que construir alguma ponte que permitisse tal acesso.

Algumas soluções foram sugeridas. A de usar uma máquina virtual com algum Windows de 32 bits exclusivamente para hospedar o site foi descartada pois a intenção da troca de servidores era incrementar a performance do site, objetivo que ficaria restringido.

Compilamos, então, a biblioteca de modo que ela passasse a usar .NET 64 bits. Após registrá-la corretamente como um COM usando a ferramenta RegAsm 64 bits (no meu computador, essa versão está na pasta C:\Windows\Microsoft.NET\Framework64\v2.0.50727) passamos a receber nas páginas ASP uma mensagem indicando que o tipo 'InternalField' não pode ser convertido para o tipo 'Short' toda vez que tentávamos passar um valor como parâmetro em chamadas a funções da biblioteca COM. Parece que há alguma coisa no ASP normal executando em IIS7 que o impede de trabalhar com um COM gerado em .NET 64 bits.

Quando pesquisei a respeito na internet, me deparei com um tutorial passo a passo mostrando como configurar o IIS 7 para rodar aplicações ASP que utilizam banco de dados. O tutorial é mais completo do que eu precisava - muitas das configurações eu já havia feito - mas me apresentou um parâmetro que eu desconhecia e que resolveu o problema de incompatibilidade. O parâmetro é o Habilitar aplicativos de 32 Bits, como mostra a imagem abaixo:
Configuração do IIS 7
O valor desse parâmetro é FALSE por padrão, significando que o site só executará aplicações de 64 bits. Para mudar-lhe o valor, acesse o Gerenciador do IIS 7, clique em Pool de Aplicativos e selecione o site que deseja configurar. Depois, escolha a ação Configurações Avançadas e habilite aplicativos 32 bits no site selecionando TRUE como valor da propriedade. Apenas o site em questão será afetado.

Com essa configuração ativa, o IIS passou a entender todo o site como sendo uma aplicação 32 bits de forma que mesmo a biblioteca original pode ser carregada sem problemas. Assim, foi possível continuar usando todo o site feito em ASP normal e também a biblioteca COM sem modificações - apenas reconfigurando o servidor.

24 de maio de 2010

Transportando arquivos muito grandes

Nas versões do Windows que ainda possuem suporte hoje em dia – XP, Vista, Seven e suas variações – os discos rígidos (HDs) são quase sempre formatados usando a estrutura de pastas chamada NTFS (NT File System, ou seja, sistema de arquivos do Windows NT). No entanto, muitos dos pen drives e HDs externos ainda vêm formatados usando FAT32, sistema de arquivos usado pelas versões anteriores do Windows (95 e 98) mas ainda reconhecidos nas versões mais recentes.

Ambos os formatos servem ao mesmo propósito, que é organizar o acesso aos arquivos que existem num disco. O que os difere é como é feita essa organização e os recursos adicionais proporcionados por cada formato. O NTFS é mais robusto, tendo sido projetado para, entre outras coisas, comportar informações de segurança mais refinadas, permitindo que sejam inseridas configurações de acesso no nível de pasta e até de arquivo, de modo que apenas usuários cujas credenciais atendam tais configurações é que podem acessar uma pasta ou arquivo. O FAT32, por ser mais antigo, é mais simples e não permite essa distinção.

Outra diferença crucial entre os dois formatos é o tamanho máximo que um arquivo pode alcançar. Enquanto no FAT32 o tamanho máximo não deve passar de 4 GB (gigabytes), a implementação atual do NTFS aceita arquivos com até 16 TB (terabytes).

Por essa razão, se você possui um arquivo de imagem de um DVD (4.5 GB) , um backup de um banco de dados de um Cliente ou outro arquivo qualquer cujo tamanho ultrapasse 4 GB, será um problema copiá-lo de um sistema com NTFS para um pen drive formatado com FAT32. Embora seja permitido que se formate um pen drive ou HD externo com NTFS, nem sempre isso é desejado.

Há uma ferramenta bastante simples e eficaz para resolver este tipo de impasse. Trata-se do HJ-Split, uma aplicação gratuita que não necessita instalação e que se propõe a fracionar um arquivo qualquer, criando arquivos menores que depois podem ser reagrupados para formar o arquivo original novamente.

A interface inicial da aplicação apresenta 4 funções representadas por botões: Split (fracionar), Join (juntar), Compare (comparar dois arquivos) e Checksum (calcular identificação para validar um arquivo). A reprodução abaixo é a tela apresentada quando se solicita a função Split para quebrar um arquivo em arquivos menores:
HJ-Split
Para usar esta função, basta informar o arquivo original, o tamanho máximo que cada fragmento gerado deverá ter e pressionar o botão Start. Na mesma pasta onde está o arquivo original serão gerados tantos fragmentos de arquivos quantos forem necessários, respeitando-se o tamanho máximo estipulado para cada parte. Em cada fragmento será acrescentado um número sequencial (começando com 001) indicando a posição do fragmento dentro do arquivo original. Esses arquivos menores agora podem ser copiados para o pen drive sem problemas.

Quando precisar do arquivo completo novamente, use a função Join do HJ-Split. Nessa função, o botão Input File permite que se localize o primeiro fragmento do conjunto que foi gerado com a função Split – aquele arquivo com o 001 acrescentado ao nome. É importante que todos os fragmentos estejam na mesma pasta para que o HJ-Split os encontre. Informe também a pasta de destino, isto é, o local onde será recriado o arquivo original completo.

No endereço http://www.freebyte.com/hjsplit há links para download de versões para diversos Sistemas Operacionais (Linux, Mac, Windows, etc.).

19 de maio de 2010

A tecnologia Flash encurralada ?

Desde que foram anunciados os novos recursos que seriam incorporados ao padrão do HTML na sua versão 5, vem aumentando o calor da discussão a respeito de tecnologias para criação de aplicações ricas para Internet (RIA), notadamente o Adobe Flash devido a sua ampla utilização.

O que antes era apenas uma questão de percepção dos especialistas ganhou peso de guerra declarada em abril, quando a Apple modificou o acordo de licenciamento para desenvolvedores de aplicações do IPhone 4.0, incluindo termos que na prática proibem o desenvolvimento em Flash nesse ambiente. Antes desse evento, a Apple já sinalizava descontentamento com o Flash ao não implementá-lo nos navegadores de seus gadgets (IPhones, IPods e o recente IPad).

Algumas das alegações da Apple fazem bastante sentido, como o fato do Flash não ter sido projetado para equipamentos touch screen, cada vez mais comuns hoje. A principal queixa, no entanto, é de que se trata de uma tecnologia proprietária - apesar da Apple não ficar atrás quando o assunto é tecnologia proprietária - e que a internet deveria ser território dominado por padrões abertos (HTML, CSS e JavaScript, por exemplo). Veja aqui reportagem da MacWorld sobre a carta aberta publicada por Steve Jobs.

E a Apple não está sozinha. A Microsoft (que tem um concorrente do Flash, o Silverlight) fez coro com as opiniões da Apple, afirmando que o futuro da internet está no HTML5 e que o Flash é parte da cultura da rede hoje mas não terá necessariamente o mesmo espaço no futuro (veja aqui). Outros fabricantes também estão se posicionando, dando seu apoio ao HTML5 de uma forma ou outra, como é o caso do Scridbd, uma rede social de leitura (aqui).

A Adobe, no entanto, continua acreditando firmemente em sua tecnologia. Além de defendê-la rebatendo as críticas da Apple, a Adobe anunciou que abandonaria o desenvolvimento de uma ferramenta que permitiria aos aparelhos da Apple executarem animações em Flash. Eles também vêm desde o começo do ano lançando novas versões de seus produtos baseados em Flash, como é o caso do Flash Builder 4 lançado em março desse ano. É uma ferramenta visual para desenvolver aplicações ricas para internet usando o Flash; veja uma avaliação da ferramenta neste link (em inglês).

Outra ferramenta, essa voltada para designers, é o Flash Catalyst CS5, no mercado desde abril. Ela proporciona aos designers criarem interfaces gráficas para aplicações Web e projetarem interatividade sem que seja necessário escrever código de programação. Mais informações podem ser encontradas aqui.

Como já disse em outra ocasião, pode até ser que o Flash venha a cair mas isso deve demorar um bocado para acontecer visto que há muitos desenvolvedores que dominam e gostam dele. Também há ainda muita coisa boa feita com essa tecnologia - vídeos, infográficos, animações, aplicações, jogos, etc. - e abandonar isso ou reescrevê-las com outra linguagem custa e leva tempo.

14 de maio de 2010

Criando componentes com Delphi - parte IV

Para completar o componente que iniciei no último post faltaram alguns detalhes. Ao criar o componente, usei como classe base o TWinControl, que introduz algumas propriedades úteis, tais como as relacionadas a posicionamento e tamanho.

No entanto, eu preciso de outras que não estão publicadas no Object Inspector - é o caso do Enabled e do Visible. Como elas já existem, publicá-las no Object Inspector é uma questão de redeclará-las na classe no nosso componente:
TWSelecionaArq = class(TWinControl)
{ ... }
published
property Constraints;
property Enabled;
property Visible;
{ ... }
Como já afirmei em outro post, a secção published funciona exatamente igual ao public, isto é, tudo que está nessa área pode ser acessado sem restrições por qualquer outra área do programa. A única diferença é que o que aparece sob o published é também exibido no Object Inspector na fase de desenvolvimento.

Mas nosso componente é composto por outros (uma caixa de edição e um botão) e o valor dessas propriedades são aplicadas apenas no componente principal, não sendo repassadas aos internos. Desse modo, os componentes internos não refletem visualmente sua condição de desabilitados, por exemplo. Para resolver isso no caso do Enabled, basta sobrescrever o método SetEnabled, que é virtual na classe TControl e ajustar manualmente os componentes internos:
procedure TWSelecionaArq.SetEnabled(Value: Boolean);
begin
inherited;

_Edit.Enabled := Value;
_Btn.Enabled := Value;
end;
Falta criar as propriedades específicas do nosso componente. Por exemplo, o nome padrão para o arquivo - que é o texto que deve aparecer na caixa de edição quando a tela é aberta. É através dela também que conseguiremos saber o nome do arquivo informado pelo usuário:
TWSelecionaArq = class(TWinControl)
{ ... }
function GetNomeArquivo : String;
procedure SetNomeArquivo (ANome: String);

published
property NomeArquivo : String read GetNomeArquivo write SetNomeArquivo;
{ ... }
function TWSelecionaArq.GetNomeArquivo : String;
begin
Result := '';
if _Edit <> Nil then
Result := _Edit.Text;
end;

procedure TWSelecionaArq.SetNomeArquivo (ANome: String);
begin
if _Edit <> Nil then
_Edit.Text := ANome;
end;
Veja que a propriedade foi inserida na área published para aparecer no Object Inspector. Note também que não há um membro interno para armazenar o valor dela - o próprio Text da caixa de edição interna ao componente fica responsável por isso.

Também teremos uma propriedade para indicar se o arquivo informado deve ou não existir, disparando uma validação quando o componente perder o foco. A declaração da propriedade em si é simples, apenas lê e grava o valor boolean informado pelo programador. O interesse aqui é outro : planejar um novo evento no componente, que levará em conta a opção do programador em relação à existência prévia do arquivo. O mesmo ponto do programa dará ao programador ainda a chance de fazer sua própria validação, através da resposta a um evento.

Há uma mensagem do Windows chamada WM_KILLFOCUS que é enviada para uma janela (componentes TWinControl também são janelas) quando ela está prestes a perder o foco do teclado. Portanto, vou interceptar essa mensagem e introduzir todo o tratamento de validação nesse ponto. O detalhe é que o componente principal não recebe foco, de modo que o tratamento terá que ser feito com a caixa de edição. A chave pra resolver esse problema é a propriedade WindowProc, existente em todos os controles. Ela armazena um método que é ativado pra tratar todas as mensagens recebidas pelo controle.
TWValidarArquivo = procedure (Sender: TObject;var AValido: Boolean) of object;

TWSelecionaArq = class(TWinControl)
private
_OnValidarArquivo : TWValidarArquivo;
_OldEditProc : TWndMethod;

protected
procedure EditWinProc(var Msg: TMessage);

published
property OnValidarArquivo : TWValidarArquivo read _OnValidarArquivo write _OnValidarArquivo;
end;

{ ... }

constructor TWSelecionaArq.Create(AOwner: TComponent);
begin
inherited;

_OnValidarArquivo := Nil;

_Edit := TEdit.Create (Self);
_Edit.Parent := Self;
_OldEditProc := _Edit.WindowProc;
_Edit.WindowProc := EditWinProc;

{ ... }

procedure TWSelecionaArq.EditWinProc(var Msg: TMessage);
var lValido : boolean;
begin
if Assigned (_OldEditProc) then
_OldEditProc (Msg);

if Msg.Msg = WM_KILLFOCUS then begin
lValido := true;
if DeveExistir and (Trim (NomeArquivo) <> '') then begin
lValido := FileExists(Trim (NomeArquivo));
if not lValido then
MessageBox (Handle, PChar ('Arquivo não existe : ' + NomeArquivo),
'Erro', MB_OK or MB_ICONERROR);

end;

if Assigned (_OnValidarArquivo) and lValido then
_OnValidarArquivo (Self, lValido);

{ Qualquer erro força o foco de volta ao Edit }
if (not lValido) And _Edit.CanFocus then
_Edit.Setfocus;
end;
end;
Duas coisas em especial chamam a atenção nos trechos de código acima. Primeiro, foi criado um novo tipo de dado para representar o evento de validação e uma propriedade com esse tipo foi inserida na área published para que o evento possa ser tratado pelo programador através do Object Inspector. Veja em EditWinProc que essa propriedade representa uma função mas ela só é chamada se o programador associou-lhe um código, isto é, se ele providenciou uma resposta ao evento através do Object Inspector. O programador poderá fazer isso separadamente cada vez que incluir o componente numa tela.

O segundo fato é a forma com que tratamos a propriedade WindowProc. É possível que ela já esteja alimentada; então, antes de sobrescrever o valor dela eu salvo o valor anterior para que a rotina não se perca. Por isso, as primeiras linhas do EditWinProc fazem a chamada da função que existia previamente, se for necessário.

Um tratamento semelhante ao do WindowProc é aplicado ao evento de clique no botão interno. A diferença aqui é que esse evento já existe e precisa apenas ser respondido manualmente:
No construtor :
constructor TWSelecionaArq.Create(AOwner: TComponent);
begin
{ ... }
_Btn := TBitBtn.Create (Self);
_Btn.Parent := Self;
_Btn.OnClick := SelecionarArquivo;

{ ... }

{ Resposta interna do evento de clique: }
procedure TWSelecionaArq.SelecionarArquivo(Sender: TObject);
begin
_OpenDialog.FileName := NomeArquivo;
if _OpenDialog.Execute then begin
NomeArquivo := _OpenDialog.FileName;
if _Edit.CanFocus then
_Edit.SetFocus;
end;
end;
Ao contrário do evento de validação, o OnClick para abrir o diálogo de seleção de arquivo é exclusivamente interno, não podendo ser manipulado pelo programador que está usando o componente.

O download dos fontes desse exemplo pode ser feito clicando aqui. O exemplo foi construído com Delphi 2005.

Mais Informações
Criando componentes com Delphi - parte I, parte II e parte III.

6 de maio de 2010

Criando componentes com Delphi - parte III

Uma técnica também bastante utilizada para criar novos componentes é fazer uma combinação de outros já existentes e publicar o conjunto como se fosse um só, selecionando propriedades e eventos relevantes para incluir no Object Inspector do Delphi e escondendo outros conforme a necessidade.

Na ABC71, por exemplo, temos um componente "lookup" cujo objetivo é permitir ao usuário digitar um valor qualquer - se ele souber de cabeça um código, a digitação se torna mais rápida - ou escolher um dos valores válidos através da consulta a uma lista. O significado do código é uma das propriedades do componente, podendo ser Cliente, Produtos, Pedidos, Títulos, etc. É exibido ao usuário uma caixa de edição para digitação do código, um botão para acionar a lista (quando for preciso) e outra caixa que exibe uma descrição para o código informado na primeira caixa.

Vou montar um componente mais simples para ilustrar essa técnica, colocando uma caixa de edição para o usuário informar um caminho completo de arquivo e um botão caso ele prefira selecionar visualmente o arquivo desejado. Ao sair da caixa de edição, o programador terá a oportunidade de validar o conteúdo informado através de um evento que criaremos. Algumas propriedades a serem publicadas: o texto inicial para a caixa de edição; se o arquivo informado deve existir ou não; quais máscaras de arquivos poderão ser aceitas; etc.

A primeira decisão a ser tomada é em relação à qual classe deve ser usada como base para o componente. Dado que ele permitirá interações com o usuário, devemo usar TControl ou uma de suas heranças, como o TWinControl. A diferença entre ambos é que o TWinControl encapsula a funcionalidade de uma janela, ou seja, pode, entre outras coisas, receber o foco, receber entrada de dados através do mouse ou do teclado, tratar mensagens do Windows (WM_PAINT, por exemplo). Nosso componente será composto por uma caixa de edição e um botão, que já têm todos esses recursos; por isso, o componente em si não precisará implementá-los. No entanto, esses componentes só são visíveis quando a propriedade Parent é informada - obrigatoriamente um TWinControl. Se quisermos que nosso componente seja o Parent dos que forem adicionados internamente, teremos que criá-lo como um TWinControl.
type
TWSelecionaArq = class(TWinControl)
private
_Edit : TEdit;
_Btn : TBitBtn;
_OpenDialog: TOpenDialog;

{ ... }

constructor TWSelecionaArq.Create(AOwner: TComponent);
begin
inherited;

_Edit := TEdit.Create (Self);
_Edit.Parent := Self;

_Btn := TBitBtn.Create (Self);
_Btn.Parent := Self;
_Btn.OnClick := SelecionarArquivo;
_Btn.SetBounds (0, 0, 28, 25);

{ ... }

_OpenDialog := TOpenDialog.Create (Self);

SetBounds (Left, Top, 200, 0);
{ ... }
O excerto acima mostra ainda a declaração dos membros internos da classe que representam o botão e a caixa de edição. Também mostra um TOpenDialog que será usado para exibir a caixa de diálogo que permitirá ao usuário final selecionar um arquivo.

No trecho relativo ao construtor, os componentes internos são criados e o Parent de cada um é ajustado para nosso próprio componente (self), responsável por exibí-los. Note que nada é feito para posicionar corretamente os componentes internos. Isto mais uma vez será feito pela função SetBounds, que é chamada sempre que o posicionamento ou tamanho de nosso componente é alterado :

procedure TWSelecionaArq.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
{ Altura do componente é a mesma do botão }
AHeight := _Btn.Height + 2;
{ Comprimento mínimo do componente }
if AWidth < 200 then
AWidth := 200;

{ posiciona o edit e o botão }
_Edit.Width := AWidth - _Btn.Width;
_Btn.Left := _Edit.Width + 1;

{ ajuste dos tamanhos do componente }
inherited SetBounds(ALeft, ATop, AWidth, AHeight);
end;

Com o esqueleto do componente pronto, podemos acrescentar declarações para as propriedades e eventos desejados. Volto no próximo post com esses detalhes da implementação.

Mais Informações
Criando componentes com Delphi - parte I e parte II