1. La programmation dirigée par les
messages
2. Mécanisme du "Cracking" des messages en Delphi
3. Création et envoi de message en Delphi
La programmation événementielle
peut être présentée comme deux attitudes à adopter
lors de la construction d'une application dans un système comme Windows
:
Dans les deux cas la notion
de messages est présente, ce sont donc des outils de base de ce type
de programmation.
1.1 Que sont et à
quoi servent les messages
Les messages sont la base de la communication à l'intérieur du système d'exploitation. Ils sont le résultat d'événements que le système intercepte ou de communications du système avec ses différents composants. |
Certaines actions extérieures
ou intérieures au système déclenchent des événements.
Windows génère alors en continu, un flot de messages en direction
des applications ou vers d'autres parties du système lui-même
dans le cas de messages internes. Comme son nom l'indique Windows s'intéresse
tout particulièrement aux fenêtres, donc pour chaque fenêtre
présente, ces messages sont stockés dans une file d'attente
(de type FIFO) et sont traités au fur et à mesure par le système.
Certains messages de cette file d'attente sont associés à des événements particuliers. Un tel message peut ainsi être récupéré par une application : nous dirons alors que l'application "réagit à l'événement". |
Rappellons que si l'on
se place soit du point de vue de l'utilisateur, soit de celui d'une application
(ce qui est notre cas), Windows devient alors semblable à une grande boucle qui attend un événement
d'où qu'il vienne, le stocke dans la file des messages, puis le traite.
Voici pour une fenêtre le pseudo-comportement de Windows :
tantque non
ArrêtSysteme faire si événement alors construire Message ; si Message ¹ ArrêtSysteme alors reconnaître la fenêtre à qui est destinée ce Message; distribuer ce Message fsi fsi ftant |
Dans ce document, la
partie traitement des messages destinés
à un contrôle d'une fenêtre ou d'une application Delphi,
est le seul mécanisme qui nous intéresse dans notre programmation
événementielle. Nous pouvons utilement et très schématiquement,
matérialiser un tel traitement sous la forme de la boucle suivante
:
tantque FIFO
des messages non vide faire lire en tête de FIFO le Message; construire une structure associée à ce Message; utiliser la méthode interne de traitement sur ce Message ftant |
Remarque
Pendant que ce traitement a lieu, Windows continue en outre à intercepter d'autres événements et à stocker dans la file d'attente les messages associés à ces événements et les messages internes. |
En résumé nous pouvons dire que :
Le système envoie ou poste des messages prédéfinis à une application, mais réciproquement une application peut envoyer ou poster des messages vers d'autres fenêtres ou d'autres applications. |
Chaque message système dispose d'un identificateur unique du message. Il correspond à une constante numérique de base de Windows. Chaque identificateur de message est associé à une action spécifique.
Le classement des messages dans Windows s'effectue par un préfixe qui permet de déterminer la catégorie à laquelle appartient le message.
Soit par exemple le message dont l'identificateur est WM_KEYDOWN, il appartient à la catégorie des messages généraux de Windows WM_xxx. C'est un message associé à l'appui d'une touche de clavier. Le préfixe de l'identificateur indique le type de fenêtre qui peut intercepter et donc réagir à ce message.
Les messages WM_xxx sont des Windows
Messages donc destinés à
toutes les fenêtres.
Les messages LB_xxx sont des List
Box messages destinés aux boîtes
de listes.
Les messages EM_xxx sont des Edit Messages destinés
aux contrôles d'édition.
Les messages SBM_xxx sont des Scroll
Barre Messages
destinés aux barres de défilement.
etc...
L'aide en ligne de
l'API W32 de Windows nous fournit à toutes fins utiles, la structure
générale d'un message de Windows :
voici cette structure
en C :
typedef
structtagMSG { // msg
|
voici cette structure
en Delphi :
type
|
Ci-dessous la signification
des champs de la structure d'un message :
hwnd = la référence
de la fenêtre à qui est destinée le message.
message = la constante numérique identifiant le message. wParam = entier positif codé sur quatre octets [0..4294967295] représentant un premier paramètre dont la signification dépend de la catégorie du message. lParam = entier quelconque codé sur quatre octets [–2147483648..2147483647 ] représentant un deuxième paramètre dont la signification dépend de la catégorie du message. time = heure système à laquelle le message a été créé par Windows. pt = position du curseur de souris en coordonnées d'écran (pt.x, pt.y :Longint) |
Si vous tapez sur la touche "M" du clavier, une série de messages est envoyée en cascade :
Un message WM_KEYDOWN est automatiquement envoyé par Windows dans la file d'attente de la fenêtre détenant la focalisation (nommons la FenWin), dès que la touche est enfoncée, et ceci tant que la touche est enfoncée.
Ensuite Windows envoie le message WM_CHAR (qui contient le caractère entré au clavier) dans la même file; puis lorsque vous relâchez la touche, il envoie enfin un message WM_KEYUP.
Que contient par exemple, la structure TMsg lors du traitement de ce message WM_KEYDOWN (information obtenue à partir de l'aide en ligne de l'API Win32):
hwnd: référence
(pointeur,adresse) de la fenêtre FenWin.
message: WM_KEYDOWN,(valeur numérique=256)
wParam: = 77 (code
virtuel de la touche M ici)
lParam: les 32 bits
de ce mot indiquent les informations suivantes :
Une application Delphi est basée sur une fenêtre d'application. Nommons notre application FenDelphi; elle est donc considérée par Windows comme une fenêtre quelconque et à ce titre elle peut recevoir et envoyer des messages.
Une application Delphi possède des outils d'interception de la structure TMsg, de son décodage et de son interprétation. Nous avons déjà vu que Delphi disposait, à l'attention du programmeur, pour chaque objet et pour certains événements, de gestionnaires d'événement. Ce sont des outils de premier niveau (en fait les plus abstraits et les plus encapsulés)de gestion des messages. L'inspecteur d'objet nous a permis de nous familiariser avec de tels gestionnaires dans son onglet événements.
Onglet - événements
Si nous reprenons l'exemple précédent de l'appui sur la touche "M" du clavier lorsque FenDelphi détient la focalisation, nous savons que Windows va construire la structure TMsg précédente et l'ajouter dans la file d'attente des messages de FenDelphi.
Supposons que dans notre application FenDelphi nous ayons déposé sur la fiche Form1 deux contrôles visuels Edit1 et Edit2 de la classe des TEdit, et que ce soit Edit1 qui détienne le focus lors du lancement de l'application FenDelphi :
Edit détient
le focus dans Form1 de FenDelphi
Nous souhaitons faire réagir Edit1 à l'appui sur la touche "M" du clavier de la façon suivante : dès que l'utilisateur appuie sur une touche du clavier dans Edit1, il apparaît dans Edit2 le code de ce caractère.
Nous allons utiliser le gestionnaire de l'événement OnKeyDown de Edit1 :
procedureTForm1.Edit1KeyDown(Sender:
TObject; var Key: Word;
Shift: TShiftState);
begin
Edit2.text:=inttostr(Key)//code du caractère entré dans Edit, recopié
dans Edit2
end;
l'utilisateur a appuyé
sur la touche "m"
type
TKeyEvent = procedure (Sender: TObject; var Key: Word; Shift: TShiftState) of object;
property OnKeyDown:
TKeyEvent;
Il existe donc un mécanisme
interne à Delphi qui a permis à notre application de reconnaître
que Windows avait envoyé le message WM_KEYDOWN
dans la structure TMsg, ce mécanisme a permis aussi d'appeler
le gestionnaire TForm1.Edit1KeyDown que
nous avons écrit afin d'effectuer la recopie du code dans Edit2.
2. Mécanisme du "Cracking" des messages en Delphi
Delphi répartit les messages de plusieurs façons :
Chaque composant hérite d'un système complet de répartition de message. Toutes les classes Delphi disposent d'un mécanisme intégré pour gérer les messages : ce sont les méthodes de gestion des messages ou gestionnaires de messages, ces méthodes sont choisies dans un ensemble de méthodes spécifiques.
Le système de répartition de message dispose d'une gestion par défaut. Vous ne définissez de gestionnaire que pour les messages auxquels vous souhaitez spécifiquement répondre. Un gestionnaire par défaut est appelé si aucune méthode n'est définie pour le message.
Le diagramme suivant illustre une partie essentielle du fonctionnement du système de répartition de message dans Delphi et les différents niveaux d'interception possibles d'un message :
property OnMessage:
TMessageEvent;
procedure MainWndProc(var
Message: TMessage);
procedure WndProc(var
Message: TMessage); virtual;
procedure Dispatch(var
Message); virtual;
procedure ABCxxx(var
Message: TMessage);message ABC_xxx;
procedure DefaultHandler(var
Message); virtual;
Si vous voulez intercepter tout ou partie des messages Windows expédiés à toutes les fenêtres de l'application Delphi, utilisez l'événement OnMessage de la classe TApplication qui encapsule toute application Delphi. Cet événement OnMessage est déclenché dès que votre application reçoit un quelconque message de Windows.
property OnMessage:
TMessageEvent;
type TMessageEvent
= procedure (var Msg: TMsg; var Handled: Boolean) of
object;
L'écriture du
gestionnaire d'événement OnMessage vous permet de répondre
à tous les messages que reçoit l'application:
type
TForm1 = class(TForm) ........ private procedure MonAppliMessages(var Msg: TMsg; var Handled: Boolean); end; procedure
TForm1.MonAppliMessages(var Msg: TMsg; var Handled:
Boolean); Le gestionnaire étant créé par l'instruction Application.OnMessage:=MonAppliMessages : procedure
TForm1.FormCreate(Sender: TObject);
|
si vous ne créez
pas un tel gestionnaire le message est distribué à la fenêtre
à laquelle il est destiné et Delphi continue la suite de tentative
de traitement du message pour la fenêtre concernée. Tout d'abord
Delphi transtype la struture TMsg en une structure interne de type TMessage
:
structure Tmsg en Delphi
: type
|
structure TMessage
en Delphi :
type
|
Le champ hwnd n'a plus de raison d'exister puisque la fenêtre
à qui le message s'adresse a déjà été
identifiée. Les champs time et pt de la structure TMsg sont réintégrés
à l'intérieur d'autres champs de la structure TMessage.
L'interception ayant eu lieu la redirection vers la fenêtre adéquate est assurée par la procedure MainWndProc(var Message: TMessage); c'est la procedure WndProc(var Message: TMessage); virtual; surchargeable, qui va se charger de traiter ou transmettre le message.
Exemple :
soit dans une application
à intercepter l'enfoncement du bouton gauche de la souris.
type
TForm1 = class(TForm) ........ private procedure WndProc(var Message: TMessage); override; end; implementation procedure
TForm1.WndProc(var Message: TMessage); |
Le traitement ci-dessus de WM_LBUTTONDOWN dans WndProc arrête la diffusion des messages vers les autres niveaux d'encapsulation. Le code de la procédure WndProc tel qu'il est écrit ci-dessus est inefficace et peut produire un message d'erreur car de tous les messages passant par WndProc nous n'avons décidé de n'en traiter qu'un seul le : WM_LBUTTONDOWN, en oubliant les autres messages de la fenêtre comme le positionnement, l'affichage etc...
Pour laisser Delphi
s'occuper du reste il suffit de rajouter inherited à la fin
de WndProc et la transmission des autres messages pourra se poursuivre.
type
TForm1 = class(TForm) ........ private procedure WndProc(var Message: TMessage); override; end; implementation procedure
TForm1.WndProc(var Message: TMessage); |
La méthode WndProc
reçoit tous les messages destinés à la fenêtre
et fait un filtrage. Elle appelle la méthode Dispatch pour chaque message
selectionné, cette méthode n'a pas de type au message qui est
transmis, c'est un message générique. Si vous voulez intercepter
un message spécifique à ce niveau vous devrez le transtyper
par exemple en TMessage.
type
TForm1 = class(TForm) ........ private procedure Dispatch(var Message); override; end; implementation procedure
TForm1.Dispatch(var Message); |
2.4 Interception directe d'un message
procedure ABCxxx(var Message: TMessage);message ABC_xxx;
Ce mécanisme
direct vous autorise à créer une méthode spécifique
de traitement d'un message donné. En reprenant le même exemple
d'enfoncement du bouton gauche de souris :
type
TForm1 = class(TForm) ........... private procedure WMLBUTTONDOWN(var Message:Tmessage);message WM_LBUTTONDOWN; end; implementation procedure
TForm1.WMLBUTTONDOWN(var Message:Tmessage);
|
Si aucun gestionnaire n'a été écrit pour ce message, le système de gestion par défaut de Delphi s'en charge à votre place : il appelle la méthode DefaultHandler. Comme pour Dispatch le paramètre est de type message générique. Il s'agit ici d'un mécanisme qui se trouve juste au dessus de celui du gestionnaire d'événement.
DefaultHandler permet
en particulier de gérer par surcharge tous les messages pour lesquels
l'objet n'a pas de gestionnaire spécifique ou bien d'intercepter (c'est
le denier niveau possible) un message connu par l'objet.
type
TForm1 = class(TForm) ........... private procedure DefaultHandler(var Message); override; end; implementation procedure TForm1.DefaultHandler(var
Message); |
Enfin si un gestionnaire
d'événement a été écrit par le programmeur,
la méthode Dispatch l'appelle. Il s'agit des gestionnaires d'événements
qui sont fournis par Delphi avec une entrée dans l'inspecteur d'objet,
ici pour l'interception de l'enfoncement de la souris l'événement
OnMouseDown est géré. l'événement OnMouseDown est déclenché
automatiquement lorsque l'un de des messages suivants WM_LBUTTONDOWN,WM_MBUTTONDOWN,WM_RBUTTONDOWN est intercepté
par l'application :
type
TForm1 = class(TForm) procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); private ........ end; implementation procedure
TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
|
Exemple : affichage de la hiérarchie des niveaux d'interception du WM_LBUTTONDOWN
Dans cet exemple nous interceptons le click gauche de souris à tous les niveaux, et nous laissons l'interception continuer aux niveaux inférieurs grâce au inherited. Ci-dessous le code source de la unit du programme Delphi correspondant :
Unit Unitexemple;
interface
uses
Windows, Messages,
SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Edit1:
TEdit;
Edit2:
TEdit;
Edit3:
TEdit;
Memo1:
TMemo;
Label1:
TLabel;
procedure
FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
private
{ Déclarations privées }
procedure
MonAppliMessages(var Msg: TMsg; var
Handled: Boolean);
procedure
WndProc(var Message: TMessage); override;
procedure
Dispatch(var Message);override;
procedure
DefaultHandler(var Message); override;
procedure
WMLBUTTONDOWN(var Message:Tmessage);message WM_LBUTTONDOWN;
public
{ Déclarations publiques }
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.FormCreate(Sender:
TObject);
begin
Application.OnMessage:=
MonAppliMessages;
end;
procedure TForm1.MonAppliMessages(var Msg: TMsg; var
Handled: Boolean);
begin
if Msg.message=WM_LBUTTONDOWN
then
begin
Edit1.text:=inttostr(Msg.wParam);
Edit2.text:=inttostr(Msg.lParam);
Edit3.text:=inttostr(Msg.time);
Edit3.color:=clBlue;
Memo1.Lines.Add('intercepté
: niveau 1 - MonAppliMessages')
end;
inherited
end;
procedure TForm1.WndProc(var Message: TMessage);
begin
if Message.Msg
= WM_LBUTTONDOWN then
begin
Edit1.text:=inttostr(Message.WParam);
Edit2.text:=inttostr(Message.LParamHi)
+ '/' + inttostr(Message.LParamLo);
Edit3.text:=inttostr(Message.Result);
Edit3.color:=clred;
Memo1.Lines.Add('intercepté
: niveau 1 - WndProc')
end;
inherited;
end;
procedure TForm1.Dispatch(var Message);
begin
if Tmessage(Message).msg
= WM_LBUTTONDOWN then
begin
Edit1.text:=inttostr(Tmessage(Message).WParam);
Edit2.text:=inttostr(Tmessage(Message).LParamHi)
+ '/' + inttostr(Tmessage(Message).LParamLo);
Edit3.text:=inttostr(Tmessage(Message).Result);
Edit3.color:=clyellow;
Memo1.Lines.Add('intercepté
: niveau 2 - Dispatch')
end;
inherited;
end;
procedure TForm1.WMLBUTTONDOWN(var Message:Tmessage);
begin
Edit1.text:=inttostr(Message.WParam);
Edit2.text:=inttostr(Message.LParamHi)
+ '/' + inttostr(Message.LParamLo);
Edit3.text:=inttostr(Message.Result);
Edit3.color:=cllime;
Memo1.Lines.Add('intercepté
: niveau 3 - Proc WMLBut') ;
inherited;
end;
procedure TForm1.DefaultHandler(var Message);
begin
if Tmessage(Message).msg
= WM_LBUTTONDOWN then
begin
Edit1.text:=inttostr(Tmessage(Message).WParam);
Edit2.text:=inttostr(Tmessage(Message).LParamHi)+'/'+inttostr(Tmessage(Message).LParamLo);
Edit3.text:=inttostr(Tmessage(Message).Result);
Edit3.color:=clAqua;
Memo1.Lines.Add('intercepté
: niveau 4 - DefaultHandler')
end;
inherited;
end;
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState;
X, Y: Integer);
begin
Edit3.color:=cllime;
Memo1.Lines.Add('intercepté
: niveau 5 - gest. OnMouseDown') ;
end;
end.
Si l'on click sur le fond de la fiche voici l'ordre d'interception en cascade du message WM_LBUTTONDOWN :
Remarquons que l'Edit3
est en couleur clLime, ce qui est normal puisque c'est le niveau 6 du gestionnaire
d'événement OnMouseDown qui est exécuté en dernier.
Si l'on click dans n'importe lequel des contrôles déposés sur la fiche (un des Edit, ou le memo) voici ce que nous obtenons :
C'est le gestionnaire
MonAppliMessages de l'événement OnMessage
de l'application qui a été le seul à être exécuté.
Ce qui signifie que l'application a bien reçu de la part du système
le message WM_LBUTTONDOWN et que son gestionnaire MonAppliMessages l'a pris
en compte. Toutefois comme nous avions clické dans l'Edit2
(Edit jaune) pour la suite Delphi n'ayant aucun traitement proposé
par le programmeur, a abandonné le traitement de ce message.
Vous devez déclarer un identificateur de message personnel. Un identificateur de message est une constante entière numérique. Windows se réserve pour son propre usage les messages dont le numéro est inférieur à 1024. Lorsque vous déclarez vos propres messages, vous devez donc toujours débuter par un numéro supérieur ou égal à 1024.
Delphi vous fournit si vous souhaitez l'utiliser, un identificateur de constante WM_USER représentant le numéro de départ (WM_USER = 1024) pour vos messages.
Voici trois constantes de messages personnels :
const
MonMessage1 = WM_USER
+ 100;
MonMessage2 = WM_USER
+ 120;
MonMessage3 = 1500;
3.1 Envoyer un message avec PostMessage et SendMessage
L'API Windows met à notre disposition de nombreuses procédures de bas niveau permettant d'envoyer un message à un contrôle fenêtré (descendant des TWinControl), dont les plus simples sont PostMessage et SendMessage. Il est rare que nous soyons obligés d'utiliser ce genre de fonction de bas niveau, mais ici l'objectif est d'indiquer comment envoyer des messages personnalisés.
SendMessage(procédure de traitement synchrone) attend que le message soit traité, si le message est envoyé à un autre thread, l'application émettrice du message reste bloquée tant que l'application receptrice n'a pas fini de le traiter.
en-tête de la fonction :
function SendMessage(hwnd
: HWND; Msg : cardinal;
wParam , lParam : Longint) : Longint;
Remarque
SendMessage envoie directement le message à une fenêtre sans passer par la file d'attente. L'événement OnMessage de l'application n'est donc pas déclenché, il est conseillé de n'utiliser SendMessage qu'à l'intérieur d'une même application pour envoyer des messages synchrones à des contrôles fenêtrés de l'application (Fiche, Panel, Edit, etc...). Dans le cas où vous utilisez SendMessage pour communiquer avec une autre application, pensez bien à construire dans l'application réceptrice, une procédure d'interception directe du message personnalisé, ce qui sera le seul moyen d'intercepter ce message. |
PostMessage(procédure de traitement asynchrone)envoie le message dans la file d'attente des messages du contrôle fenêtré et n'attend pas que le message soit traité.
en-tête de la fonction :
function PostMessage(hwnd
: HWND; Msg : cardinal;
wParam , lParam : Longint) : Longint;
Remarque
PostMessage envoie le message à une procédure de fenêtre dans la file d'attente de la fenêtre. L'événement OnMessage de l'application est déclenché. Dans le cas où vous utilisez PostMessage pour communiquer avec une autre application vous pouvez intercepter le message personnalisé comme avec SendMessage au niveau procédure d'interception directe, mais aussi au niveau de L'événement OnMessage de l'application. |
Bien entendu, outre vos messages personnalisés, ces deux procédures permettent aussi d'envoyer des messages définis de Windows (WM_xxx, LB_xxx, EM_xxx, etc...)
SendMessage(Form1.Handle,WM_CLOSE,0,0);//message de fermeture de la fiche Form1
SendMessage(Form1.Edit1.Handle,WM_CHAR,ord('M'),0);//envoi du caractère 'M' dans Edit1
etc...
Ces deux fonctions de bas niveau, ont comme premier paramètre le Handle de fenêtre du contrôle à qui est envoyé le message, il existe pour les contrôles en général, une méthode notée Perform qui permet d'envoyer directement un message à la procédure de fenêtre du contrôle sans avoir à connaître son Handle : le contrôle répond alors comme s'il avait reçu le message Windows spécifié.
function Perform(Msg:
Cardinal; WParam, LParam: Longint): Longint;
Enoncé :
Fermer la fenêtre principale d'une application PReception.exe, à
partir d'une autre application PEmissPost.exe, par l'envoi d'un message personnalisé.
1 - Source de l'application PEmissPost.exe
unit UFemissPost;
interface
uses
Windows, Messages,
SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class(TForm)
Button1:
TButton;
Edit1:
TEdit;
procedure
Button1Click(Sender: TObject);
private
{ Déclarations
privées }
public
{ Déclarations
publiques }
procedureEnvoyerUnMessage;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
{------------ Emission message fermeture à la fenêtre
TFExemple ----(émission)---------}
// fermeture de la fenêtre TFxxxx déjà ouverte
avant fermeture
procedure TForm1.EnvoyerUnMessage;
var
Wnd:HWnd;
begin
Wnd:=FindWindow('TFExemple',nil);// recherche de la fenêtre TFExemple
if Wnd<>0
then// si instance de la fenêtre
trouvée => on la ferme
begin
Edit1.Text:='Fenêtre
TFExemple trouvée et fermée';
PostMessage(Wnd,WM_USER,0,0);// message associé à la fermeture
//SendMessage(Wnd,WM_USER,0,0);// message à traiter
directement
end
else
Edit1.Text:='Fenêtre
TFExemple non trouvée';
end;
procedure TForm1.Button1Click(Sender:
TObject);
begin
EnvoyerUnMessage;
end;
end.
2 - Source de l'application PReception.exe
unit UFReception;
interface
uses
Windows, Messages,
SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TFExemple = class(TForm)
Label1:
TLabel;
procedure
FormCreate(Sender: TObject);
private
{ Déclarations privées }
procedure
MessageUser(var mess:TMsg; var hand:boolean);
procedureMessWMUSER (var Mess:TMessage);message
WM_USER;
public
{ Déclarations
publiques }
end;
var
FExemple: TFExemple;
implementation
{$R *.DFM}
{---------------- Interception message utilisateur ----(reception)---------------}
procedure TFExemple.MessageUser(var mess:TMsg;var hand:boolean);
{ traite le message wm_User intercepté comme un ordre donné
à la fenêtre.
il est traité au niveau OnMessage }
begin
if
mess.message=WM_USER then // message envoyé
= WM_USER (<=>close ici)
begin
close
// message reçu interprété
ici comme une fermeture "close"
end;
{else if mess.message=WM_USER+1 then .....}
inherited;
end;
procedure TFExemple.MessWMUSER
(var Mess:TMessage);
//le message wm_User est intercepté directement
begin
close
end;
procedure TFExemple.FormCreate(Sender:
TObject);
begin
Application.OnMessage:=MessageUser;
end;
end.
On lance les deux applications PReception.exe et PEmissPost.exe :
la fiche précédente
se ferme lors du click sur le bouton
Essayez de nouveau en remplaçant dans l'application émettrice le PostMessage(Wnd,WM_USER,0,0) par SendMessage(Wnd,WM_USER,0,0);