|
Usuários |
|
62 Usuários Online
|
|
[Artigos]
[Avançado] - Utilizando as mensagens do Windows no Delphi |
Publicado por rboaro : Sexta, Junho 21, 2013 - 08:40 GMT-3 (773 leituras)
2 Comentários Enviar para um amigo Versão para impressão
|
Existem diversas maneiras de trocar informações entre aplicativos, como por exemplo, Sockets, TCP/IP, arquivo de texto (com uma rotina de monitoramento de diretório), etc. Uma maneira que achei interessante usar quando essa comunicação é somente local, é o uso de mensagens do Windows.
O Windows possui um número vasto de tipos de mensagens, como por exemplo a mensagem WM_QUIT que consiste em uma mensagem de encerramento de um aplicativo, ou a mensagem WM_ACTIVE que consiste em uma mensagem para alertar que a aplicação foi ativada no Windows, entre outras. É possível interceptar essas informações e também é possível enviar esse tipo de mensagens através do Delphi. Veremos a seguir como isso é possível e também nos aprofundaremos um pouco na API de mensagens do Windows.

Interceptando as mensagens do Windows:
No Delphi existe a diretiva message, que quando utilizada nos métodos, determina qual mensagem do Windows aquele método deve interceptar. Outra observação pertinente a se fazer é com relação a assinatura padrão de um método que interceptar as mensagens do Windows:
procedure MsgKeyDown(var Message : TMessage); message WM_KEYDOWN;
No exemplo acima, estamos criando um método que intercepta as mensagens do tipoVW_KEYDOWN (código 256), tipo de mensagem já previsto pelo Windows e que é utilizando a cada tecla que é pressionada. O conteúdo desta mensagem virá no parâmetro Message do tipo TMessage. O tipo TMessage consiste em um record do Delphi baseado na API do Windows, portanto, a declaração desse record está na unit Winapi.Messages.
O record TMessage possui algumas informações das quais destaco 3:
TMessage.MSG : código/tipo da mensagem.
TMessage.WParam : primeira mensagem principal.
TMessage.LParam : segunda mensagem principal.
Como primeiro exemplo, aproveitando os dados acima, iremos interceptar as mensagens WM_KEYDOWN e apresentaremos o seu conteúdo em um TMemo:
type
TfrmPrincipalReceptor = class(TForm)
Memo1: TMemo;
private
procedure MsgKeyDown(var Message : TMessage); message WM_KEYDOWN;
public
end;
implementation
procedure TfrmPrincipalReceptor.MsgKeyDown(var Message: TMessage);
begin
Memo1.Lines.Add(format('%d - %s',[message.WParam,char(Message.WParam)]));
end;
Desta forma, o nosso form (TfrmPrincipalReceptor) está interceptando as mensagens do Windows do tipo WM_KEYDOWN. Vale lembrar que por padrão todos os componentes visuais de alguma forma já interceptam essas mensagens e já possuem tratamentos distintos para cada uma, este mecanismo que estamos criando está direcionando essas mensagens para um tratamento específico.
Segundo a API do Windows, a mensagem WM_KEYDOWN, traz como informação no WParam o código de tecla virtual (VK_KEY) pressionada. Sendo assim, ao executar o código acima, a cada tecla pressionada no form, será adicionada uma linha no memo, desde que, o memo não esteja em foco, pois desta forma o próprio memo irá interceptar a tecla.

Basicamente o que foi feito, foi o mesmo que utilizar o evento OnKeyDown do formulário.
Trocando mensagens entre aplicações
É possível criar mensagens personalizadas e transmiti-las pelo sistema operacional, seja para uma aplicação específica ou até mesmo um broadcasting. Um cuidado essencial a se tomar quando estamos trocando mensagens do Windows entre aplicações é a escolha do código de identificação da mensagem. Segundo a API do Windows, os códigos de mensagens reservadas para uso do sistema operacional vão de 0 (WM_NULL) até 1024 (WM_USER).
Faremos um exemplo de troca de mensagens do Windows entre duas aplicações, começando com a aplicação receptora, ou seja, a aplicação responsável por receber a mensagem do Windows:
type
TfrmPrincipalReceptor = class(TForm)
Memo1: TMemo;
private const
MSG_DIRETA = WM_USER + 1;
private
procedure MsgsDiretas (var Message : TMessage); message MSG_DIRETA;
public
end;
implementation
procedure TfrmPrincipalReceptor.MsgsDiretas(var Message: TMessage);
begin
Memo1.Lines.Add(format('%d - %d',[message.WParam,Message.LParam)]));
end;
Note que praticamente não temos nenhuma novidade com relação ao exemplo anterior, com exceção do tipo da mensagem que estamos interceptando, MSG_DIRETA, que é o mesmo que 1025. Fazemos isso para que nosso método não intercepte mensagens transmitidas pelo sistema operacional ou por outros comportamentos que fogem do nosso domínio.
Transmitir uma mensagem do windows é algo muito simples, basta utilizar as funções SendMessage ou PostMessage da própria API do windows. Ambas funções esperam os seguintes parâmetros:
hWnd: Handle da aplicação que deverá receber a mensagem.
Msg: Código do tipo de mensagem enviada.
wParam: Primeira parte da mensagem.
lParam: Segunda parte da mensagem.
A diferença entre as duas fica por conta da forma de processamento destas, sendo uma síncrona (SendMessage) e outra assíncrona (PostMessage), ou seja, ao transmitir uma mensagem com o método SendMessage a aplicação transmissora da mensagem fica aguardando o processamento desta na aplicação receptora, enquanto que com o método PostMessage, após a mensagem ser transmitida, o processamento na aplicação transmissora continua normalmente.
Voltando ao nosso exemplo, antes do envio da mensagem, é necessário descobrir o handle da janela que irá receber a mensagem, faremos isso com o método FindWindow.
procedure TfrmPrincipalTransmissor.EnviarMsgDireta;
const
MSG_DIRETA = WM_USER + 1;
var
hReceptor : THandle;
begin
hReceptor := FindWindow('TfrmPrincipalReceptor',nil);
if hReceptor > 0 then
PostMessage(hReceptor,MSG_DIRETA,123,321);
end;
Basicamente estamos localizando o handle da aplicação receptora e enviando uma mensagem do tipo MSG_DIRETA, tipo definido por nós. Vale ressaltar que localizamos o handle através da classe do formulário que iria receber a mensagem. O método Findwindow recebe dois parâmetros, o primeiro é o nome da classe da janela receptora e o segundo é o caption da janela.
Com o código acima, já estamos transmitindo a mensagem 123 em LParam e 321 emWParam.
Transmitindo texto como mensagem
Muitas vezes, transmitir somente números não é o suficiente e torna o protocolo mais complexo e mais limitado. Mas existe uma forma de converter os parâmetros LParam e WParam em textos, através do método GlobalAddAtom(). O Método GlobalAddAtom é um método da API do Windows que adiciona um texto na tabela global de átomos do Windows. Basicamente ele adiciona o texto em um tabela do sistema operacional e retorna um código para identificar o texto dentro desta tabela. Desta forma, basta adicionar um texto nesta tabela, enviar o código para o aplicativo receptor e neste recuperar o texto da tabela de acordo com o código. Vejamos como isso poderia funcionar na prática, começando com o aplicativo transmissor:
procedure TfrmPrincipal.EnviarMsgDiretaComTexto();
var
wParam : word;
wValor : word;
hReceptor: THandle;
begin
wParam := GlobalAddAtom('Parametro');
wValor := GlobalAddAtom('Valor');
hReceptor := FindWindow('TfrmPrincipalReceptor',nil);
if hReceptor > 0 then
SendMessage(hReceptor,Self.MSG_DIRETA,wParam,wValor);
GlobalDeleteAtom(wParam);
GlobalDeleteAtom(wValor);
end;
Vale aqui algumas observações. Repare que neste exemplo optei pelo uso do SendMessage, isso se deve ao fato de que a tabela de átomos do Windows não é limpa automaticamente ao final da execução do aplicativo, ou seja, todos os textos adicionados nesta tabela, ficarão disponíveis até o encerramento do sistema operacional, ou até que seja solicitada a remoção do texto desta tabela, para isso, existe o método GlobalDeleteAtom().
Para recuperar esta mensagem, basta utilizar o método GlobalGetAtomName(). Em nosso exemplo, poderia ser feito desta forma:
procedure TfrmPrincipalReceptor.MsgsDiretas(var Message: TMessage);
var
sParam: String;
sValor : string;
begin
SetLength(sParam,255);
SetLength(sParam,GlobalGetAtomName(Message.wParam,Pchar(sParam),255));
SetLength(sValor,255);
SetLength(sValor,GlobalGetAtomName(Message.lParam,Pchar(sValor),255));
Memo1.Lines.Add(format('%s - %s',[sParam,sValor)]));
end;
Com a execução dos códigos acima (transmissor e receptor) teremos no Memo1 o texto: Parametro – Valor.
Registrando um código de mensagem no Windows.
Esse processo de troca de mensagens como descrito acima, apresenta um risco com relação ao código da mensagem. É possível que outra aplicação de terceiros faça uso do mesmo código de mensagem que você pretende utilizar. Para contornar essa situação, é possível registrar um tipo de mensagem no Windows, através do métodoRegisterWindowMessage(). O RegisterWindowMessage é mais um método da API do Windows que registra mensagens no sistema operacional exatamente com o intuito de facilitar a comunicação entre aplicações. O tipo de mensagem é registrado a partir de um texto identificador.
Adaptando nosso exemplo do aplicativo receptor para tratar uma mensagem registrada através do método RegisterWindowMessage, teremos que sobrescrever o método WndProc do nosso formulário. Esse método é responsável por receber as mensagens do Windows em um componente descendente da classe TWinControl.
type
TfrmPrincipalReceptor = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
FMsgDireta : cardinal;
protected
procedure WndProc(var Message: TMessage); override;
public
end;
implementation
procedure TfrmPrincipalReceptor.FormCreate(Sender: TObject);
begin
Self.FMsgDireta := RegisterWindowMessage('MsgDireta');
end;
procedure TfrmPrincipalReceptor.WndProc(var Message: TMessage);
begin
if Message.Msg = Self.FMsgDireta then
begin
Memo1.Lines.Add(format('%d - %d',[message.WParam,Message.LParam]));
end
else
inherited WndProc(Message);
end;
Note que no evento OnCreate do formulário eu registro a mensagem MsgDireta no Windows e armazeno o código retornado do método RegisterWindowMessage no atributo FMsgDireta, esse código é a referencia deste tipo de mensagem no sistema operacional.
No evento WndProc que foi sobrescrito, eu valido o tipo da mensagem, caso seja a mensagem que registrei eu a recupero, caso contrário, invoco o método WndProc na classe ascendente.
No aplicativo transmissor, basta registrar a mesma mensagem no sistema operacional e usar o identificador como código de mensagem, assim como foi feito nos outros exemplos.
Transmitindo um broadcast
Podem existir situações em que seja necessário transmitir a mesma mensagem para N aplicativos, como também, existem situações ondem não se sabe ao certo quem deverá receber a mensagem. Para resolver essas questões, é possível transmitir uma mensagem como broadcast.
Em termos de código, a alteração fica por conta do direcionamento da mensagem, ou seja, ao invés de enviarmos a mensagem para um handle especifico, utilizaremos a constante HWND_BROADCAST:
PostMessage(HWND_BROADCAST,MSG_BROADCAST,123,321);
Segundo a api do Windows, quando uma mensagem é enviada como broadcast, esta é transmitida para todas janelas que estejam no nível superior do sistema operacional, inclusive janelas que estejam invisíveis.
Registrando e trabalhando com HOT KEYS
Em determinados tipos de aplicação, é interessante o uso de teclas de atalho para determinadas ações. Essas são chamadas de HotKeys. As HotKeys são mensagens do Windows com o código $0312, mapeado através da constante VW_HOTKEY. Basicamente para utilizar HotKeys é necessário registrar uma combinação de teclas que será efetivamente nosso atalho e interceptar as mensagens do tipo VW_HOTKEY.
Para registar uma combinação de teclas como HotKey, existe o método RegisterHotKey() da API do Windows. O método RegisterHotKey recebe o seguintes parâmetros:
hWnd: Handle da janela que irá receber a mensagem do Windows quando a HotKey for acionada.
id: Id da HotKey, utilizado para diferenciar uma HotKey da outra.
fsModifiers: Modificadores de tecla de atalho, que consiste nas teclas que serão combinadas com a tecla especificada no parametro vk. É neste parametro onde podemos fazer referencia as tecla Alt (MOD_ALT), Control (MOD_CONTROL) etc.
vk: A tecla que será utilizada em conjunto com os modificadores para formar a HotKey.
Como exemplo, registraremos a combinação de teclas Ctrl + Alt + D como sendo uma HotKey:
procedure TfrmHotKey.FormCreate(Sender: TObject);
begin
RegisterHotKey(Self.Handle,Self.HOT_KEY_EXEMPLO,MOD_CONTROL + MOD_ALT,ord('D'));
end;
A constante, HOT_KEY_EXEMPLO foi definida no private do formulário com o valor de 1, semelhante ao que fizemos com a constante MSG_DIRETA. Sempre prefiro usar constantes ao invés de “números mágicos”, acredito que assim o código se torna mais claro.
Agora com nossa HotKey definida, precisamos interceptá-la. Da mesma forma que fizemos com as mensagens comuns, faremos com a HotKey, mas interceptando as mensagens do tipo WM_HOTKEY:
type
TfrmHotKey = class(TForm)
procedure FormCreate(Sender: TObject);
private const
HOT_KEY_EXEMPLO = 1;
public
procedure CapturarHotKeys (var Msg: TWMHotKey); message WM_HOTKEY;
end;
Note que o parâmetro da mensagem é do tipo TWMHotKey, e não mais do tipo TMessage,porem, a forma de lidar com a mensagem é muito semelhante:
procedure TfrmHotKey.CapturarHotKeys(var Msg: TWMHotKey);
begin
if Msg.HotKey = Self.HOT_KEY_EXEMPLO then
ShowMessage('HotKey pressionada');
end;
Para desregistrar a HotKey, basta utilizar o método UnRegisterHotKey():
UnregisterHotKey(Self.Handle,Self.HOT_KEY_EXEMPLO);
Apesar do BOOM do multiplataforma que o Delphi está passando, é sempre bom conhecer esse tipo de possibilidade tão poderosa.
Diego Garcia
http://drgarcia1986.wordpress.com
http://twitter.com/drgarcia1986
|
|
Comentários | |
| | Comentários pertencem aos seus respectivos autores. Não somos responsáveis pelo seus conteúdos. |
|
|
Edição 112 |
|
|
50 Programas Fontes |
|
|
Produtos |
|
|