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?
Evaluate
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:
1 |
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. Com a ú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
:
1 2 3 4 5 6 7 8 |
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
:
1 2 3 4 5 6 |
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:
1 2 3 4 5 6 7 8 9 10 |
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
:
1 2 3 4 5 6 |
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:
1 2 3 |
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:
1 2 3 4 5 6 7 8 9 10 |
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.
1 2 3 4 5 6 7 8 9 10 |
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):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
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:
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!
Para finalizar a compreensão do 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:
- (1) Thread que obtemos pelo método
CurrentThread
; - (2) Expressão para avaliação;
- (3) Função
Evaluate
da InterfaceIOTAThread
; - (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] Visualizador de DataSets com Open Tools API – Pacote
[Delphi] Visualizador de DataSets com Open Tools API – Evaluate
[Delphi] Visualizador de DataSets com Open Tools API – Formulário
[Delphi] Visualizador de DataSets com Open Tools API – Notificador
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.
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!
Boa noite, estou tentando contribuir com um projeto de um plugin que faz essa parte descrita no blog e agora estou tentando pegar o valor do CommandText do ClientDataset e estou com dificuldade de ler a resposta.
As vezes funciona, EvalRes = erOK, mas na maioria das vezes o retorno é erBusy!
Alguma dica???
O material menos ruim que eu encontrei: https://medium.com/soundvibe/tcolor-visualizer-ee2b230f9143
Olá, Ramyres!
Peço desculpas pela demora.
O seu código aparentemente está correto. O problema é que algumas vezes (ou na maioria delas), a thread responsável por avaliar a expressão está processando outras mensagens ou demorando mais que o usual. Uma das soluções é criar um notificador responsável por “avisar” quando a avalição estiver pronta. Na parte 4 dessa série eu comento sobre essa Interface, porém, ela só existe nas versões mais recentes do Delphi.
Se você tiver outras dúvidas ou alguma dificuldade, me envie um e-mail no endereço [email protected]!
Abraço!