19 de dezembro de 2012

Personalizando a apresentação de mapas com o Google Maps

Na maioria das vezes, as informações apresentadas num mapa produzido através da API do Google Maps são suficientes para que o usuário rapidamente consiga ver um endereço e seu entorno ou a rota a ser seguida para ir de um ponto a outro. Em certas situações, no entanto, pode ser necessário adicionar ao mapa outras informações para melhor transmitir a ideia correta que se deseja.

Esse tipo de necessidade foi exemplificado quando falei sobre o uso de marcações num mapa para apontar locais de interesse nas proximidades de um determinado endereço. O exemplo pode ser encontrado neste link.

A API do Google Maps nos permite acrescentar informações extras aos mapas através do conceito de camadas que são sobrepostas ao mapa, chamadas de overlays. Para isso, a API disponibiliza um conjunto de classes javascript. Além da já citada Marker, há classes para traçar linhas, delimitar áreas circulares ou retangulares e até mesmo para projetar uma imagem personalizada sobre uma área do mapa.

O overlay mais simples é o Polyline, usado para traçar linhas por sobre o mapa. O quadro abaixo traz código javascript que une com uma linha reta 2 pontos do mapa: a sede da ABC71 e a praça da Sé, em São Paulo:
var mapOptions = {zoom: 10,mapTypeId: google.maps.MapTypeId.ROADMAP};
map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);
geocoder = new google.maps.Geocoder();

var endDe = new google.maps.LatLng(-23.566146,-46.652579);
var endAte = new google.maps.LatLng(-23.550563,-46.633101);
var request = { location: endDe, region: 'BR'};

geocoder.geocode (request, function(response, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.fitBounds (new google.maps.LatLngBounds(endDe, endAte));
/* ... */
var pontos = new google.maps.MVCArray ();
pontos.push (endDe);
pontos.push (endAte);

var rect = new google.maps.Polyline ({
path: pontos,
map:map,
strokeWeight: 5,
strokeColor: 'blue',
visible: true
});
}
});
Após as inicializações de praxe para o ambiente do Maps, o código solicita a geocodificação do endereço da ABC71 (na verdade, sua latitude/longitude).
Em resposta à conclusão da geocodificação, isto é, assim que o mapa com o endereço solicitado está pronto, eu crio uma lista MVCArray e adiciono a ela os dois pontos que definem a reta desejada.

Em seguida, instancio a classe Polyline propriamente dita, fornecendo-lhe a lista de pontos. Também indico o mapa onde a linha será traçada (map), a espessura da linha em pixels (strokeWeight) e a cor dela (strokeColor). As cores podem ser informadas tanto pelo nome HTML quanto pela notação RGB hexadecimal.

Posso, por exemplo, demarcar um bairro inteiro com esse recurso? Sim, mas você terá que montar a lista de pontos o mais completa possível para que o desenho resultante tenha uma precisão adequada. O Polyline produz linhas abertas; para traçar linhas fechadas com uma quantidade arbitrária de pontos use a classe Polygon.

A função fitBounds, usada no início da resposta, apenas aplica um nível de zoom apropriado para garantir que ambos os endereços sejam exibidos num único quadro, sem que o usuário precise interagir com ele.

O código analisado produz o mapa a seguir:
Imagine agora o caso de uma pizzaria que queira divulgar num mapa o raio de ação onde ela faz entregas. O overlay produzido pela classe Circle permite dar destaque a uma área circular, dado um centro e um raio em metros. Veja o exemplo:
var endDe = new google.maps.LatLng(-23.566146,-46.652579);
var request = { location: endDe, region: 'BR'};

geocoder.geocode (request, function(response, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(endDe);
/* ... */
var circleOpts = ({
map: map,
center: endDe,
editable: false,
fillColor: 'yellow',
fillOpacity: 0.4,
radius: 3500,
strokeColor: '#0099ff',
strokeWeight: 4,
visible: true });
var circle = new google.maps.Circle (circleOpts);
}
});
Como no caso da linha, para o círculo também valem as propriedades que indicam o mapa, a cor do traço e sua espessura. Mas, ao invés de uma lista de pontos, informamos apenas o centro; no código anterior, usei o próprio endereço da ABC71 para isso. A definição do círculo fica completa com a propriedade radius - o raio, fornecido em metros; no exemplo, o raio terá 3500 metros (ou 3,5 Km).

Outra configuração disponível diz respeito ao preenchimento da área delimitada pelo círculo. A propriedade fillColor determina a cor do preenchimento enquanto a fillOpacity controla o grau de transparência, sendo que o valor 1 significa totalmente opaco (o mapa sob o círculo não é visto) e o valor 0 suspende o preenchimento.

A propriedade editable, quando ativa, permite ao usuário redimensionar o círculo. Eu a desliguei pois ela não faz sentido no contexto do exemplo. O resultado é o mapa exibido no quadro abaixo:

Os mesmos conceitos valem para a classe Rectangle. Além disso, é permitido trabalhar com vários overlays num mesmo mapa, se for necessário. Os exemplos publicados nesse post podem ser acessados respectivamente nos seguintes links : Overlay com Linha e Overlay com Círculo.

23 de novembro de 2012

Integrando o Outlook em aplicações Delphi - Parte II

No último post, eu mostrei os conceitos básicos envolvidos na integração do Outlook com aplicações Delphi. O exemplo apresentado lá abordou a manipulação dos contatos cadastrados no Outlook bem como a criação de novos contatos. Neste post eu abordo o aspecto mais utilizado da ferramenta, que é lidar com envio e recebimento de emails.

Enviar um email com o Outlook de dentro de um programa Delphi é uma operação relativamente simples. Assim como no caso dos contatos, o ponto de partida é objeto Outlook.Application. Aqui, no entanto, não é necessário trabalhar com Namespaces. Basta criar uma instância de MailItem (este é o tipo de objeto do Outlook que representa uma nova mensagem), fornecer-lhe informações relevantes para o envio e, então, efetivamente enviá-la. O código a seguir exemplifica esses passos:
procedure TForm1.btnEnviarClick(Sender: TObject);
var OutlookApp: TOutlookApplication;
email : MailItem;
begin
OutlookApp := TOutlookApplication.Create(Nil);

email := OutlookApp.CreateItem(olMailItem) As MailItem;
email.Subject := 'Envio automático';
email.BodyFormat := olFormatHTML;
email.HTMLBody := 'Este email é um <b>teste</b> para envio <b><span style="color:maroon;">automático</span></b>.';
email.Importance := olImportanceNormal;

email.Recipients.Add('balaiotecnologico@gmail.com');
if (email.Recipients.ResolveAll) then
email.Send
else begin
Application.MessageBox('Um ou mais destinatários não puderam ser resolvidos.'#13+
'Reveja a informação e tente novamente.'
, 'Alerta', MB_ICONWARNING);
email.Display(true);
end;

OutlookApp.Disconnect;
OutlookApp.Free;
end;
Veja que a criação da instância da mensagem é feita diretamente no objeto que representa a aplicação Outlook. A função CreateItem usada para esse fim é genérica e permite criar qualquer item dentro do Outlook. Assim, o valor olMailItem passado como parâmetro garante que o que está sendo criado é uma nova mensagem.

Após a criação, eu formato a mensagem fornecendo valores para propriedades relevantes como o assunto (Subject), a prioridade da mensagem (Importance), o texto dela (Body ou HTMLBody, dependendo do formato escolhido) e os destinários que a receberão (Recipients).

Opcionalmente, o método ResolveAll da lista de destinatários é chamado para verificar se os endereços incluídos são válidos. Em caso negativo, a própria tela de envio de email do Outlook é apresentada para que o usuário faça as correções necessárias antes de enviar o email manualmente.

Uma vez que a verificação foi feita com sucesso, a função Send é chamada para efetivamente fazer o envio. Por padrão, uma cópia da mensagem é armazenada na pasta Sent Items. Se não quiser guardar a cópia, use a propriedade DeleteAfterSubmit, ajustando-a para true. É possível também indicar outra pasta para guardar a cópia, modificando-se o valor da propriedade SaveSentMessageFolder.

Repare que não informei quem é o remetente da mensagem. Normalmente, o Outlook é configurado para se logar automaticamente quando a aplicação é executada, fazendo com que o remetente seja este usuário que fez o logon. É possível forçar outro logon por código, usando a função Logon de forma explícita e passando a ela as credenciais a serem utilizadas.

O código finaliza desconectando do Outlook e liberando a memória que esta sendo usada por ele.

Neste exemplo, passei o valor olFormatHTML na propriedade BodyFormat para permitir a inserção de código HTML diretamente em HTMLBody, o que flexibiliza a formatação da mensagem.

Embora eu não tenha tratado no código, é possível também incluir anexos no email. Um MailItem possui a propriedade Attachments para conter a lista de anexos. Antes do envio, podemos adicionar um ou mais anexos:
email.Attachments.Add('c:\ERP\abc71.jpg', EmptyParam, EmptyParam, EmptyParam);
O primeiro parâmetro é o caminho completo do arquivo a ser anexado. Os demais parâmetros são opcionais; passei EmptyParam a eles para que a função assuma valores internos padronizados para eles.

Para encerrar esse tópico, falo de um problema recorrente pra quem automatiza envio de email com o Outlook: a inclusão da assinatura padrão do usuário no corpo da mensagem. Da forma como foi exposto até aqui, apenas o texto incluído em HTMLBody será enviado, sem qualquer decoração extra que o usuário tenha configurado - como a assinatura ou um papel de carta. Isso acontece porque esses recursos não são inerentes ao MailItem mas sim ao editor associado a ele. Para acionar o editor, precisamos apenas invocar o método GetInspector. Um inspector é o responsável pela edição do item atual e, ao invocar o método, todo o ambiente para edição é criado, incluindo a formatação padrão para o item - neste caso, um email.

Isso preencherá o HTMLBody com um HTML completo, englobando a assinatura e outras formatações. Então, teremos que localizar a tag BODY desse HTML para acrescentar nosso texto personalizado no local correto sem perder a formatação padrão. O quadro abaixo mostra como obter esse efeito:
var { ... }
email : MailItem;
insp : Inspector;
idx: integer;
begin
{ ... }
insp := email.GetInspector;

idx := Pos ('<body', email.HTMLBody);
idx := PosEx ('>', email.HTMLBody, idx);
email.HTMLBody := MidStr (email.HTMLBody, 1, idx) +
'Este email é um <b>teste</b> para envio <b><span style="color:maroon;">automático</span></b>.' +
MidStr (email.HTMLBody, idx+1, Length(email.HTMLBody));

if (email.Recipients.ResolveAll) then
email.Send;
{...}
Nessa abordagem, apesar de o editor ser criado e sua configuração padrão ser respeitada, ele não é exibido para usuário. Se for necessário, chame o Display do MailItem para exibí-lo.

Comentei no post anterior mas não custa lembrar: todas as operações descritas neste post exigem que o Outlook esteja instalado no computador que for executá-las.

6 de novembro de 2012

Integrando o Outlook em aplicações Delphi - Parte I

Embora eu não tenha números exatos, o Outlook é o programa de email mais utilizado em desktops. Parte desse sucesso se deve à ampla adoção da ferramenta no ambiente corporativo, já que ela também centraliza funções de calendário, acompanhamento de tarefas e agenda de contatos, permitindo compartilhar facilmente as informações.

Um outro aspecto positivo é que o Outlook é componentizado, o que abre caminho para criação de extensões e integrações com outros sistemas. Neste post, mostro como utilizar os recursos COM do Outlook para integrá-lo a uma aplicação Delphi.

Do mesmo modo que as outras aplicações do Microsoft Office (como o Excel), todos os recursos disponíveis do Outlook devem se acessados a partir de um objeto centralizador que representa a própria aplicação. Neste caso, precisamos obter uma instância de Outlook.Application, mapeada pelo Delphi na unit OutlookXP, junto com as demais interfaces existentes para se trabalhar com a ferramenta.

O quadro abaixo mostra um exemplo dos passos básicos necessários para interagir com o Outlook. O código apresentado percorre os contatos registrados na pasta local do Outlook, listando informações de cada um deles numa tabela:
procedure TForm1.btnListarContatosClick(Sender: TObject);
var i : integer;
OutlookApp: TOutlookApplication;
ns : NameSpace;
pasta: MAPIFolder;
contato : _ContactItem;
begin
StringGrid1.RowCount := 2;
OutlookApp := TOutlookApplication.Create(Nil);

ns := OutlookApp.GetNamespace('MAPI');
pasta := ns.GetDefaultFolder(olFolderContacts);

for i := 1 to pasta.Items.Count do
begin
if (i > 1) then
StringGrid1.RowCount := StringGrid1.RowCount + 1;

contato := pasta.Items.Item(i) As _ContactItem;

StringGrid1.Cells [1,i] := contato.FullName;
StringGrid1.Cells [2,i] := contato.Email1Address;
StringGrid1.Cells [3,i] := contato.EntryID;
StringGrid1.Cells [4,i] := contato.CompanyName;
StringGrid1.Cells [5,i] := contato.BusinessAddress;
end;

OutlookApp.Disconnect;
OutlookApp.Free;
end;
Vamos destrinchar esse código. Após criar uma instância para acessar a aplicação (classe TOutlookApplication), o trecho recupera um tipo de objeto denominado Namespace. Atualmente, apenas o namespace MAPI (API de Mensagens do Windows) está disponível, concentrando todos os registros, isto é, os emails, contatos, tarefas e entradas do calendário. Um Namespace é organizado em pastas dispostas hierarquicamente, podendo haver pastas dentro de pastas para armazenar os itens.

Há algumas formas de acessar as pastas do Outlook. A propriedade Folders do Namespace lista todas as pastas, incluindo as compartilhadas com outros usuários no servidor. As subpastas eventualmente existentes são acessíveis por meio de propriedade Folders própria. E como saber que tipo de item uma pasta contém? Em princípio, pode-se consultar a propriedade DefaultItemType para se ter uma ideia. Mas, certas pastas (como a DeletedItems) podem conter itens de tipos diferentes misturados. Neste caso, recomenda-se checar a propriedade Class do item ou fazer um cast dinâmico para o tipo desejado e ver se a conversão foi bem sucedida.

Outra forma de acesso é requerer ao Namespace a pasta padrão para um determinado tipo de item, como no código mostrado acima. Lá, uso GetDefaultFolder para recuperar exclusivamente a pasta que armazena meus contatos. Por fim, se eu possuir a identificação única da pasta (seu entryID, posso recuperá-la diretamente com a função GetItemFromID do Namespace.

Voltando ao código, agora tenho uma instância da pasta de contatos para trabalhar. Como toda pasta, esta possui uma lista Items que dá acesso ao conteúdo específico - no exemplo, os contatos cadastrados. Então, percorro a lista para prencher a tabela com as informações desejadas de cada um dos contatos.

Para concluir, desconecto minha instância do Outlook e removo-a da memória para liberar os recursos utilizados.

Note que não foi preciso informar usuário e senha para fazer logon. Isso acontece porque o Outlook pode ser configurado para se conectar automaticamente com o mesmo usuário do Windows. Se for preciso, pode-se logar manualmente usando a função Logon do Namespace.

Aproveitando os mesmos conceitos, podemos criar automaticamente um novo contato, conforme mostra o trecho de código a seguir:
contato := pasta.Items.Add(olContactItem) As _ContactItem;
contato.FirstName := 'Balaio';
contato.LastName := 'Tecnológico';
contato.CompanyName := 'ABC71 Soluções Em Informática';
contato.Save;
contato.Display(1);
Como se vê, a criação é bastante simples. Primeiro, chamo o método Add da lista de itens da pasta, fornecendo-lhe o tipo de item que deve ser criado - um item de contato, neste caso. Após prover algumas informações básicas do contato, salvo o registro. A última linha não é imprescindível; ela apenas exibe no próprio Outlook o contato recém-criado para que o usuário possa complementar as informações, se desejar.

De forma similar, conseguimos trabalhar com tarefas e compromissos no calendário. Também é possível fazer o envio de email aproveitando a infraestrutra já configurada do Outlook, operação que mostro no próximo post.

Obviamente, todos os recursos apresentados aqui exigem que haja uma cópia do Outlook instalado no computador local para funcionar.

2 de outubro de 2012

Trabalhando com marcações em mapas com a versão 3 da API do Google Maps

Além dos tradicionais recursos de exibir o mapa para um determinado endereço ou traçar rotas entre endereços fornecidos, a API do Google Maps disponibliza um outro recurso bastante interessante que permite incluir num mapa marcações customizadas. Com isso, você pode marcar pontos de interesse de acordo com as necessidades de sua aplicação. Um exemplo de aplicabilidade desse conceito seria indicar num mapa a localização de estabelecimentos comerciais nas proximidades de um endereço, tais como Caixas 24 Horas, Supermercados, escolas de inglês ou outro qualquer. Obviamente, isso demandará saber de antemão a posição de cada um dos locais passíveis de serem marcados no mapa.

O processo de marcação em si é bastante simples. Após termos ajustado a parametrização inicial do ambiente do Maps e requisitado a exibição de um endereço (veja post Usando a versão 3 da API do Google Maps), basta criar uma instância da classe google.maps.Marker, como no quadro abaixo:
function mostraEnderecoABC71 () {
var lRequest = { address: 'Alameda Santos, 1000, São Paulo - SP, 01418-902', region: 'BR'};
geocoder.geocode( lRequest, trataLocais);
}

function trataLocais (results, status){
if (status == google.maps.GeocoderStatus.OK)
{
map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker(
{map: map,
position: results[0].geometry.location,
animation: google.maps.Animation.DROP,
icon: 'http://maps.google.com/mapfiles/kml/pal3/icon18.png',
shadow: 'http://maps.google.com/mapfiles/kml/pal3/icon18s.png'
});
/* ... */
}
}

A função trataLocais reproduzida acima é o callback passado para o Geocoder junto com a requisição do endereço a ser apresentado no mapa. No exemplo, a marcação é inserida no próprio endereço requisitado, razão pela qual foi usado o retorno da API results[0].geometry.location como local para fixar a marcação.

Dos parâmetros para criar o marcador, apenas 2 são obrigatórios : o map (representando a instância de Map que gerencia a apresentação do mapa na página) e o position (um par latitude/longitude onde o marcador deverá ser fixado). Os demais parâmetros são auxiliares, úteis para melhorar a apresentação. No exemplo, forneço outros 3 parâmetros : animation (uma animação dinâmica usada para cravar o marcador no mapa; o padrão é o marcador simplesmente aparecer), icon (uma URL apontando a imagem que será usada como marcador; o padrão é ) e shadow (URL com a imagem que representa uma sombra do marcador). Há uma lista com imagens padronizadas e suas respectivas sombras neste link mas pode-se trabalhar com qualquer URL personalisada que se queira.

O mapa abaixo usa esses conceitos pra incluir algumas marcações nas proximidades da sede da ABC71 em São Paulo:

A API prevê ainda a possibilidade de se abrir uma janela para apresentar outros detalhes a respeito de um ponto no mapa. A flexibilidade desse tipo de recurso é grande já que é permitido incluir código HTML para formatar as informações que são apresentadas - embora isso não seja obrigatório.

Do ponto de vista da programação, criar uma janela com informações adicionais sobre um ponto também é simples. Consiste em instanciar a classe google.maps.InfoWindow, providenciando um conteúdo para essa janela. Uma vez instanciada, podemos solicitar que a janela seja aberta num ponto específico do mapa. Complementando a função trataLocais :
function trataLocais (results, status){
if (status == google.maps.GeocoderStatus.OK)
{
/* ... */
google.maps.event.addListener(marker, 'click',
function () {
var infowindow = new google.maps.InfoWindow(
{
content: '<div style="text-align:justify;"><b>Ponto de Interesse</b><br/><br/>ABC71 Soluções em Informática.</div>'
});
infowindow.open(map, marker);
});
}
}
No código javascript do quadro anterior, a abertura da janela de informação é vinculada a um evento de "clique" no marcador. Isto é, ela só aparece se o usuário clicar no marcador em questão. Veja que o conteúdo (content) da janela foi formatado com tags HTML, possibilitando adequar a apresentação ao propósito de sua aplicação. A função open - que exibe a janela - aceita como parâmetros o mapa que estamos usando e o marcador que servirá de referência para a exibição da janela.

No mapa que aparece nesse post, os marcadores podem ser clicados, ação que cria uma janela simples com outras informações sobre o ponto marcado.

O arquivo HTML encontrado neste endereço traz o exemplo completo mostrado aqui no post. Ele simula a recepção em formato XML de informações sobre locais que devem ser marcados no mapa. Para isso, o exemplo utiliza também recursos da biblioteca JQuery.

27 de setembro de 2012

ABC71 busca parceiros de tecnologia para comercializar ERP na nuvem

A ABC71, empresa de desenvolvimento de softwares de gestão empresarial, acaba de lançar uma versão em cloud computing de seu ERP Omega. A solução - batizada de ERP Pronto - funciona na internet, dispensando seus clientes de preocupações com infraestrutura de hardware e software para a instalação. Basicamente, é preciso apenas ter um navegador compatível e a disponibilidade de uma conexão com a internet. Ficam a cargo da fornecedora do software os demais aspectos, tais como a preparação de um servidor, configurações gerais, controles de segurança, instalação e gerenciamento do banco de dados (incluindo backup sistemático dos dados lançados pelos usuários), etc.

A empresa preparou o site ERP Pronto para apresentar os principais benefícios do novo produto. Nele é possível encontrar detalhes sobre as diversas operações que o software é capaz de gerenciar, cobrindo faturamento, finanças, estoques, NF-e Federal, SPED, entre outros. O site também aborda dúvidas comumente levantadas, além de apresentar vídeos demonstrando algumas das operações do sistema.

Agora, a ABC71 está buscando firmar parcerias com novos canais de venda para levar a solução aos potenciais clientes, mesmo aqueles que se encontram longe dos chamados "grandes centros". O texto abaixo foi preparado com o objetivo de auxiliar nessa busca, dando outras características do produto, o modo de aquisição e os preços praticados para um cenário típico de pequenas empresas, que é o foco do produto.
ABC71 busca parceiros de tecnologia para comercializar o ERP Pronto

Consultorias especializadas em TI podem representar a solução e aumentar a rentabilidade

A ABC71 – empresa nacional especializada no desenvolvimento de softwares de gestão empresarial – está investindo em sua rede de canais para ampliar as vendas do ERP Pronto.

O ERP PRONTO é um sistema de gestão completo baseado em Cloud Computing e voltado para pequenas e médias empresas. O software pode ser utilizado pelos mais variados segmentos da economia e está preparado para atender às principais necessidades de gestão dos negócios das PMEs (pequenas e médias empresas), atende às áreas comercial, financeira, estoque, contábil e fiscal, incluindo o SPED (Sistema Público de Escrituração Digital) e a NF-e (Nota Fiscal Eletrônica).

O ERP Pronto é comercializado na modalidade SaaS (software como serviço), dispensando a contratação de licença de uso e investimentos em infraestrutura de TI. Funciona como uma conta telefônica onde só se paga pelo o que se usa. O pacote inicial do ERP Pronto está disponível a partir de R$ 510,00 por mês para que até três usuários possam utilizá-la simultaneamente.

Para chegar às PMEs, pulverizadas pelo país, e atingir a meta de vendas em torno de três mil clientes até 2015, a ABC71 procura empresas especializadas em TI que queiram aumentar a rentabilidade.

Entre os canais já credenciados estão: TCi Sistemas, RM Benasse, KLX, Freestore, Publisoft, Lesoft e Viera e Souza Assessoria Contábil.

Entre clientes que já utilizam a solução destacam-se: a Fazenda Novo Horizonte, produtora de grãos do Mato Grosso; a Foccus, distribuidora de ferramentas de corte; e a Invento, importadora de artigos de decoração.

Para se tornar parceiro da ABC71 basta entrar em contato pelo telefone (11) 2179-3190 ou pelo e-mail erppronto@erppronto.com.br.


Mais Informações
Site do ERP Pronto

28 de agosto de 2012

Avaliando a performance de queries no SQL Server

Muito da aceitação de um programa de computador por seus usuários pode ser atribuído à praticidade de sua interface gráfica e até mesmo a aparência que ela tenha. No entanto, a aceitação rapidamente se reverte se o programa apresentar problemas de performance. Com bastante frequência, a degradação de performance num programa está associado a queries mal projetadas submetidas a um banco de dados pelo programa.

Quando digo "mal projetada", eu me refiro a certas características das queries adotadas pelo programa, tais como não utilizar os índices apropriados das tabelas ou manter tabelas sem índice, fazer ligações ineficientes com outras tabelas, trazer registros demais que não serão utilizados ou ainda executar uma mesma query mais vezes do que o necessário.

A partir da sua versão 2005, o SQL Server mantém em tabelas virtuais uma série de informações a respeito das queries que foram executadas pelo banco de dados. Essas informações ficam armazenadas em memória e são descartadas quando o serviço do banco é desligado. No entanto, são informações bastante úteis para auxiliar na localização de problemas de performance gerados pelas queries mal planejadas.

Um usuário que queira consultar as informações disponibilizadas nessas tabelas virutais precisa ter atribuida a permissão VIEW SERVER STATE. Isso pode ser feito visualmente, através das propriedades do servidor no Management Studio ou via comando SQL, utilizando-se o GRANT.

Uma das views mais interessantes nesse contexto é a sys.dm_exec_query_stats, responsável pelas estatísticas de execução de queries. Ela traz, dentre outras informações, a quantidade de vezes que uma query foi executada, o tempo gasto nessas execuções, quantas linhas foram retornadas, a quantidade de leituras e gravações feitas durante a execução. Ela dá acesso também ao texto da query e ao plano de execução montado pelo SQL. O exemplo no quadro abaixo lista em ordem decrescente as queries que mais consumiram CPU:
SELECT qs.total_worker_time / qs.execution_count As 'Uso Médio da CPU',
qs.total_worker_time As 'Uso Total da CPU',
qs.execution_count As 'Qtde de Execuções',
qt.text As 'Query',
DB_NAME(qt.dbid) As 'Nome do Banco'
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) As qt
ORDER BY 1 DESC

Veja que a quantidade de vezes que uma query executou não é determinante para que ela tenha consumido mais CPU na média. Consulta semelhante pode ser montada para obter as queries que mais foram executadas (pra avaliar se todas as execuções são mesmo necessárias) ou a quantidade de linhas retornada (pra avaliar se não é possível restringir mais os registros, acrescentando outras comparações à cláusula WHERE). Os tempos listados estão em microsegundos; para convertê-lo em segundos, divida o valor por 1 milhão. É possível ainda levantar a quantidade de gravações feitas por uma query e avaliar se tantas alterações são mesmo necessárias

Outro detalhe a ser destacado no quadro anterior é que o texto da query não está na mesma view. Para obtê-lo, é necessário pegar o código contido no campo sql_handle e repassá-lo à função sys.dm_exec_sql_text.

Outra view bastante útil é a sys.dm_db_missing_index_details. Como o próprio nome diz, ela armazena detalhes sobre o uso de queries cujas execuções não estão otimizadas por que não foi possível encontrar na tabela um índice apropriado. Baseado nas informações retornadas por ela, podemos introduzir novos índices e melhorar a performance das consultas envolvendo as tabelas em questão. O quadro abaixo mostra um exemplo de consulta a essa view, listando as colunas encontradas na cláusula WHERE da query com problema, a tabela envolvida e a quantidade de vezes que a query problemática foi disparada:
SELECT equality_columns, inequality_columns, statement, count(*) As 'Qtde Exec'
FROM sys.dm_db_missing_index_details
WHERE DB_NAME(database_id) = 'ABC71DS94'
GROUP BY equality_columns, inequality_columns, statement
ORDER BY statement

A coluna statement traz o nome da tabela que está sem índice apropriado. A equality_columns lista as colunas usadas em comparações de igualdade no WHERE - algo como NOME_DO_CAMPO = VALOR. Em inequality_columns aparecem as colunas usadas em outros tipos de comparação do WHERE. Veja que é possível filtrar as informações por banco de dados instalado, permitindo avaliar os índices para cada banco isoladamente.

A lista de informações que podemos obter no DMV (Dynamic Management Views) inclui ainda os motivos de espera para uma query ser executada, dados de espelhamento e replicação, tamanho do banco de dados e suas partições, etc. A lista completa das informações disponíveis por esse meio pode ser encontrada em Dynamic Management Views and Functions no MSDN online.

1 de agosto de 2012

Aplicações em Java ainda valem a pena ?

Depois de protagonizar o primeiro grande ataque a sistemas Mac OS no início deste ano, quando uma vulnerabilidade crítica permitiu a infecção de mais de 600 mil Macs, o Java volta a ficar na berlinda. Desta vez, um pesquisador do Centro de Proteção a Malwares da Microsoft publicou um artigo intitulado "Como se proteger de malwares baseados em Java" onde aconselha os usuários a trabalharem sempre com as releases mais recentes da Virtual Machine e, quando for possível, que a deixem desativada para proteger seus sistemas. No caso de não utilizar aplicações Java, a recomendação é de remover completamente o JRE.

Na mesma linha, só que um pouco mais radical, Woody Leonhard defendeu em artigo publicado na InfoWorld.com que a melhor opção é mesmo abandonar a tecnologia, substituindo-a o mais rápido possível. O texto abaixo é uma tradução do artigo onde ele expressa sua opinião:
Quatro meses atrás, eu critiquei o Java -- ou, mais apropriadamente, o uso do Java Runtime Environment (JRE) -- no post "É hora de expulsar o Java da cidade". Quatro meses depois, a situação não melhorou um milímetro. De fato, ela piorou, se é que piorar é uma opção para o vetor de infecção número 1 em PCs e Macs.

Na semana passada, o pesquisador Matt Oh do Centro de Proteção Contra Malware da Microsoft publicou um artigo no TechNet sobre como se proteger contra os malwares baseados em Java. Para enfatizar o ponto, ele deu uma palestra no Black Hat 2012 no mesmo dia, dizendo que a situação com o Java está se deteriorando -- e não somente no Windows.

"Estamos vendo cada vez mais vulnerabilidades no Java sendo exploradas ... e uma vulnerabilidade no Java pode às vezes ser explorada em múltiplas plataformas," disse o pesquisador.

A principal preocupação para Oh são as brechas no sandbox. Se autores de malware conseguirem acesso fora dos limites do sandbox de execução do Java/JRE, eles podem tomar o controle de um sistema, não importando se ele está em Windows, Mac OS X ou Unix. Uma única vulnerabilidade no Java -- como a brecha de segurança "confusão de tipos" CVE-2012-1723, descoberta há apenas algumas semanas, ou a velha CVE-2012-0507, que levou à rede de robôs Flashback, com mais de 600 mil Macs infectados neste ano -- pode resultar numa exploração bem sucedida que burle as defesas do sistema operacional simplesmente por ele estar executando o Java.

"A confusão de tipos é uma vulnerabilidade que ocorre quando a verificação de segurança de tipos feita pelo JRE falha quando são fornecidos tipos diferentes do esperado para uma instrução. Alguns dos tipos do Java, como o ClassLoader, podem ser alvos desse ataque. Se a segurança de tipos dessas classes é quebrada, pode-se ter acesso a alguns métodos que não deveriam estar acessíveis a processos externos à própria classe. Esta violação na segurança de tipos leva, em última análise, ao comprometimento do sandbox do Java," disse Oh.

Para piorar, o fato do programa ser escrito em Java torna mais fácil ocultar a violação usando ferramentas disponíveis e bem documentadas da própria linguagem para embaralhar o código.

A recomendação de Oh é que você esteja sempre com o JRE mais atualizado e que o desabilite sempre que isso for possível. Se você não usa aplicações Java, então simplesmente desinstale o JRE.

Minha recomendação para TI é um pouco mais proativa: é hora de tirar seus usuários da esteira do JRE/JVM. Se você tem um produto que requer o JRE, migre-o. Se seus planos de negócio incluem aplicações Java, altere seus planos. Se você ou sua equipe de desenvolvimento programam aplicações client Java, é hora de diversificar suas habilidades.

Ao continuar usando Java, você está colocando sua empresa e seus clientes em risco.

Embora eu entenda as razões dele e concorde com sua argumentação - que é irretocável, considerando-se a agudez da crise atual - acredito que ainda seja cedo pra tomar uma decisão tal radical quanto simplesmente abdicar das aplicações feitas em Java. Digo isso porque me parece que a Oracle vem mantendo um plano razoavelmente consistente de atualizações da linguagem. Veja, por exemplo, o projeto JigSaw e os planos para levar Java para o iOS.

Apesar de às vezes a Oracle pecar na velocidade das correções, creio que o ideal é seguir as recomendações de Matt Oh: manter sempre atualizado o JRE e eventualmente desligá-lo (se possível) quando brechas críticas forem detectadas e a correção demorar a sair.

18 de julho de 2012

Assinando documentos XML com CAPICOM e Delphi

A Microsoft disponibiliza para o Windows uma biblioteca com tecnologia COM para tratar a criação e manipulação de arquivos XML. A biblioteca, chamada MSXML, está atualmente na versão 6 e suporta também transformações XSLT e validação através de esquemas XSD. Em apenas uma das versões (o MSXML5), foram incluídos ainda recursos para fazer assinatura digital de XMLs.

A versão 5 foi distribuída exclusivamente com o Microsoft Office para que os desenvolvedores dessa plataforma pudessem assinar XMLs. No entanto, uma busca na internet revela que a biblioteca é amplamente utilizada fora desse contexto.

Isto é uma boa notícia para quem usa Delphi (ou outra linguagem que suporte COM) pois o MSXML5, em associação com o CAPICOM, facilita bastante a tarefa de assinar XMLs. Neste post eu mostro como realizar este processo, considerando que você já tem montado o XML que quer assinar. O quadro abaixo mostra parte do XML de uma Nota Fiscal que vou usar como exemplo:
<?xml version="1.0" encoding="UTF-8" ?>
<enviNFe versao="2.00" xmlns="http://www.portalfiscal.inf.br/nfe">
<idLote>71</idLote>
<NFe>
<infNFe Id="NFe3508059978" versao="2.00">
<cUF>35</cUF>
<cNF>518005127</cNF>
<natOp>Venda a vista</natOp>
<mod>55</mod>
<serie>1</serie>
<dEmi>2012-05-06</dEmi>
<tpAmb>2</tpAmb>
</infNFe>
</NFe>
</enviNFe>

A primeira providência é importar os fontes dos ActiveX MSXML5 e CAPICOM para podermos utilizá-los no projeto Delphi. Há um resumo de como fazer essa importação neste endereço; para o MSXML5, a descrição da Type Library é Microsoft XML, v5.0. Ambos os fontes gerados devem ser incluídos na cláusula uses da unit que for fazer a assinatura.

Obviamente, vamos precisar de um certificado digital para realizar a assinatura. Utilizando o CAPICOM, podemos acessar o Certificate Store do Windows e localizar um apropriado, que tenha sido gerado para sua empresa. No entanto, as interfaces disponíveis no MSXML5 exigem que você informe um Certificate Store com o certificado que será utilizado, bem como sua cadeia de validação, se for necessário. Este passo extra é mostrado no quadro abaixo.
var store : IStore3;
cert : TCertificate;
lKey : IPrivateKey;
begin
cert := getCert;
lKey := Cert.PrivateKey;

{ Monta um Store em memória com o Certificado obtido antes }
store := CoStore.Create;
store.Open(CAPICOM_CURRENT_USER_STORE, 'My', CAPICOM_STORE_OPEN_MAXIMUM_ALLOWED);
store.Add(cert.DefaultInterface);
{ ... }

A função GetCert utilizada acima é minha e segue as instruções descritas no post sobre acesso ao Certificate Store com CAPICOM. De resto, o código apenas cria um Store em memória e adiciona a ele o certificado encontrado. O trecho também salva a chave privada deste certificado, informação que será usada mais adiante para gerar a assinatura em si.

A seguir, precisamos preparar o XML do exemplo para receber a assinatura. É que a interface do MSXML5 responsável por essa etapa se baseia num nó Signature do XML. Esse nó descreve as transformações pelas quais o XML deve ser submetido antes de ser de fato assinado. O nó Signature deve ser criado como mostrado abaixo:
<?xml version="1.0" encoding="UTF-8" ?>
<enviNFe versao="2.00" xmlns="http://www.portalfiscal.inf.br/nfe">
<idLote>71</idLote>
<NFe>
{ ... }
<Signature>
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="#NFe3508059978">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue></DigestValue>
</Reference>
</SignedInfo>
<SignatureValue></SignatureValue>
</Signature>
</NFe>
</enviNFe>

Como podemos ver acima, o nó também deixa reservado um local para receber o hash (DigestValue) e outro para a própria assinatura (SignatureValue), que serão preenchidos automaticamente. A única parte variável do bloco preparado acima é o atributo URI da tag Reference, valor que reflete a identificação da Nota Fiscal contida no XML. Ou seja, deve-se montar o valor desse atributo a partir do valor do atributo Id da tag infNFe, precedendo este valor com o sinal #. Como o próprio nome diz, essa parte do XML faz referência ao nó (e seus filhos) que será resguardado pela assinatura digital.

Uma vez que o XML está preparado, podemos partir para a assinatura propriamente dita, o que é conseguido através da interface IXMLDigitalSignature do MSXML5. Então, precisamos criar uma instância dela e lhe fornecedor os dados para a assinatura:
var {...}
xmlDoc: IXmlDomDocument3;
noSignature : IXMLDOMElement;
xmlSign : IXMLDigitalSignature;
xmlDsigKey: IXMLDSigKey;
begin
{...}
{ Monta o XML e prepara a tag Signature }
xmlDoc := PreparaXML ();
noSignature := EncontraNodeSignature (xmlDoc);

if (noSignature <> Nil) then begin
xmlSign := CoMXDigitalSignature50.Create;
xmlSign.signature := noSignature;
xmlSign.store := store;

{ Monta a chave com o tipo exigido pelo método Sign }
xmlDsigKey := xmlSign.createKeyFromCSP(lKey.ProviderType, lKey.ProviderName, lKey.ContainerName, 0);
{ Assina o XML }
xmlSign.sign(xmlDsigKey, NOKEYINFO);
{...}
end;
end;

Como podemos ver no trecho de código acima, o IXMLDigitalSignature precisa que indiquemos qual é o nó Signature do nosso XmlDomDocument e o Certificate Store que construímos no início do post. Agora, temos que chamar o método sign para completar o serviço. Para funcionar, ele precisa de algumas informações baseadas na chave privada do certificado. No exemplo acima, utilizei a chave recuperada a partir do certificado para criar uma instância da interface IXMLDSigKey contendo as informações necessárias.

O segundo parâmetro da função sign determina como os dados da chave serão incluídos na estrutura do XML. No exemplo, usei o valor NOKEYINFO para que tais dados não sejam incluídos automaticamente.

Após chamar a função sign, as tags que deixamos reservadas no nó Signature são automaticamente calculadas e preenchidas. Finalizando, para que o XML fique no padrão exigido para Notas Fiscais pela Receita Federal, só falta incluir um nó com os dados do certificado usado para assinar. Mostrei como fazer isso no post Certificado Digital para inclusão no XML com CAPICOM e Delphi.

22 de junho de 2012

Trabalhando com informações sobres tipos no Delphi em tempo de execução - parte II

A extensão do mecanismo de RTTI (Run Time Type Information) introduzida no Delphi 2010 - e que eu citei no meu último post - abre novas possibilidades de soluções para arquitetura de softwares feitos em Delphi. Isso é particularmente útil para desenvolvedores de infraestrutura de software, situação onde é comum a necessidade de se criar rotinas genéricas para certas tarefas.

Customizar classes estendendo o RTTI também permite melhorar a organização da solução já que estas classes serão mais coesas. Isto é, funcionalidades que não são centrais podem ser transferidas para outras classes associadas. É o caso, por exemplo, da exportação de objetos para XML (ou outro formato qualquer), processo conhecido como serialização.

Estender o RTTI significa fazer marcações em classes, métodos, campos ou propriedades, introduzindo características extras no elemento marcado. Como a marcação também é uma classe, podemos criar comportamentos auxiliares complexos.

O quadro abaixo mostra a declaração de uma classe simples que possui algumas marcações para permitir exportar seu conteúdo como um XML:
[TWXmlExportable('notaFiscal')]
TWNota=class
public
[TWXmlExportableNode('nro')]
Numero : longint;

[TWXmlExportableNode('serie')]
Serie : String;

[TWXmlExportableNode('observ')]
Observ: String;

[TWXmlExportableNode('vlrTotal')]
Valor: Double;

InfoNaoExport : String;
procedure GravaNota;
end;
Como podemos ver, a marcação de um elemento é feita colocando-se um par abre e fecha colchetes antes da declaração desse elemento, inserindo entre eles o nome da classe usada para marcá-lo. No exemplo, há duas classes de marcação sendo usadas - TWXmlExportable e TWXmlExportableNode, sendo que ambas aceitam um parâmetro do tipo string em seus construtores, o que também é fornecido na marcação do exemplo. Aqui, o parâmetro indica o nome da tag XML que conterá o elemento marcado. Observe que nem todos os campos foram marcados; apenas aqueles que eu quero exportar.

As classes de marcação são também chamadas de atributos e em Delphi têm obrigatoriamente que ser heranças da classe TCustomAttribute. A declaração das classes usadas no exemplo está na listagem a seguir:
TWXmlExportable = class(TCustomAttribute)
protected
FIsRoot: Boolean;
FTagName: String;
public
constructor Create (ATagName: String); virtual;

property TagName : String read FTagName;
property IsRoot: Boolean read FIsRoot;
end;

TWXmlExportableNode = class(TWXmlExportable)
public
constructor Create (ATagName: String); override;
end;

{ ... }

constructor TWXmlExportable.Create (ATagName: String);
begin
inherited Create;
FTagNAme := ATagName;
FIsRoot := true;
end;

constructor TWXmlExportableNode.Create (ATagName: String);
begin
inherited Create(ATagName);
FIsRoot := false;
end;
Introduzi nelas a propriedade IsRoot para poder diferenciar classes - que são elementos compostos por outros elementos - dos demais tipos. Assim, os campos de uma classe poderão ser aninhados em uma tag, enquanto cada campo de tipo básico é exportado em sua tag individual. Com isso, a exportação funcionará corretamente até mesmo com classes que possuam outros objetos exportáveis em sua estrutura.

Portanto, essas classes de marcação podem ser aplicadas livremente a quaisquer outros elementos no programa. A grande vantagem é que tal flexibilidade nos permite montar uma rotina genérica que seja capaz de exportar para XML qualquer objeto que contenha as marcações apropriadas. Da mesma forma que recuperamos a lista de métodos de um objeto no outro post, podemos interagir com os atributos desse objeto:
function TForm1.GetExportToXml (AObj: TObject) : TWXmlExportable;
var i : integer; lTipo: TRttiType;
begin
Result := Nil;
{_RttiCntxt é um TRttiContext instanciado em outro ponto do sistema }
lTipo := _RttiCntxt.GetType(AObj.ClassType);

{ Obtem os atributos - ou marcações - do objeto }
i := Length (lTipo.GetAttributes()) - 1;

{ Verifica se algum dos atributos é do tipo TWXmlExportable; isso significa que o objeto passado no parâmetro é "exportável" }
while (Result = Nil) And (i >= 0) do begin
if (lTipo.GetAttributes()[i] is TWXmlExportable) then
Result := (lTipo.GetAttributes()[i] As TWXmlExportable);
Dec (i);
end;
end;
A exportação para XML, então, passa a ser uma questão de levantar quais são os campos do objeto, determinar quais desses campos são "exportáveis" usando a mesma técnica acima, obter o nome da tag e o valor do campo. A função listada no quadro abaixo faz a exportação, seguindo esses passos:
function TForm1.ExportToXml (AObj: TObject) : string;
var lTipo: TRttiType;
lCampo: TRttiField;
lAttrib : TCustomAttribute;
lExpField, lExpObj : TWXmlExportable;
begin
Result := '';
lExpObj := GetExportToXml (AObj);

{ só continua se o objeto é "exportável" }
if lExpObj <> Nil then begin
lTipo := _RttiCntxt.GetType(AObj.ClassType);

{ Inicia a tag XML, obtendo o nome no atributo encontrado para o objeto }
Result := '<' + lExpObj.TagName + '>';

{ Quais são os campos nesse tipo de classe ? }
for lCampo in lTipo.GetFields() do begin
{ Analisa os atributos do campo pra ver quais são exportáveis }
for lAttrib in lCampo.GetAttributes() do
if (lAttrib is TWXmlExportable) then begin
lExpField := lAttrib As TWXmlExportable;
if (lExpField.IsRoot) then
{ campos do tipo classe são exportados recursivamente }
Result := Result + ExportToXml (lCampo.GetValue(AObj).AsObject)
else begin
{ campos de tipos simples apenas envolve com o nome da tag contido no atributo do campo }
Result := Result + '<' + lExpField.TagName + '>';
{ Obtem o valor atual do campo }
Result := Result + lCampo.GetValue(AObj).toString();
Result := Result + '</' + lExpField.TagName + '>';
end;
end;
end;

{ Encerra a tag desse objeto }
Result := Result + '</' + lExpObj.TagName + '>';
end;
end;
As informações sobre cada campo de uma classe são mantidas em instâncias de TRttiField. Além dos eventuais atributos associados ao campo, podemos descobrir com ela o nome do campo, seu tipo e sua visibilidade (público, protegido ou privado). Ela também fornece meios para recuperarmos ou modificarmos o valor do campo, conforme podemos ver no quadro anterior.

Agora, para exportar uma instância da classe de Nota precisamos apenas passá-la para a função acima. Na verdade, a função é genérica o suficiente pra ser capaz de exportar para XML instâncias de qualquer classe anotada com os atributos apresentados aqui.

Do jeito que esse recurso funciona, a classe de Nota apresentada no início do post pode focar na implementação das tarefas inerentes ao negócio, deixando a tarefa acessória de exportar os dados para outros pontos do sistema, favorecendo a coesão das classes na solução.

1 de junho de 2012

Trabalhando com informações sobres tipos no Delphi em tempo de execução - parte I

Em muitas situações num programa é conveniente saber o tipo de dado exato com o qual uma rotina está trabalhando, principalmente quando envolve objetos complexos. Dependendo do cenário, é preciso tomar uma decisão baseada no tipo e acessar propriedades e métodos do tipo específico.

Imagine, por exemplo, que você queira garantir que os textos nas telas do seu sistema sejam exibidos usando uma fonte predeterminada. Ao invés de ajustar o fonte manualmente em cada componente da tela, podemos percorrer a lista de componentes e automatizar o ajuste:
procedure TForm1.FormCreate(Sender: TObject);
var i : integer;
lLabel : TLabel;
begin
for i := 0 to ComponentCount - 1 do
if (Components[i] is TLabel) then begin
lLabel := Components[i] as TLabel;
lLabel.Font.Name := 'Calibri';
lLabel.Font.Color := clBlue;
lLabel.Font.Size := 10;
end;
end;
O código acima percorre a lista de componentes da tela e usa a palavra chave IS para saber se o componente atual é do tipo TLabel (ou uma herança dessa classe). Em caso positivo, é feito um cast usando o AS e, então, as propriedades relacionadas ao fonte podem ser configuradas à vontade.

O recurso de uma linguagem de programação que permite obter informações sobre tipos de dados em tempo de execução é chamado de RTTI (Run Time Type Information). Em alguns ambientes, isso também é chamado de Reflexion.

Todos os objetos em Delphi conseguem reportar informações básicas sobre si mesmos, como o nome da classe a que pertencem, de qual classe ela herda, se implementa um determinado método, entre outros.

No entanto, a versão 2010 do Delphi passou por uma grande reformulação no tratamento de RTTI, aumentando significativamente sua capacidade. Agora você também pode obter informações sobre tipos atômicos, além de poder levantar os métodos e propriedades de classes, mesmo aqueles que sejam protegidos ou privados. Também é possível personalizar o RTTI, disponibilizando outras informações e funcionalidades a classes Delphi.

O novo mecanismo é baseado no conceito de "contexto", que nada mais é que o escopo constituido pelo programa principal, as bibliotecas (DLLs) e pacotes agregados. Toda aplicação Delphi passa a ter um único contexto RTTI onde são lançadas as informações sobre os tipos de dados em uso. Com isso, o primeiro passo para se obter informações sobre tipos é declarar e instanciar um contexto.
var lContexto: TRttiContext;
lType: TRttiType;
lMetodo : TRttiMethod;
lParm : TRttiParameter;
begin
lContexto := TRttiContext.Create;
lType := lContexto.GetType(TypeInfo (TForm1));

for lMetodo in lType.GetMethods() do begin
ListBox1.AddItem(lMetodo.Name, lMetodo);
for lParm in lMetodo.GetParameters () do
ListBox1.AddItem(' ' + lParm.ToString (), lParm);
end;

lContexto.Free;
end;
Após instanciar o contexto, esse trecho de código obtém informações sobre o Form atual em dois passos. No primeiro, a função TypeInfo retorna um ponteiro para as informações básicas. Então, o ponteiro é usado num segundo passo para obter as informações extendidas que foram introduzidas na nova versão do RTTI e que são representadas pela classe TRttiType.

Em seguida, o código percorre a lista de métodos do Form1 - incluindo toda a hierarquia de classes a qual ele pertence - e adiciona seus nomes num list box para exibição. Para cada método também é recuperada a lista dos parâmetros que devem ser passados quando ele for chamado em seu programa.

Embora não esteja representado no exemplo, o mesmo tipo de levantamento pode ser feito com as propriedades, ainda que elas não estejam declaradas como published.

A dúvida mais frequente sobre esse tipo de recurso é: em que situações isso pode ser aplicado ? Além do cenário citado no início do post, outras situações podem ser mais facilmente resolvidas com RTTI, tais como a clonagem de objetos (incluindo o estado completo dele), serialização de objetos (transformá-los em um XML ou enviá-los para a impressora, por exemplo), criação de uma infraestutura para aceitar plugins em uma aplicação, chamada dinâmica de métodos, etc.

O próprio IDE do Delphi usa aspectos do RTTI para poder trabalhar com as classes e Forms que você desenvolve, fornecendo informações, por exemplo, para o Code Insight ou serializando a estrutura do Form em um arquivo DFM.

No próximo post, mostro o recurso que permite incrementar as informações de runtime em classes, métodos e propriedades.

Mais Informações
Working with RTTI

16 de maio de 2012

Preparando o Certificado Digital para inclusão no XML com CAPICOM e Delphi

Há algum tempo, publiquei uma série de posts sobre a manipulação de XMLs e a criação de assinatura digital para eles. Como a plataforma .NET da Microsoft traz classes prontas para realizar o grosso das tarefas envolvidas, eu usei o C# para montar os exemplos contidos naqueles posts.

Mas muita gente não pode usar a solução com .Net, principalmente quem já têm aplicações desenvolvidas em outras linguagens. Algumas dessas pessoas entraram em contato pra saber como fazer esse mesmo procedimento usando a biblioteca de criptografia da Microsoft - o CAPICOM. Uma dúvida bastante frequente é relativa ao conteúdo a ser inserido na tag X509Certificate do XML assinado. O quadro abaixo mostra o trecho de um XML assinado de Nota Fiscal Eletrônica que inclui a tag em questão:
<?xml version="1.0" encoding="UTF-8" ?>
<enviNFe versao="2.00" xmlns="http://www.portalfiscal.inf.br/nfe">
<idLote>71</idLote>
<NFe>
<infNFe Id="NFe3508059978" versao="2.00">
<cUF>35</cUF>
<cNF>518005127</cNF>
<natOp>Venda a vista</natOp>
<mod>55</mod>
<serie>1</serie>
<dEmi>2012-05-06</dEmi>
<tpAmb>2</tpAmb>
</infNFe>
<Signature>
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<Reference URI="#NFe3508059978"
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<DigestValue>4aXU7m8rl14ALy6X...=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>Igq7eI/Fy3PyjjSW...=</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIGxDCCBaygAwIBAgIIN6q4...=</X509Certificate>
</X509Data>
</KeyInfo>
</Signature>
</NFe>
</enviNFe>

Como podemos ver, a tag X509Certificate é parte da estrutura que traz as informações sobre a chave usada pela assinatura e é incluída no fim do XML, junto com a área reservada para a assinatura em si. O valor armazenado na X509Certificate descreve o certificado usado na assinatura, codificado na Base64. Esse valor representa a chave do certificado e é parte imprescindível para a validação do documento XML, isso é, ele ajuda a garantir que o XML não foi adulterado depois de ter sido assinado.

E como calcular esse valor com CAPICOM? Após recuperar o certificado digital de acordo com as instruções do post Acessando o Certificate Store do Windows com CAPICOM, podemos conseguir o conteúdo desse certificado já na Base64 em um único comando:
var cert : TCertificate;
certBase64 : WideString;
begin
{ ... }
certBase64 := cert.Export(CAPICOM_ENCODE_BASE64);
{ ... }
A função export da interface ICertificate2 aceita como parâmetro o tipo de codificação que se deseja como resultado da exportaçao. Os valores permitidos para esse parâmetros são os definidos no tipo enumerado CAPICOM_ENCODING_TYPE. Ele pode assumir basicamente dois tipo de codificação : a Base64 ou a informação binária pura, sem codificação.

O código utilizado no exemplo - CAPICOM_ENCODE_BASE64 - faz a exportação de todas as informações necessárias para inclusão na tag X509Certificate do XML assinado. Há, no entanto, um detalhe no qual é preciso prestar atenção: o texto exportado está quebrado em diversas linhas.
MIIGNjCCBR6gAwIBAgIITDgm4uwI+60wDQYJKoZIhvcNAQEFBQAwTDELMAkGA1UE
BhMCQlIxEzARBgNVBAoTCklDUC1CcmFzaWwxKDAmBgNVBAMTH1NFUkFTQSBDZXJ0
aWZpY2Fkb3JhIERpZ2l0YWwgdjEwHhcNMTEwNzA3MTMxMDA0WhcNMTIwNzA2MTMx
MDA0WjCB7jELMAkGA1UEBhMCQlIxEzARBgNVBAoTCklDUC1CcmFzaWwxFDASBgNV
BAsTCyhFTSBCUkFOQ08pMRgwFgYDVQQLEw8wMDAwMDEwMDE5NjExNzIxFDASBgNV
BAsTCyhFTSBCUkFOQ08pMRQwEgYDVQQLEwsoRU0gQlJBTkNPKTEUMBIGA1UECxML
.
.
.
aXRhbC5jb20uYnIvc2VyYXNhY2R2MTANBgkqhkiG9w0BAQUFAAOCAQEAY8NBFO6e
kPTHirS8DHnlsx1kTJfmsHoUR1QgnDMNdChQjW7ctAn5nIpPYXdhf26om+Us80D/
5RA2iXHhXFT5eL7Crqq0DQ73zWbdn8e81dIDFqYjxIt4LBMy4CO0lJeiT9YHkmYB
XSuADpElqwXA0rzx7bBeIOzuVUGMoYQBduViByI813XIQ2i86Z9VgRLO5KuPUNPn
0RkZS2rSpX1/wLkef8ura++dZEYmbVWpu3g2yCLQJfCArYy/fzsIDwBKruSWnY/O
NK/1AgUaceVAcxxvVmuECwCBqtpfmHqAOXC/uEOXiKefF7L++j0Xc+Okew1SMkHJ
sNNJ+AcX4VfQrw==

Como o conteúdo da tag X509Certificate deve estar numa única linha, o texto retornado pelo export deve ser tratado, removendo-se os espaços em branco do fim e as quebras de linha:
certBase64 := Trim (ReplaceStr (certBase64, #13#10, ''));

O valor da variável certBase64 agora pode ser incluído sem problemas como conteúdo da tag X509Certificate em seu XML assinado.