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

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

Finalmente chegamos ao fim dessa pequena série sobre Open Tools API. Agradeço a todos vocês por terem acompanhado e compartilhado os artigos!
Essa última parte, na verdade, envolve a implementação de uma melhoria para solucionar um problema de conflito de acesso ao arquivo de dados gerado. Para isso, utilizaremos uma Interface do Open Tools API chamada IOTAThreadNotifier. Let’s do it!

 

Em termos gerais, o nosso wizard já está praticamente pronto, porém, instável. Conforme mencionado no artigo anterior, o desenvolvedor pode receber eventuais erros informando que o arquivo já está sendo utilizado por outro processo, que, neste caso, é a própria geração do arquivo. Quando isso ocorre, significa que a avaliação da expressão demorou um pouco mais do que o esperado.

Como solução, adicionaremos um notificador na thread para que seja possível identificar o término da avaliação da expressão.
Lembram-se que mencionei os valores de retorno do método Evaluate na segunda parte da série? Apenas para recordá-los, são eles: erOK, erError, erDeferred e erBusy. Observem também que, antes de abrir o visualizador, verifico se os valores são diferentes de erError e erBusy. Bom, já que erOK indica que foi a avaliação foi executada com sucesso, então o que seria o erDeferred?

Tem razão… o que é?
Este retorno indica que o método Evaluate está utilizando threads adicionais e/ou acessando outros contextos para avaliar a expressão, logo, não podemos considerá-lo como um erro de execução. O nosso dever é adicionar uma instrução de espera até que a avaliação seja concluída. Pois bem, a Interface IOTAThreadNotifier nos fornece esse mecanismo. Acompanhe os passos.

 

1) Incluir IOTAThreadNotifier na lista de implementações da classe
Além de IOTAWizard e IOTAMenuWizard, adicionaremos também a Interface IOTAThreadNotifier na lista de implementações, na qual exige a implementação de três métodos:

type
  TVisualizadorDataSets = class(TInterfacedObject,
    IOTAWizard, IOTAMenuWizard, IOTAThreadNotifier)
  private
    { ... }
 
    { Assinaturas do IOTAThreadNotifier }
    procedure EvaluteComplete(const ExprStr: string; const ResultStr: string;
      CanModify: Boolean; ResultAddress: Cardinal; ResultSize: Cardinal; ReturnCode: Integer);
    procedure ModifyComplete(const ExprStr: string; const ResultStr: string; ReturnCode: Integer);
    procedure ThreadNotify(Reason: TOTANotifyReason);
  end;

Porém, o único método que nos interessa é EvaluteComplete. Curiosamente, o nome do método está incorreto – falta um “a” em “Evalute”. Mesmo assim, pelo nome, já podemos deduzir que este método é chamado sempre que o depurador termina a avaliação de uma expressão. É o que precisamos. Com esse recurso, faremos o seguinte: quando o retorno do método Evaluate for erDeferred, utilizaremos uma variável boolean para identificar que a avaliação da expressão foi concluída, definindo-a como True no método EvaluteComplete.
Vamos prosseguir com os próximos passos.

 

2) Declarar a variável boolean na seção private

type
  TVisualizadorDataSets = class(TInterfacedObject,
    IOTAWizard, IOTAMenuWizard, IOTAThreadNotifier)
  private
    FProcessamentoFinalizado: boolean;
    { ... }

 

3) Atribuir True para a variável boolean no término da avaliação da expressão (método EvaluteComplete)

procedure TVisualizadorDataSets.EvaluteComplete(const ExprStr,
  ResultStr: string; CanModify: Boolean; ResultAddress, ResultSize: Cardinal;
  ReturnCode: Integer);
begin
  FProcessamentoFinalizado := True;
end;

 

4) Alterar o método Execute para adicionar a rotina de espera
Nesse passo, faremos uso de um método chamado ProcessDebugEvents dentro de uma instrução while para que a thread processe os eventos de depuração pendentes. Este método pode ser comparado ao Application.ProcessMessages que utilizamos em algumas ocasiões. Além disso, como a nossa classe já implementa a Interface IOTAThreadNotifier, podemos adicioná-la como “observadora” da thread, de forma que o EvaluteComplete seja chamado para alterar o valor da nossa variável. Confira a codificação a seguir:

var
  { ... }
  IndiceNotifier: integer;
begin
  { ... }
  Retorno := Thread.Evaluate(Expressao, '', 0, CanModify, True, '', Endereco,
    Tamanho, Resultado);
 
  if Retorno = erDeferred then
  begin
    FProcessamentoFinalizado := False;
 
    // Adiciona um notificador, retornando um índice para que depois possamos removê-lo
    IndiceNotifier := Thread.AddNotifier(Self);
 
    // Processa os eventos pendentes do depurador até que EvaluteComplete seja chamado
    while not FProcessamentoFinalizado do
      (BorlandIDEServices as IOTADebuggerServices).ProcessDebugEvents;
 
    // Remove o notificador após a conclusão da avaliação
    Thread.RemoveNotifier(IndiceNotifier);
  end;
 
  if not (Retorno in [erError, erBusy]) then
    AbrirVisualizador;    
end;

Problema solucionado! Com essa alteração, certificamos de que o arquivo sempre estará disponível quando o formulário carregá-lo.

 

Melhorias no wizard
Pessoal, o principal objetivo dessa série de artigos foi apresentar e demonstrar os recursos do Open Tools API, mas, mesmo assim, nada impede que você aplique algumas melhorias no código, como condições de guarda para verificar se existe um texto selecionado no editor, bem como garantir que o processo de depuração (CurrentProcess) e a thread (CurrentThread) estão acessíveis ao acionar o menu.
No formulário de dados, por sua vez, uma boa ideia é adicionar novos campos para exibir o filtro do DataSet (propriedade Filter), índices (propriedade IndexFieldNames) e a quantidade de registos (propriedade RecordCount). Quanto mais informações para análise, melhor! 🙂

 

O wizard da DB1
Na DB1, empresa em que trabalho, desenvolvi um wizard com o Open Tools API, mas não somente para criar um visualizador de DataSets. Tirei proveito das Interfaces para adicionar também atalhos para ferramentas externas, configurações de acesso às bases de dados, compilações de conjuntos de projetos e algumas rotinas de apoio específicas do projeto que trabalhamos. Só pelo fato de estarem acessíveis diretamente na IDE do RAD Studio, conforme demonstrado na imagem abaixo, contribuiu muito para a produtividade da equipe de desenvolvimento.

Imagem do wizard desenvolvido para a DB1

 

Fico por aqui, amigos! Toda a codificação abordada nessa série de artigos está disponível no GitHub:

https://github.com/AndreLuisCelestino/Exemplos-Blog/tree/master/PacoteWizard

Espero essa série de artigos tenha sido útil. Nos vemos em breve!


[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. ola, Andre,

    Venho de novo para comentar no msm artigo, rs.

    Apesar de achar fantástico, só tive tempo de instalar agora, e no meu caso berlin up 2 nao funcionou! 🙁
    A primeira coisa q eu te digo é q se eu criar um projeto novo multi device por exemplo, e logo em seguida ir no meu para clicar na opção recebo uma mensagem erro (AV), acredito q falte algum tratamento lá, não sei.

    No segundo caso, é quando eu seleciono um DataSet em runtime, igual vc fez e escolho a opção, nesse momento o Delphi fica não respondendo.
    A única coisa q eu posso te dizer é q é firedac, mas apesar de tudo, n deixa de descender de um DataSet, rs.

    Não sei se ajuda, acho q n, mas é sqlite (local).
    Não sei se consegui ajudar muito e também n consigo imaginar onde pode estar a falha, vc tem ideia?

    Achei um projeto q é concorrente do seu, rsrsrs
    https://github.com/darianmiller/dxIDEPackage_TDataSetVisualizer

    esse achei legal pq coloca um botao VISUALIZERs ali mesmo, dispensando acessar o menu no Delphi.
    Acessando um método genérico DATASET não deu erro, mas a grid veio vazia.
    Já acessando com o FDQUERY direto, funcionou corretamente.

    fica a dica! 😉

    1. Olá, Conde, tudo certo?

      A ideia dessa série de artigos foi apenas apresentar o potencial das Interfaces do Open Tools API. A codificação do visualizador de DataSets foi abordada de forma bastante básica para que os artigos não ficassem extensos demais. Observe que no final do artigo faço algumas ressalvas quanto às melhorias do wizard, como adicionar condições de guarda.

      O Access Violation provavelmente ocorreu porque o projeto não estava em tempo de execução. Neste caso, o wizard tentou buscar o processo de depuração, mas não o encontrou. Para solucioná-lo, basta verificar se a função CurrentProcess retorna um ponteiro válido, como exemplificado abaixo:

      var
        Processo: IOTAProcess;
       
      ...
      
      Processo := (BorlandIDEServices as IOTADebuggerServices).CurrentProcess;
      
      if not Assigned(Processo) then
      begin
        ShowMessage('O projeto não está em tempo de execução.');
        Exit;
      end;

      A respeito do FireDAC, o componente TFDQuery herda de TDataSet, mas não possui o método SaveToFile, no qual utilizamos no Evaluate. Este método está disponível somente para os DataSets que herdam de TCustomClientDataSet.
      Para que este visualizador funcione para componentes TFDQuery (e outros componentes similares), alguns ajustes devem ser aplicados no código.

      Obs: muito bom esse visualizador do GitHub! Vou baixá-lo para estudar o código.

      Abraço!

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.