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!
Notificador na thread
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
?
Bom, 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:
1 2 3 4 5 6 7 8 9 10 11 12 |
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
1 2 3 4 5 6 |
type TVisualizadorDataSets = class(TInterfacedObject, IOTAWizard, IOTAMenuWizard, IOTAThreadNotifier) private FProcessamentoFinalizado: boolean; { ... } |
3) Atribuir True ao término da avaliação da expressão
1 2 3 4 5 6 |
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:
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 |
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 Group, 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.
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] 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
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! 😉
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:
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!
Excelente artigo, exemplifica muito bem o que é possível fazer. Obrigado.
Obrigado, Anderson! O OpenTools API é bsatante versátil.
Abraço!
Excelente artigo.
Será lançado mais algum artigo sobre Open Tools API? Seria muito interessante, pois pouco se encontra de documentação acerca desse assunto.
Olá, João Antônio!
Sim, haverá mais artigos! O Open Tools API é um universo de possibilidades.
A documentação realmente é um pouco escassa. Por isso que muitos desenvolvedores ainda desconhecem este recurso.
Abração!
Olá André, esse artigo foi muito útil, usando como base o que você mostrou aqui, eu fiz meu próprio wizard para customizar trechos de código dentro do editor, para ficar num padrão que nós adotamos aqui na empresa, mas eu não consegui colocar atalho e nem criar um menu igual a esse da DB1. Na segunda parte alguém comentou sobre o IOTAKeyboardBinding, eu implementei os métodos na minha classe mas mesmo assim não funcionou, que tal uma quinta parte nesse artigo? rsrsrs
Olá, Valdeir, boa noite!
Ótima ideia! Vou colocar esse tema na pauta de artigos. Na verdade, eu também tive dificuldades na utilização dessa Interface, portanto, a elaboração desse artigo seria um aprendizado até para mim, rsrs.
Obrigado pela sugestão!