Bonjour à tous !
Je vous propose aujourd'hui un nouveau tutoriel sur l'envoi de chaînes ou de structures par PostMessage en utilisant la table d'atomes.
N'hésitez pas à faire part de vos réactions
Bonne lecture !
Bonjour à tous !
Je vous propose aujourd'hui un nouveau tutoriel sur l'envoi de chaînes ou de structures par PostMessage en utilisant la table d'atomes.
N'hésitez pas à faire part de vos réactions
Bonne lecture !
Bonjour,
Personnellement j'utilise un autre approche que je trouve bcp plus simple
le destinataire du message est responsable de la suppression de "data", mais il est possible d'ajouter ces objets à une ThreadList globale qu'on purge le cas échéant quand c'est nécessaire.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 type TMessageData = class UnEntier: Integer; UneChaine: string; UnStream: TStream; NImporteQuoi: TNImportQuoi; end; TDataMessage = record Msg: Integer; Null: Integer; Data: TMessageData; Result : Integer; end; procedure SendInfos(HWnd, Msg: Integer; UnEntier, UneChaine...); var data: TMessageData; begin data := TMessageData.Create; data.UnEntier := UnEntier; data.UneChaine := UneChaine; ... PostMessage(HWnd, Msg, 0, Integer(@data)); end;
C'est possible en effet
Par record, il faudra prendre soin de faire appel à GetMem/FreeMem (New/Dispose) et pas simplement déclarer une variable locale à la fonction !
Mon approche se veut cependant plus universelle et autorise l'envoi entre processus
J'ai jamais eu le temps de m'occuper de [QR] Comment passer une string dans un message Windows ? qui utilise WM_COPYDATA qui fonctionnee en inter-processus
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
...mais qui est obligatoirement synchrone par SendMessage
@AndNotY,
Salut, j'ai parcouru tout ton article, et à la fin je me suis demandé à quoi cela peut servir en cas réel ? (je préfère passer pour un idiot, mais je pose quand même la question...)
Est-ce un moyen de transmettre des données (tableau, chaîne, nombre) entre 2 applications ?
Tu peux envoyer tout ce que tu veux, entre threads ou entre processus
Voici deux exemples.
Rediriger la sortie d'une application console.
Admettons que cycliquement, tu lances en tâche de fond une application console invisible pour de la maintenance (backup, mise à jour...). Tu aimerais cependant que l'utilisateur soit informé de l'avancement ou d'une éventuelle erreur. Tu pourrais bien sûr :
- utiliser CreateProcess et rediriger la sortie sur un Pipe. Un travail conséquent ;
- lancer ShellExecuteEx depuis un thread et attendre la fin pour contrôler le code de sortie (ExitCode), mais pas de notification d'avancement ;
- envoyer WM_COPYDATA mais SendMessage est bloquant et dû à la structure de taille fixe, le texte risque d'être tronqué (ou alors il faut surdimentionner le buffer).
Ici, rien de tout cela ! Il suffit de redéfinir WriteLn et d'envoyer PostTextMessage
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 program Project1; {$APPTYPE CONSOLE} {$R *.res} uses SysUtils, Windows, Messages, MessagesEx; procedure WriteLn(aText :string); begin PostTextMessage(FindWindow('ClassName', 'WindowName'), WM_USER, aText); end; begin ClearTableOnExit := FALSE; try WriteLn('BlaBla'); WriteLn('BlaBla'); except on E: Exception do WriteLn(E.Message); end; end.
Hook.
Tu aimerais intercepter le retour de traitement d'un message par une fenêtre quelconque. Tu vas pour cela injecter une DLL dans les processus par SetWindowsHookEx. Mais un hook peut être très pénalisant pour le système et le ralentir considérablement si le traitement à effectuer est long.
Jusqu'à Windows XP, on privilégiait le passage d'info par fichier mappé. Les lectures et écritures devant bien sûr être synchronisées par mutex. Depuis Vista et l'UAC, C'est fini ! La DLL injectée dans une application de niveau d'intégrité inférieur (par exemple Internet Explorer en mode protégé) n'aura pas accès au fichier mappé. Une violation est générée et c'est le deadlock assuré de l'application cible.
On pourrait à nouveau utiliser WM_COPYDATA, mais il faudra que notre application soit très réactive. Imagine un hook souris tellement pénalisant que le curseur avance par saccade !
Router simplement les informations par PostMessage ? Bien sûr si peu d'informations nous intéressent et tiennent dans WParam/LParam. Mais il ne faut pas espérer en récupérer plus par la suite par AttachThreadInput (UAC quand tu nous tiens...).
Intercepter le retour d'un message se fait en plaçant un hook de type WH_CALLWNDPROCRET. Un pointeur est passé dans LParam correspondant à la structure suivante :
Nous n'aurons donc plus qu'à faire :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 type TCwpRetStruct = record lResult :LRESULT; lParam :LPARAM; wParam :WPARAM; message :UINT; hwnd :HWND; end;
Est-ce plus clair ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 function CWPRetProc(aCode: integer; aWParam: WPARAM; aLParam: LPARAM):LRESULT; stdcall; begin if (aCode = HC_ACTION) then PostBufferMessage(FindWindow('ClassName', 'WindowName'), WM_USER, aLParam, SizeOf(TCwpRetStruct)); Result := CallNextHookEx(0, aCode, aWParam, aLParam); end;
WM_COPYDATA est bloquant, cela a un avantage car son retour permet de vérifier la bonne reception\compréhension du message par le receveur
Sinon WM_COPYDATA utilise une structure COPYDATASTRUCT de taille fixe mais permet justement de gérer un buffer de longueur variable via se membres cbData et lpData
On peut donc strictement définir un buffer de la taille exacte de la donnée devant être envoyé
Sinon, pourquoi ne pas avoir utiliser BinToHex qui aurait fait le travail de conversion en Hexa bien plus rapidement que les nombreuses réallocation\concaténation de Data.Bytes même si FastMM le gère bien mieux que l'ancien gestionnaire de mémoire
Tient, j'ai fait un petit dev hier que l'on m'a réclamé pour simplifier la vie de l'utilisateur
un programme A doit lancer un programme B
A étant déjà loggué, B est lancé en mode "AutoLogin" pour que l'utilisateur n'est pas retapé son Login+PW
Dans d'autres programmes qui font la même chose, le login est passé sur la ligne de commande, en clair pour certaines et d'autres en chiffré
Cela reste une jolie faille de sécurité puisque cela permet un login sans PW
Mon prédecesseur n'avait pas du tout conscience de la sécurité !
Je passe un HWND qui servirait ensuite pour un échange entre A et B d'un GUID servant d'ID de session qui aboutit à l'authentification, c'est déjà plus difficile à choper (un hook)
je viens d'en extraire une petite classe plus générique, on voit tout de suite qu'avec un peu d'effort que l'on pourrait hériter d'un TStream et utiliser un TBinaryReader\TBinaryWriter dessus
la Version C++, si j'ai le temps, je la tenterais en Delphi
Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 //--------------------------------------------------------------------------- typedef bool __fastcall (__closure *TShaiCopyDataMessagerReadEvent)(TObject* Sender, HWND DataSender, void *Data, int DataLen); //--------------------------------------------------------------------------- // TShaiCopyDataMessager - //--------------------------------------------------------------------------- class TShaiCopyDataMessager : public TObject { private: // Membres Privés HWND FSenderHandle; HWND FRecipientHandle; COPYDATASTRUCT FRawData; TShaiCopyDataMessagerReadEvent FOnRead; // Accesseurs HWND __fastcall GetSenderHandle(); // Méthodes Privées void __fastcall WndProc(Messages::TMessage &Message); bool DoRead(HWND DataSender, void *Data, int DataLen); public: // Constructeurs Publiques /*constructor*/__fastcall TShaiCopyDataMessager(); /*destructor*/virtual __fastcall ~TShaiCopyDataMessager(); // Méthodes Publiques void BeginWrite(); void WriteInteger(int Value); void WriteString(String Value); void WriteBuffer(void *Data, int DataLen); bool EndWrite(); void CancelWrite(); int ReadInteger(void *Data, /*out*/int &Value); int ReadString(void *Data, /*out*/String &Value); // Propriétés Publiques __property HWND SenderHandle = {read=GetSenderHandle}; __property HWND RecipientHandle = {read=FRecipientHandle, write=FRecipientHandle}; __property TShaiCopyDataMessagerReadEvent OnRead = {read=FOnRead, write=FOnRead}; };
Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138 //--------------------------------------------------------------------------- // TShaiCopyDataMessager - //--------------------------------------------------------------------------- /*constructor*/__fastcall TShaiCopyDataMessager::TShaiCopyDataMessager() { } //--------------------------------------------------------------------------- /*destructor*/__fastcall TShaiCopyDataMessager::~TShaiCopyDataMessager() { CancelWrite(); if (FSenderHandle != 0) { DeallocateHWnd(FSenderHandle); FSenderHandle = 0; } } //--------------------------------------------------------------------------- HWND __fastcall TShaiCopyDataMessager::GetSenderHandle() { if ( ! FSenderHandle) FSenderHandle = AllocateHWnd(WndProc); return FSenderHandle; } //--------------------------------------------------------------------------- void TShaiCopyDataMessager::BeginWrite() { CancelWrite(); } //--------------------------------------------------------------------------- void TShaiCopyDataMessager::WriteInteger(int Value) { WriteBuffer(&Value, sizeof(Value)); } //--------------------------------------------------------------------------- void TShaiCopyDataMessager::WriteString(String Value) { int Len = Value.Length(); WriteInteger(Len); if (Len > 0) { int Size = Len * sizeof(Char); // gère que la table BMP de UTF-16 WriteBuffer(Value.c_str(), Size); } } //--------------------------------------------------------------------------- void TShaiCopyDataMessager::WriteBuffer(void *Data, int DataLen) { int Position = FRawData.cbData; FRawData.cbData += DataLen; if (Position > 0) ReallocMemory(FRawData.lpData, FRawData.cbData); else FRawData.lpData = GetMemory(FRawData.cbData); CopyMemory((Byte*)FRawData.lpData + Position, Data, DataLen); } //--------------------------------------------------------------------------- bool TShaiCopyDataMessager::EndWrite() { bool Result = SendMessage(FRecipientHandle, WM_COPYDATA, (WPARAM)FSenderHandle, (LPARAM)&FRawData); CancelWrite(); return Result; } //--------------------------------------------------------------------------- void TShaiCopyDataMessager::CancelWrite() { FreeMemory(FRawData.lpData); ZeroMemory(&FRawData, sizeof(FRawData)); } //--------------------------------------------------------------------------- int TShaiCopyDataMessager::ReadInteger(void *Data, /*out*/int &Value) { int Result = -1; if ( ! IsBadReadPtr(Data, sizeof(Value))) { Value = *(int*)Data; Result = sizeof(Value); } return Result; } //--------------------------------------------------------------------------- int TShaiCopyDataMessager::ReadString(void *Data, /*out*/String &Value) { int Result = -1; int Len = 0; int TmpResult = ReadInteger(Data, Len); if (TmpResult > 0) { Result = TmpResult; if (Len > 0) { Value = UnicodeString((WideChar*)((Byte*)Data + TmpResult), Len); int Size = Len * sizeof(Char); // gère que la table BMP de UTF-16 Result += Size; } else Value = ""; } return Result; } //--------------------------------------------------------------------------- void __fastcall TShaiCopyDataMessager::WndProc(Messages::TMessage &Message) { if (Message.Msg == WM_COPYDATA) { PCOPYDATASTRUCT PRawData = (PCOPYDATASTRUCT)Message.LParam; if (PRawData && PRawData->cbData) Message.Result = DoRead((HWND)Message.WParam, PRawData->lpData, PRawData->cbData); else Message.Result = false; } else Message.Result = DefWindowProc(FSenderHandle, Message.Msg, Message.WParam, Message.LParam); } //--------------------------------------------------------------------------- bool TShaiCopyDataMessager::DoRead(HWND DataSender, void *Data, int DataLen) { bool Result = false; if (FOnRead) Result = FOnRead(this, DataSender, Data, DataLen); return Result; }
//---------------------------------------------------------------------------
Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 void __fastcall TSystemManipForm::BtnCopyDataWhoIAmClick(TObject *Sender) { if ( ! FCopyDataMessager) { FCopyDataMessager = new TShaiCopyDataMessager(); FCopyDataMessager->OnRead = CopyDataMessagerReadEventHandler; } EditCopyDataWhoIAm->Text = UIntToStr((unsigned)FCopyDataMessager->SenderHandle); } //--------------------------------------------------------------------------- bool __fastcall TSystemManipForm::CopyDataMessagerReadEventHandler(TObject *Sender, HWND DataSender, void *Data, int DataLen) { bool Result = false; TShaiCopyDataMessager* Messager = dynamic_cast<TShaiCopyDataMessager*>(Sender); if (Messager) { String S1, S2, S3; int OffSet = Messager->ReadString(Data, S1); Data = (Byte*)Data + OffSet; OffSet = Messager->ReadString(Data, S2); Data = (Byte*)Data + OffSet; OffSet = Messager->ReadString(Data, S3); MemoCopyDataRecv->Lines->Add(S1 + S2 + S3); Result = Pos("bad", S2) <= 0; } return Result; } //--------------------------------------------------------------------------- void __fastcall TSystemManipForm::BtnCopyDataSendToClick(TObject *Sender) { if ( ! FCopyDataMessager) { FCopyDataMessager = new TShaiCopyDataMessager(); FCopyDataMessager->OnRead = CopyDataMessagerReadEventHandler; } FCopyDataMessager->RecipientHandle = (HWND)StrToInt(EditCopyDataSendTo->Text); FCopyDataMessager->BeginWrite(); FCopyDataMessager->WriteString("La Chaine est : \""); FCopyDataMessager->WriteString(EditCopyDataSendToText->Text); FCopyDataMessager->WriteString("\" !"); if ( ! FCopyDataMessager->EndWrite()) MemoCopyDataRecv->Lines->Add("bad est refusé"); } //---------------------------------------------------------------------------
Code dfm : Sélectionner tout - Visualiser dans une fenêtre à part
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 object BtnCopyDataWhoIAm: TButton Left = 3 Top = 3 Width = 75 Height = 25 Caption = 'Who I Am ?' TabOrder = 0 OnClick = BtnCopyDataWhoIAmClick end object EditCopyDataWhoIAm: TEdit Left = 84 Top = 5 Width = 121 Height = 21 Alignment = taRightJustify NumbersOnly = True ReadOnly = True TabOrder = 1 Text = '?' end object BtnCopyDataSendTo: TButton Left = 3 Top = 34 Width = 75 Height = 25 Caption = 'Send To' TabOrder = 2 OnClick = BtnCopyDataSendToClick end object EditCopyDataSendTo: TEdit Left = 84 Top = 36 Width = 121 Height = 21 Alignment = taRightJustify NumbersOnly = True TabOrder = 3 Text = '?' end object EditCopyDataSendToText: TEdit Left = 211 Top = 36 Width = 121 Height = 21 TabOrder = 4 Text = 'Bonjour !' end object MemoCopyDataRecv: TMemo Left = 3 Top = 65 Width = 329 Height = 274 Lines.Strings = ( 'Receive :') ReadOnly = True ScrollBars = ssBoth TabOrder = 5 end
Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
Attention Troll Méchant !
"Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
L'ignorance n'excuse pas la médiocrité !
L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
Il faut avoir le courage de se tromper et d'apprendre de ses erreurs
C'est clair qu'à partir du moment où un dialogue est nécessaire, cette technique n'est pas applicable. Ce n'est pas pour rien que j'ai pris l'exemple de WH_CALLWNDPROCRET dont la donnée ne peut être modifiée
Maintenant, mon but était vraiment de faire de l'asynchrone...
J'aurais pu Comme j'aurais pu aussi définir une chaîne de 2x la taille de la donnée et travailler sur l'index du caractère.
Mais je suis un pervers et au départ je comptais convertir un octet sur 10 bits (actuellement 16 bits) pour utiliser au mieux les 255 octets à disposition. Puisqu'une des contraintes est : pas de byte à "0" son codage aurait été 1000010000 (en ajoutant toujours "1"). Mais ça devenait un peu compliqué et le tuto n'aurait plus été "Confirmé", mais "Expert"
Mais j'aurais dû plus simplifier !
J'ai aussi utiliser ce genre de technique en envoyant un message que l'application ne sait pas gérer (RegisterWindowMessage, donc inconnu par la cible)
mais récupéré par hook pour exécuter du code dans le processus cible
c'est Noël
Dans l'endroit et au bon moment que ce tutoriel car je passais des heures pour apprendre quelques choses sur les HOOK(s).
Merci pour ce tutoriel et les interventions des chères membres
salut, j'ai vu le tuto, mais j'avoue c'est quand il faut envoyer ou recevoir je ne sais pas quoi mettre dans la commande : PostTextMessage(aWnd :hWnd; aMessage :cardinal; aText :string) :integer;
si quelqu'un connais bien cette commande, je suis preneur car ca corresponds bien a ce que je voudrais faire, j'ai bien récuperer le fichier MessagesEx je l'ai intégrer a mon application, mais pas plus.
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager