23 de novembro de 2009

Criando Serviços do Windows - parte 1

Um Serviço do Windows é um tipo especial de programa projetado para executar sob demanda uma tarefa ou um conjunto de tarefas correlacionadas - o serviço fornecido. A característica mais marcante dos Serviços é o fato de que eles não possuem interface gráfica, isto é, não se espera a intervenção direta de um usuário. Por isso, o funcionamente padrão deles é ficar em execução indeterminadamente, aguardando solicitações de outros programas ou outro evento qualquer para o qual tenha sido programado.

Assim, o objetivo desse tipo de programa é fornecer um "serviço" especializado bem determinado, de modo que os outros programas podem se ater na sua própria execução e delegar ao Serviço aquelas tarefas que ele disponibiliza. Veja que essa característica torna o Serviço uma espécie de componente, capaz de atender diversos programas diferentes, mesmo que esses outros programas não tenham sido criados por você. Os programas que queiram se beneficiar do Serviço, então, terão apenas que saber como acessá-lo, isto é, qual é a sua interface. Alguns exemplos de situações onde se pode aplicar esse conceito são listados a seguir : banco de dados, Web Server, Agendador de Tarefas, Assinatura digital de documentos, etc.

De forma simplificada, podemos classificar os Serviço em duas categorias básicas, de acordo com a técnica utilizada para ativar a execução de uma tarefa. Há os que são acionados deliberadamente por outro programa e que, portanto, apenas reagem a uma solicitação, executando imediatamente a tarefa requisitada e retornando algum resultado (quando for o caso). Se encaixam nessa categoria serviços para atender ações em bancos de dados ou num Web Server ou ainda um serviço específico para assinar documentos digitalmente. A outra categoria são os Serviços que monitoram a ocorrência de um evento e executam algum processamento quando o evento é detectado. Na ABC71, por exemplo, temos um Serviço que executa processos e relatórios agendados em qualquer das estações com o Client do ERP. Ele faz isso monitorando os agendamentos registrados em uma tabela no banco de dados e executando os processos em fila quando chega a data e hora configuradas. Da forma como está exposto aqui, um mesmo serviço pode ser projetado para funcionar sem problemas até mesmo com ambas as maneiras, dependendo da situação.

Uma outra propriedade importante diz respeito à inicialização do Serviço: ele pode ser configurado para iniciar junto com o Windows, sem que seja necessário fazer login no Windows. Isso é particularmente útil quando o Serviço é instalado num servidor para atender às demandas através de uma rede, pois neste cenário o Serviço deve estar disponível independentemente se um login foi feito no Windows do servidor ou não.

Mas, se não há um login feito, quais políticas de segurança são aplicadas ao programa ? Isto é, como é determinado o que o Serviço pode ou não pode acessar ? Quais arquivos, entradas no Registry ou outro recurso qualquer do Windows ? Uma das configurações permitidas num Serviço é justamente um nome de usuário e sua senha, de modo que as permissões atribuídas a esse usuário é que determinam o que um Serviço pode ou não fazer. É permitido também escolher um dos 3 usuários padrões, criados e mantidos pelo Windows (Local System, LocalService ou Network Service), cada um com seu próprio conjunto de credenciais. Veja aqui uma explicação bem curta de cada uma dessas contas.

O Windows possui uma API própria para a criação e manipulação de Serviços. No entanto, o RAD Studio (Delphi e C++ Builder) e o Visual Studio (C#, Visual Basic) encapsulam em classes uma solução mais prática, que não exige acesso direto às APIs. Num próximo post, mostro a classe publicada no Delphi/C++ Builder e monto um exemplo simples de Serviço.

19 de novembro de 2009

Customizando a aparência de um Grid em Delphi - parte 3

Há alguns dias, tratei num post a customização da aparência de um Grid, onde mostrei alguns conceitos básicos do objeto Canvas do Delphi e C++ Builder, usado para fazer a customização num StringGrid. Os mesmos conceitos são válidos para os DBGrids mas, como há algumas diferenças, vou abordar aqui a customização desse tipo de grid.

A diferença mais importante está nos parâmetros que são passados para o evento de pintura de uma célula:
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState);

No caso do DBGrid, ao invés de nos informar a linha e a coluna que se está desenhando, o evento nos dá o campo atual da query, representado pelo parâmetro Field. Para ficar mais claro o que está acontecendo, a ordem de desenho das células é a seguinte: primeiro, um registro da query é posicionado; depois, o evento de desenho é disparado para cada uma das colunas (os campos ou Fields) selecionados na query. Novos registros são posicionados até que a parte visível do grid esteja completa ou que o fim do result set seja atingido. Com isso, cada novo registro é desenhado numa nova linha e, obviamente, cada campo ocupa uma coluna.

O trecho de código abaixo pinta o valor de um Field numérico de azul (quando positivo) ou vermelho (quando negativo).
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; Field: TField; State: TGridDrawState);
var yCalc, xCalc : integer;
Texto : String;
lCanvas : TCanvas;
DBGrid : TDBGrid;
begin
{ Salva em variáveis locais para facilitar a clareza }
DBGrid := Sender As TDBGrid;
lCanvas := DBGrid.Canvas;

if (not (gdFixed in State) ) and
(Field.FieldName = 'VALOR')
then begin
{ Formata o valor para apresentação }
texto := FormatFloat ('###,##0.00', Field.AsFloat);

{ Cor de fundo para célula depende se está selecionada }
if gdSelected in State then
lCanvas.Brush.Color := clHighlight
else
lCanvas.Brush.Color := DBGrid.Color;

{ Cor do fonte para o campo, de acordo com o valor }
if Field.AsFloat >= 0 then
lCanvas.Font.Color := clBlue
else
lCanvas.Font.Color := clRed;

{ Preenche com a cor de fundo }
lCanvas.FillRect(Rect);

{ Calcula posição para centralizar o texto na vertical }
yCalc := lCanvas.TextHeight (texto);
yCalc := Rect.Top + (Rect.Bottom - Rect.Top - yCalc) div 2;

{ calcula posição para alinha valor numérico à direita }
xCalc := lCanvas.TextWidth (texto);
xCalc := Rect.Right - xCalc - 3;

lCanvas.TextRect (Rect, xCalc, yCalc, texto);
end
else
DBGrid.DefaultDrawDataCell (Rect, Field, State);
end;

Observe que este código precisa tratar apenas o Field que queremos - distinguido pelo nome do campo no banco de dados, ao contrário do código postado a respeito da aparência do StringGrid. Aqui, o tratamento para os demais fields e states é repassado para a função DefaultDrawDataCell do Grid, que cuida de desenhar e formatar as células que não nos interessa customizar.

E é possível determinar o número da linha atual? Há uma propriedade no DataSet chamada RecNo que indica o número sequencial do registro que está atualmente posicionado. Como o TField está associado a um DataSet (query ou table), posso usar o RecNo para, por exemplo, pintar as linhas do grid com cores alternadas, dando o efeito de zebrado:
{ Cor de fundo para as linhas }
if (Field.DataSet.RecNo mod 2) = 0 then
lCanvas.Brush.Color := clSilver
else
lCanvas.Brush.Color := DBGrid.Color;

A diferença entre os eventos OnDrawDataCell e OnDrawColumnCell é que esse segundo recebe um parâmetro com as configurações da coluna do Grid, isto é, um TColumn) ao invés do TField. Como uma das propriedades do TColumn é justamente o Field associado, adaptar o código mostrado neste post é simples. E, de acordo com o help do Delphi, é preferível usar a versão com o TColumn pois a outra é considerada obsoleta.

Embora as funções e propriedades do objeto Canvas tenha sido apresentado num contexto específico nessa série de posts sobre aparência de Grids, os conceitos podem ser usados para trabalhar em qualquer contexto onde haja um Canvas.

Mais Informações
Customizando a aparência de um Grid em Delphi - parte 1 e parte 2

15 de novembro de 2009

Google anuncia novos projetos

Parece que o pessoal do Google não consegue parar de se mexer... Além do estardalhaço que eles vêm fazendo para divulgar o Google Wave (que ainda não foi oficialmente lançado), esta semana eles fizerem mais dois anúncios relacionados a novas linhas de pesquisa em que a empresa está se envolvendo. Na verdade, o que eles anunciaram foram os primeiros resultados dessas novas linhas.

O primeiro anúncio foi que eles estão construindo uma nova linguagem de programação, chamada GO. Mas porque criar uma nova linguagem de programação se já há uma infinidade delas por aí, com os mais variados propósitos e uma vasta gama de características diferentes ?

A proposta, segundo o site oficial do GO, é resolver duas frustrações que os desenvolvedores da linguagem têm. Para eles, as linguagens existentes hoje cujo código resulta em programas com melhor performance - como C/C++ - têm um processo de compilação bastante lento. Dentre as razões alegadas para essa lentidão, eles citam a existência de arquivos header que contêm as pré definições necessárias para se usar funções e classes num programa e a tipologia de dados do C/C++. Ambas as questões são resolvidas, com a eliminação das pré definições e com a introdução de tipos de dados dinâmicos, no mesmo estilo dos tipos existentes no JavaScript. Para finalizar, a nova linguagem não possui a ideia de ponteiros no sentido entendido pelo C/C++ e outras, o que permitiu também a criação de um garbage collector mais eficiente. Isto é, o gerenciamento da memória do GO é automatizado como no Java e no .NET.

A outra frustração que a linguagem procura resolver é o fato de que o hardware dos computadores tem evoluido muito, em especial em relação ao número de processadores, enquanto as linguagens de programação não têm acompanhado tal evolução. Neste sentido, a sintaxe do GO procura facilitar a criação de programas que tirem o máximo desse tipo de arquitetura, de modo que o resultado são programas pequenos e leves com capacidade de trocar informações entre si e sincronizar suas execuções de forma nativa, isto é, tudo contemplado na própria sintaxe da linguagem.

Embora os engenheiros do Google estejam usando internamente esta ferramenta, o site do GO alerta que ela ainda não é madura o suficiente pra uso em projetos comerciais e que a divulgação da linguagem neste ponto tem justamente o objetivo de obter sugestões da comunidade de desenvolvedores para complementá-la e testá-la.

A segunda linha de pesquisas que eles revelaram diz respeito à comunicação entre os navegadores de internet (os browsers) e os servidores da Internet. O mecanismo existente hoje é o protocolo HTTP, estabelecido como padrão nos primórdios da internet comercial. A pesquisa do Google tenta minimizar alguns gargalos decorrentes da forma como esse protocolo permite a um navegador de internet receber certos tipos de informação do servidor Web e como o servidor as disponibiliza. São recursos como streams de áudio e vídeo e a compactação de dados de controle do HTTP, além do modo como o servidor prioriza as requisições feitas pelo navegador.

O novo protocolo foi chamado de SPDY (speedy) e, segundo o blog dos pesquisadores, os testes preliminares em laboratório foram animadores, resultando em cargas de páginas até 55% mais rápidas. Para o teste, foi usado um servidor de Web criado para se comunicar usando o SPDY e uma versão modificada do Chome, compatibilizado para entender o novo protocolo. Novamente, a pesquisa ainda não gerou um produto e a fase atual do desenho no novo protocolo é de abertura para que a comunidade de desenvolvedores possa conhecê-lo e dar sugestões para melhorá-lo.

Veja neste link a notícia publicada no site IDG Now! sobre o SPDY.

10 de novembro de 2009

Customizando a aparência de um Grid em Delphi - parte 2

Continuando o post anterior, sobre como customizar a aparência de grids em Delphi, mostro aqui exemplos práticos dessa customização.

Os parâmetros aceitos pelo evento OnDrawCell num StringGrid são mostrados abaixo.
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState);

Este evento é disparado uma vez para cada célula do Grid que precisar ser desenhada. Além do tradicional Sender (que representa o próprio grid que está gerando o evento), o evento também trás a linha e coluna que identifica qual é a célula desenhada. O parâmetro Rect indica um retângulo dentro do Canvas cujas coordenadas delimitam a célula a ser desenhada, isto é, é o espaço do Canvas reservado para o desenho da Célula indica pelos parâmetros ACol, ARow. O último parâmetro - State - é um conjunto de indicadores que sinalizam condições especiais para o desenho da célula. São estados tais como se a célula está selecionada ou não, se está com o foco para entrada de dados e se está numa das linhas ou colunas fixas do Grid.

Antes de prosseguir com a inclusão de código como resposta a esse evento, vou modificar a propriedade DefaultDrawing do StringGrid para False. Isso avisa a VCL que nós estamos assumindo o controle do desenho das células e que a VCL não deve executar as suas rotinas internas associadas à exibição dos dados de cada célula. Isto dito, segue um exemplo de resposta padrão para modificar as cores apresentadas no texto de um stringGrid:
procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
Rect: TRect; State: TGridDrawState);
var yCalc : integer;
Texto : String;
lCanvas : TCanvas;
begin
{ Salva em variáveis locais por questão de clareza do código }
texto := StringGrid1.Cells[ACol, ARow];
lCanvas := StringGrid1.Canvas;

if gdFixed in State then begin
{ Cor do fonte e de fundo para linhas e colunas fixas }
lCanvas.Font.Color := clBlack;
lCanvas.Brush.Color := StringGrid1.FixedColor;
end else
if gdSelected in State then begin
{ Cor do fonte e de fundo para linhas e colunas selecionadas }
lCanvas.Font.Color := clWhite;
lCanvas.Brush.Color := clHighlight;
end
else begin
{ Cor do fonte e de fundo para linhas e colunas com dados }
if ACol mod 2 = 0 then
lCanvas.Font.Color := clBlue { azul nas colunas pares}
else
lCanvas.Font.Color := clRed; { vermelho nas ímpares }
lCanvas.Brush.Color := StringGrid1.Color;
end;

{ Preenche com a cor de fundo }
lCanvas.FillRect(Rect);

{ Calcula posição para centralizar o texto na vertical }
yCalc := lCanvas.TextHeight (texto);
yCalc := Rect.Top + (Rect.Bottom - Rect.Top - yCalc) div 2;
lCanvas.TextRect (Rect, Rect.Left + 3, yCalc, texto);
end;

Seguem algumas considerações sobre o trecho acima. A primeira coisa a observar é que grande parte do código é dedicada a configurar as propriedades do Canvas - o Brush para pintar a cor de fundo da célula e a Font para determinar a cor do texto. Algumas dessas configurações consideram o "Estado" da célula, isto é, se ela está selecionada ou se é parte da linha e coluna fixa. Poderia ainda ter levado em consideração também qual é a célula que está com foco e dar-lhe algum destaque.

O outro objeto disponível (Pen) não é alterado neste exemplo mas poderia ter sido caso quiséssemos desenhar linhas com características distintas. Isso inclui a borda da célula. Repare apenas que o Rect passado no parâmetro não considera a borda da célula, de modo que teríamos que expandir manualmente este retângulo caso quiséssemos aplicar uma cor ou espessura diferente à borda.

Uma coisa importante a ressaltar é que o Grid em si possui uma propriedade Font cuja configuração é usada como padrão para desenhar os textos, ou seja, o que configuramos nela é repassado para o Canvas antes da pintura do Grid iniciar. Modificar a Font do Grid, então, dispara a pintura do componente como um todo - e não de uma célula específica. Por isso, alteramos diretamente a fonte do Canvas no evento de pintura da célula, evitando que novos eventos de pintura sejam gerados eternamente.

No exemplo, as colunas pares são escritas em azul e as ímpares em vermelho. Claro que faria mais sentido interpretar o texto da célula e aplicar-lher cores mais adequadamente.

A linha de código lCanvas.FillRect(Rect) usa as configurações feitas no Brush para pintar o fundo da célula antes de aplicarmos o texto associado a ela. O comando lCanvas.TextRect (Rect, Rect.Left + 3, yCalc, texto) usa as configurações de fonte do Canvas para desenhar o texto da célula centralizado na vertical em relação ao retângulo reservado para a célula.