[Delphi] Criando um visualizador de DataSets com o Open Tools API – Parte 2

[Delphi] Criando um visualizador de DataSets com o Open Tools API - Parte 2

Olá, programadores!
No artigo anterior, observamos a facilidade ao criar um novo menu na IDE do RAD Studio com as Interfaces do Open Tools API. No artigo de hoje, focaremos na codificação do método Execute para extrair os dados do DataSet em runtime para depois exibi-los em uma Grid. Vamos continuar?

 

Para compreender como o visualizador irá funcionar, vamos, primeiramente, executar alguns passos manuais.
Uma das formas mais simples de salvar os dados de um DataSet em runtime, considerando que os breakpoints estão devidamente posicionados, é acionar a janela de Evaluate/Modify (CTRL + F7) e escrever a seguinte instrução:

ClientDataSet.SaveToFile('C:\Aplicacao\Dados.xml');

Em seguida, é necessário utilizar uma ferramenta que exiba os dados de um arquivo XML organizados em uma Grid.
O objetivo do nosso wizard é automatizar todo este procedimento, ou seja, acessar a operação de Evaluate do debugger, salvar os dados e carregá-los em um formulário de modo automático, dispensando qualquer interação.
Para isso, faremos uso de três novas Interfaces nesse artigo: IOTAEditorServices, IOTADebuggerServices e IOTAThread. Com a primeira, identificaremos o texto selecionado no editor de código. Com a segunda, acessaremos o processo na IDE referente à depuração. Coma última, simularemos a operação de Evaluate do depurador. Embora essa explicação nos dê a impressão de uma codificação extensa e trabalhosa, veremos, a seguir, que é bem simples.

 

1) Configurar o caminho do arquivo temporário
O método SaveToFile da classe TClientDataSet exige o diretório e o nome do arquivo que será gerado. No nosso caso, já que este arquivo será temporário, pensei em salvá-lo justamente na pasta temporária do usuário, que recebe o nome de “AppData” no Windows. No entanto, não precisamos mais de funções específicas para buscar este diretório. O próprio Delphi já possui uma função nativa para essa finalidade, contida na unit System.IOUtils:

uses
  System.IOUtils;
  { ... }
 
var
  ArquivoDados: string;
begin
  ArquivoDados := System.IOUtils.TPath.GetTempPath + 'Dados.xml';

 

2) Identificar o texto selecionado no editor de código
Na unit “ToolsAPI.pas”, existe uma variável global chamada BorlandIDEServices, que corresponde aos serviços compartilhados pela IDE, como o editor de código. O nosso wizard precisa acessar estes serviços, portanto, utilizaremos essa variável global, aplicando alguns castings para as Interfaces mencionadas no início do artigo.
Pois bem, para identificar o texto selecionado, ou melhor, o “bloco” selecionado no editor, basta chamar o método GetBlock da Interface IOTAEditorServices:

var
  { ... }
  TextoSelecionado: string;
begin
  { ... }
  TextoSelecionado := (BorlandIDEServices as IOTAEditorServices).TopView.GetBlock.Text;

 

3) Montar a expressão que será interpretada pelo debugger
Desejamos que os dados do DataSet selecionado no editor (variável “TextoSelecionado”) sejam exportados para um arquivo (variável “ArquivoDados”), certo? Bom, como essas duas variáveis já estão preenchidas, montar a expressão não será nada complicado:

uses
   System.SysUtils;
   { ... }
 
var
  { ... }
  Expressao: string;
begin
  { ... }
  Expressao := Format('%s.SaveToFile(''%s'')', [TextoSelecionado, ArquivoDados]);

Neste ponto, vale ressaltar que “expressão” corresponde ao texto que digitamos no campo “Expression” da janela Evaluate/Modify (CTRL + F7) quando estamos depurando uma aplicação.

 

4) Acessar a thread do debugger
Essa etapa é muito importante. O serviço de depuração do Delphi (que nos fornece a possibilidade de examinar breakpoints, inspecionar objetos, monitorar variáveis, entre outros) trabalha em uma thread enquanto o programa está em tempo de execução. Para executar o Evaluate, obteremos o ponteiro dessa thread através do método CurrentThread da Interface IOTADebuggerServices:

var
  { ... }
  Thread: IOTAThread;
begin
  { ... }
  Thread := (BorlandIDEServices as IOTADebuggerServices).CurrentProcess.CurrentThread;

 

5) Executar o Evaluate!
Até agora, usei bastante a palavra “Evaluate” no artigo. Pela tradução, que significa “Avaliar”, fica fácil compreender o nosso propósito: solicitaremos ao Delphi que avalie a nossa expressão, da mesma forma como ocorre na janela Evaluate/Modify. Porém, antes de finalmente executar a função Evaluate da Interface IOTAThread, acho importante estudarmos a sua assinatura:

function Evaluate(const ExprStr: string; ResultStr: PChar; ResultStrSize: LongWord;
  out CanModify: Boolean; AllowSideEffects: Boolean; FormatSpecifiers: PAnsiChar;
  out ResultAddr: TOTAAddress; out ResultSize, ResultVal: LongWord): TOTAEvaluateResult;

Eis uma breve explicação dos parâmetros:

  • ExprStr: expressão que será avaliada;
  • ResultStr: texto de resultado da avaliação;
  • ResultStrSize: tamanho do texto de resultado da avaliação;
  • CanModify (out): indica se a expressão pode ser alterada;
  • AllowSideEffects: indica se o “avaliador” pode invocar outras funções para completar a avaliação;
  • FormatSpecifiers: especifica formatadores para o resultado;
  • ResultAddr (out): endereço em memória do resultado;
  • ResultSize (out): tamanho em memória do resultado (SizeOf);
  • ResultVal (out): valor em memória do resultado;

Já que executaremos um SaveToFile, que é um procedure, não é necessário considerar o texto do resultado da avaliação, logo, o segundo e terceiro parâmetros podem ser ignorados. Este texto seria necessário apenas se estivéssemos avaliando uma propriedade do DataSet, como State, por exemplo.
Além disso, como há quatro parâmetros out na assinatura do método, também devemos criar as variáveis para preenchê-los:

var
  { ... }
  CanModify: boolean;
  Endereco: UInt64;
  Tamanho: Cardinal;
  Resultado: Cardinal;
begin
  { ... }
  Thread.Evaluate(Expressao, '', 0, CanModify, True, '', Endereco, Tamanho, Resultado);
end;

Com essa instrução, o arquivo já será criado! 🙂

 

6) Verificando o retorno da avaliação
Em algumas situações, a função Evaluate pode falhar devido a erros levantados pelo próprio depurador. Em vista dessa possibilidade, é prudente verificar o retorno da função para que possamos prosseguir com a visualização dos dados do DataSet. Para isso, utilizaremos uma variável do tipo enumerado TOTAEvaluateResult, no qual comporta os seguintes valores: erOK, erDeferred, erError ou erBusy. Continuaremos a execução somente se a variável for diferente dos dois últimos valores.

var
  { ... }
  Retorno: TOTAEvaluateResult;
begin
  { ... }
  Retorno := Thread.Evaluate(Expressao, '', 0, CanModify, True, '', Endereco, Tamanho, Resultado);
 
  if not (Retorno in [erError, erBusy]) then
    //AbrirVisualizador;
end;

 

Hora de juntar tudo!
Após seguir os seis passos, o resultado final da nossa codificação é apresentado abaixo (com comentários):

procedure TVisualizadorDataSets.Execute;
var
  ArquivoDados: string;
  TextoSelecionado: string;
  Expressao: string;
  Thread: IOTAThread;
  Retorno: TOTAEvaluateResult;
 
  // Variáveis para preencher os parâmetros "out" do Evaluate
  CanModify: boolean;
  Endereco: UInt64;
  Tamanho: Cardinal;
  Resultado: Cardinal;
begin
  // O arquivo de dados será gravado na pasta temporária do usuário
  ArquivoDados := System.IOUtils.TPath.GetTempPath + 'Dados.xml';
 
  // Obtém o texto selecionado no editor
  TextoSelecionado := (BorlandIDEServices as IOTAEditorServices).TopView.GetBlock.Text;
 
  // Monta a expressão que será avaliada pelo Debugger
  Expressao := Format('%s.SaveToFile(''%s'')', [TextoSelecionado, ArquivoDados]);
 
  // Obtém a thread do serviço de depuração
  Thread := (BorlandIDEServices as IOTADebuggerServices).CurrentProcess.CurrentThread;
 
  // Solicita a avaliação da expressão
  Retorno := Thread.Evaluate(Expressao, '', 0, CanModify, True, '', Endereco, Tamanho, Resultado);
 
  // Se a avaliação foi realizada com sucesso, abre o formulário para visualização dos dados
  if not (Retorno in [erError, erBusy]) then
    //AbrirVisualizador;
end;

Por que o método “AbrirVisualizador” está comentado?
Este método é justamente o que faremos na terceira parte dessa série de artigos! 😀

 

Hora de testar!
Para demonstrar a geração do arquivo através do nosso wizard, fiz um teste rápido. Criei um novo projeto, inseri um TClientDataSet e preenchi alguns dados em memória. No evento de clique de um botão, adicionei um breakpoint em uma linha que contém o DataSet e executei o projeto. Na parada do breakpoint, em tempo de execução, selecionei o DataSet:

Texto selecionado no Code Editor do Delphi

Em seguida, acionei o wizard pelo menu Help > Help Wizards > Visualizar DataSet.
Ao navegar até a pasta temporária do usuário, observe que o arquivo “Dados.xml” foi gerado conforme esperado!

Demonstração do arquivo de dados gerado pelo wizard

 

O que fizemos até agora?
Observe, com atenção, os destaques da imagem da janela Evaluate/Modify e as relações com a nossa codificação:

Janela de Evaluate/Modify durante o debugging no Delphi

  • (1) Thread que obtemos pelo método CurrentThread;
  • (2) Expressão para avaliação;
  • (3) Função Evaluate da Interface IOTAThread;
  • (4) Texto do resultado (segundo parâmetro da função Evaluate).

 

Volto amanhã com a criação do formulário para visualização dos dados.
Boa noite, leitores!


[Delphi] Criando um visualizador de DataSets na IDE com o ToolsAPI – Parte 1
[Delphi] Criando um visualizador de DataSets na IDE com o ToolsAPI – Parte 2
[Delphi] Criando um visualizador de DataSets na IDE com o ToolsAPI – Parte 3
[Delphi] Criando um visualizador de DataSets na IDE com o ToolsAPI – Parte 4


 

Compartilhe!
Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+Pin on PinterestEmail this to someone

2 comentários

  1. Boa tarde!
    André, seria interessante colocar teclas de atalho nesse wizard… Pesquisando aqui eu achei a interface “IOTAKeyboardBinding” seria essa mesmo para colocar atalhos?
    Abraço.

    1. Olá, Elton. Bem observado!
      Sim, com essa Interface é possível adicionar teclas de atalho personalizadas para as ações do menu. Por ser algo um pouco mais avançado, preferi não abordá-la nessa série de artigos, porém, posso elaborar uma 5ª parte extra para essa demonstração. 🙂
      Obrigado!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Preencha o campo abaixo * Time limit is exhausted. Please reload CAPTCHA.