O advento da programação visual - onde você simplesmente arrasta botões, caixas de edição e outros componentes para desenhar as janelas de uma aplicação e responder a eventos associdados a eles - escondeu do programador um fato básico a respeito do funcionamento do Windows : tudo em uma janela acontece através do envio de mensagens. Desde a criação e destruição da janela, passando pela pintura e alterações no tamanho até o clique de botões, digitação de textos e mesmo o simples passar do mouse por sobre a janela geram uma infinidade de eventos chamados de "Mensagens".
Cada janela é criada com uma fila interna onde as mensagens recebidas são colocadas pelo Windows para que a janela possa recuperá-las e tratá-las. Por isso, nos primórdios da programação para Windows, todo programa era montado em cima de um laço para obter cada mensagem e uma estrutura do tipo switch/case para tratar cada uma delas - muitas tratadas automaticamente pelo próprio Windows, como mover ou redimensionar a janela. Hoje, esse mecanismo continua existindo mas é encapsulado por classes dentro de frameworks como a VCL ou o .NET.
Uma mensagem é constituida de um código que a define (ex: WM_PAINT quando a janela precisa ser desenhada ou WM_MOUSEMOVE quando passamos o mouse sobre uma janela), o handle que indica a qual janela a mensagem é direcionada e parâmetros com informações extras para complementar a descrição do evento. O significado dos parâmetros varia de acordo com a mensagem.
Além das mensagens definidas pelo próprio sistema operacional, é permitido às aplicações registrar tipos de mensagens privadas, cujo siginificado é restrito ao escopo da própria aplicação. Foi isso que fiz no programa de exemplo das Threads, declarando duas novas mensagens:
var
WM_NOTIFY_THREAD_STATUS : Cardinal;
WM_NOTIFY_THREAD_TERMINATED : Cardinal;
{ ... }
WM_NOTIFY_THREAD_STATUS := RegisterWindowMessage ('WM_NOTIFY_THREAD_STATUS');
WM_NOTIFY_THREAD_TERMINATED := RegisterWindowMessage ('WM_NOTIFY_THREAD_TERMINATED');
WM_NOTIFY_THREAD_STATUS : Cardinal;
WM_NOTIFY_THREAD_TERMINATED : Cardinal;
{ ... }
WM_NOTIFY_THREAD_STATUS := RegisterWindowMessage ('WM_NOTIFY_THREAD_STATUS');
WM_NOTIFY_THREAD_TERMINATED := RegisterWindowMessage ('WM_NOTIFY_THREAD_TERMINATED');
O Windows atribuirá ao nome passado para a função RegisterWindowMessage um código interno de modo que novas chamadas a ela sempre retornarão o mesmo valor. Não é o caso do exemplo mas eu poderia ter usado esse mecanismo para comunicar com outro programa meu em execução no mesmo computador e que registrasse uma mensagem com o mesmo nome.
Com a mensagem registrada, minha Thread pode notificar o seu status à janela principal enviando a ela uma mensagem. Cada Thread de classificação no exemplo é associada a uma estrutura chamada _Status que, entre outras coisas, possui o handle da janela do programa, usado no envio da mensagem.
PostMessage (_Status.OwnerForm.Handle,
WM_NOTIFY_THREAD_STATUS,
Integer (_Status), 0);
WM_NOTIFY_THREAD_STATUS,
Integer (_Status), 0);
A função PostMessage da API do Windows coloca uma mensagem na fila de mensagens da janela indicada pelo Handle mas não aguarda seu tratamento. Os demais parâmetros são o código da mensagem e informações adicionais específicas dessa mensagem - no caso, o ponteiro para toda a estrutura de status da Thread.
No caso do Delphi e do C++ Builder, o tratamento de mensagens direcionadas a uma janela pode ser estendido criando uma sobreposição da função WndProc, que existe em todas as janelas.
{ Definição na classe da Janela }
protected
procedure WndProc(var Message: TMessage);override;
{ ... }
{ Corpo da função }
procedure TWSyncThread.WndProc(var Message: TMessage);
var lMsg : TWMsgNotifyStatus;
begin
if Message.Msg = WM_NOTIFY_THREAD_STATUS then
begin
lMsg := TWMsgNotifyStatus(Message);
lblMsg.Caption := lMsg.Status.Texto;
{ ... }
lMsg.Result := -1;
end else
inherited;
end;
protected
procedure WndProc(var Message: TMessage);override;
{ ... }
{ Corpo da função }
procedure TWSyncThread.WndProc(var Message: TMessage);
var lMsg : TWMsgNotifyStatus;
begin
if Message.Msg = WM_NOTIFY_THREAD_STATUS then
begin
lMsg := TWMsgNotifyStatus(Message);
lblMsg.Caption := lMsg.Status.Texto;
{ ... }
lMsg.Result := -1;
end else
inherited;
end;
Veja o uso da palavra-chave inherited. Isto repassa o tratamento de todas as outras mensagens à mesma função existente na classe pai (a própria classe TForm). Sem isso, a janela deixa de funcionar.
A estrutura TWMsgNotifyStatus é definida por mim para conter os parâmetros esperados por mensagens do tipo WM_NOTIFY_THREAD_STATUS. A estrutura é construída de forma que os parâmetros passados à função PostMessage se encaixem nela, facilitando o acesso e a interpretação do significado de cada parâmetro.
Mais Informações
Laço de mensagens no Windows, Função PostMessage, Sincronização com Eventos em Threads - parte 1 e parte 2, Download do código do programa exemplo com Threads
Laço de mensagens no Windows, Função PostMessage, Sincronização com Eventos em Threads - parte 1 e parte 2, Download do código do programa exemplo com Threads
Um comentário :
muito bom, parcero..
Postar um comentário
OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.
Observação: somente um membro deste blog pode postar um comentário.