WM_COPYDATA message

Je suis un peu sur le cul de ce que je viens de lire dans le MSDN.

Une application (app1) enyoyant un message WM_COPYDATA (via SendMessage) décrit par une structure COPYDATASTRUCT peut transmettre l’ensemble de son contenu à une autre application (app2), y compris une adresse de sa zone de donnée :stuck_out_tongue: .

Ca veut dire qu’au travers de cette méthode on peut faire partager son espace adressable à une autre appli. Tiens, j’ai toujours pensé qu’il fallait expressément le préciser (zone partagée par ex.) et que par défaut les espaces adressables de deux process étaient étanches.

Quelqu’un peut confirmer ? je me fourvoie complètement ?

T’as fumé :P. Si tu mattes les exemples, dans un cas cross proc le dwData est null, ou bien tu met n’importe quoi qui t’arrange dedans et sur lequel les deux applis sont d’accord. Tu passes tes trucs a plat dans lpData. C’est fait que pour envoyer des toutes petites quantites de data entre HWND.

Dans les remarques, la premiere ligne:

The data being passed must not contain pointers or other references to objects not accessible to the application receiving the data.

While this message is being sent, the referenced data must not be changed by another thread of the sending process.

The receiving application should consider the data read-only. The lParam parameter is valid only during the processing of the message. The receiving application should not free the memory referenced by lParam. If the receiving application must access the data after SendMessage returns, it must copy the data into a local buffer.

Oops, je me suis mal exprimé et pourtant je n’avais pas fumé (jamais quand je bosse) :stuck_out_tongue:

J’ai « compilé » ce que j’ai lu dans le MSDN avec le code que j’ai sous les yeux et j’en ai déduit le message que j’ai posté précédemment. J’ai sûrement été un peu vite en mangeant une part de l’info. Je vais préciser.

Je suis tellement étonné par le code que je lis actuellement que j’ai lu entre les lignes du MSDN. Néanmoins, je suis dubitatif quant aux différentes informations fournis par le MSDN mais il y a sûrement une explication :

  1. Le titre : The WM_COPYDATA message is sent when an application passes data to another application
    Il est donc clair que ce message existe pour communiquer inter-process, n’est-ce pas ? et paradoxalement la structure (COPYDATASTRUCT ) mise à disposition pour cette comm. est une strucuture accueillant un pointeur… alors que, comme tu le dis, il est bien précisé ensuite : …/… must not contain pointers …/….

Cela dit, il faut noter que le MSDN précise bien "The data being passed must not contain pointers or other references to objects not accessible to the application receiving the data. ".

Nous avons donc à faire à un message dédié à la comm. inter-process capable de transporter un pointeur… mmm… ok ok, le cas d’une zone de mémoire partagée entre dans ce cadre. Mais ma confusion a été plus grande lorsque j’ai lu ce qui suit :

  1. An application must use the SendMessage function to send this message, not thePostMessage function.

J’ai eu l’impression (probablement à tort) que l’usage de SendMessage (et non Post) couplé à ce message permettait d’ouvrir une zone « partagée » entre process. Pourquoi cette restriction sur Send vs Post alors ?

Pour finir, et là c’est le coup de grace : je suis face à un code qui utilise justement le passage d’un pointeur (zone allouée sur le tas) entre deux applis, et en principe l’appli fonctionne :stuck_out_tongue:

Petit bémole tout de même, je n’ai pas pu vérifier personnellement le bon comportemement fonctionnel de cette appli car l’ensemble nécessite une infra-structure que je n’ai pas mais le principe mis en oeuvre existe depuis des années visiblement et fait parti du fondement de cette appli (tuyau de comm.).

Si tu vois une raison qui expliquerait le comportement correct de cette appli, je suis preneur car là je suis face à des infos contradictoires.

Je suis un peu sec là.

Non, c’est une structure de la taille d’un pointeur, pas forcement un pointeur. La declaration de la struct ne veut pas forcement dire qu’il faut y coller un pointeur de l’appli A pour parler a l’appli B. Ou alors seulement en intra process. Tu y met ce que tu veux dans ce champ, et ca peut tres bien etre un pointeur valide vers une zone de memoire partagee, rien n’empeche de partager de la memoire entre deux process si tu le fais comme il faut et que l’autre appli y a acces… Donc au final, tu peux meme y coller un pointeur, si il est valide, tu peux y coller un pointeur invalide si tu t’en sert pas de l’autre cote, c’est juste un chiffre, ou tu peux passer 0 et pas t’en servir, tu peux y coller un truc custom a ta sauce que seul toi et l’appli de l’autre cote comprend. Ton choix…

Ca n’ouvre pas tout seul de zone partagee entre les deux process, dans le fonctionnement “de base”, les data que t’envois sont a mettre dans ta structure et tu en specifie la taille dans le champ adapte.

La diff entre post message et send message c’est que send message attend de savoir si l’execution a reussit et fail apres quelques secondes. Post message balance dans la file d’attente et retourne. C’est obligatoire parceque comme dit dans la doc :

The receiving application should consider the data read-only. […] If the receiving application must access the data after SendMessage returns, it must copy the data into a local buffer.

Je comprends parfaitement ce que tu dis mais je ne voudrais pas trop m’éloigner du sujet principal.

Pour répondre rapidement je colle la doc que j’ai :

[quote]COPYDATASTRUCT
The COPYDATASTRUCT structure contains data to be passed to another application by the WM_COPYDATA message.

typedef struct tagCOPYDATASTRUCT { // cds
DWORD dwData;
DWORD cbData;
PVOID lpData;
} COPYDATASTRUCT;

Members
dwData
Specifies up to 32 bits of data to be passed to the receiving application.
cbData
Specifies the size, in bytes, of the data pointed to by the lpData member.
lpData
Pointer to data to be passed to the receiving application. This member can be NULL.[/quote]

C’est quand même bien une structure de données dédiée à la comme inter-application et qui utilise un pointeur sur data.

Mais ok ok, je ne suis pas là pour couper les cheveux en quatre, le pointeur (quand même :stuck_out_tongue: ) peut en effet contenir ce qu’on veut … une valeur, un chiffre comme tu le dis, on s’en tamponne.

Oui oui, je connais la différence entre Send et Post et compte-tenu de cette restriction, je me suis dit qu’il y avait peut-être un service de synchro + partage de ressource via le processing de ce message :stuck_out_tongue: bon c’est clairement tiré par les cheveux ma position mais elle a été envisagée du fait du code que je lis en ce moment … va falloir que je torture le dev qui m’a fait ça pour comprendre ce biniou.

(tiens d’ailleurs j’ai levé un débordement de 2 octets dans son code… un peu strange :stuck_out_tongue: )

A la base je parlais surtout du premier ULONG_PTR dwData ou tu mettais ce qui te chantais.

Mais bon la memoire pointee par lpData est copiee quand tu envoie ce message. Ce qui est copie c’est lpData sur la longueur cbData. En fait c’est ton LPARAM de ton sendmessage qui est copié.

Tu peux le demontrer avec ce code (fait a l’arrache en 10 minutes).
Lance deux instances, dans une met le titre de la deuxiemme dans la textbox et clique sur le bouton.

[code]using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;

namespace WindowsApplication3
{
///


/// Summary description for Form1.
///

public class Form1 : System.Windows.Forms.Form
{
       private System.Windows.Forms.TextBox textBox1;
       private System.Windows.Forms.Button button1;
       private System.Windows.Forms.StatusBar statusBar1;
 ///
 /// Required designer variable.
 ///

 private System.ComponentModel.Container components = null;

 public Form1()
 {
 //
 // Required for Windows Form Designer support
 //
 InitializeComponent();

 //
 // TODO: Add any constructor code after InitializeComponent call
 //

 }

 ///


 /// Clean up any resources being used.
 ///

 protected override void Dispose( bool disposing )
 {
 if( disposing )
 {
   if (components != null)
   {
   components.Dispose();
   }
 }
 base.Dispose( disposing );
 }

 #region Windows Form Designer generated code
 ///


 /// Required method for Designer support - do not modify
 /// the contents of this method with the code editor.
 ///

 private void InitializeComponent()
 {
           this.textBox1 = new System.Windows.Forms.TextBox();
           this.button1 = new System.Windows.Forms.Button();
           this.statusBar1 = new System.Windows.Forms.StatusBar();
           this.SuspendLayout();
           //
           // textBox1
           //
           this.textBox1.Location = new System.Drawing.Point(88, 56);
           this.textBox1.Name = “textBox1”;
           this.textBox1.TabIndex = 0;
           this.textBox1.Text = “”;
           //
           // button1
           //
           this.button1.Location = new System.Drawing.Point(88, 88);
           this.button1.Name = “button1”;
           this.button1.TabIndex = 1;
           this.button1.Text = “button1”;
           this.button1.Click += new System.EventHandler(this.button1_Click);
           //
           // statusBar1
           //
           this.statusBar1.Location = new System.Drawing.Point(0, 249);
           this.statusBar1.Name = “statusBar1”;
           this.statusBar1.Size = new System.Drawing.Size(292, 22);
           this.statusBar1.TabIndex = 2;
           this.statusBar1.Text = “statusBar1”;
           //
           // Form1
           //
           this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
           this.ClientSize = new System.Drawing.Size(292, 271);
           this.Controls.Add(this.statusBar1);
           this.Controls.Add(this.button1);
           this.Controls.Add(this.textBox1);
           this.Name = “Form1”;
           this.Text = “Form1”;
           this.Load += new System.EventHandler(this.Form1_Load);
           this.ResumeLayout(false);

       }
 #endregion

 ///


 /// The main entry point for the application.
 ///

 [STAThread]
 static void Main()
 {
 Application.Run(new Form1());
 }

       private void Form1_Load(object sender, System.EventArgs e) {
           this.Text = this.Handle.ToString();
       }

       private const int WM_COPYDATA = 0x4A;
       [DllImport(“user32.dll”)]
       static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam,
           IntPtr data);
       

       private void button1_Click(object sender, System.EventArgs e) {
           IntPtr targetHandle = new IntPtr(long.Parse(textBox1.Text));
           string text = “Blah bblah blah”;
           IntPtr textPtr = Marshal.StringToHGlobalAnsi(text);
           COPYDATASTRUCT cds = new COPYDATASTRUCT();
           cds.data = 0;
           cds.length = text.Length;
           cds.pData = textPtr;
           IntPtr data = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(COPYDATASTRUCT)));
           Marshal.StructureToPtr(cds, data, false);
           IntPtr result = SendMessage(targetHandle, WM_COPYDATA, IntPtr.Zero, data);
           this.statusBar1.Text = “SENT LPARAM PTR: " + data.ToString() +”; " +cds.pData.ToString();

           Marshal.FreeHGlobal(textPtr);
           Marshal.FreeHGlobal(data);
       }
       protected override void WndProc(ref Message m) {
           switch(m.Msg) {
               case WM_COPYDATA:
                   
                   COPYDATASTRUCT cds = new COPYDATASTRUCT();
                   cds = (COPYDATASTRUCT) Marshal.PtrToStructure(m.LParam, typeof(COPYDATASTRUCT));
                   this.statusBar1.Text = "REICEIVED LPARAM PTR: " + m.LParam.ToString() + "; " + cds.pData.ToString();
                   if (cds.length > 0) {
                       string text = Marshal.PtrToStringAnsi(cds.pData).Substring(0,cds.length);
                       
                   }
                   base.WndProc(ref m);
                   break;
                   
               default:
                   base.WndProc (ref m);
                   break;
           }
           
       }

}

   [StructLayout(LayoutKind.Sequential)]
   public struct COPYDATASTRUCT  {
       public int data;
       public int length;
       public IntPtr pData;
   }

}[/code]

Heuu c’est du C# et je n’ai pas l’environnement adéquat.

Ok pour dwData.

Je ne comprends pas ce que tu dis :

gniiii c’est la mémoire décrite par lpData qui est copiée ou bien celle décrite par lParam ? (après relecture, on s’en fout, peu importe)

Il s’agit donc d’une recopie de données lors de l’émission de CE message, c’est ça ? le processing de ce message est particulier alors ?

Je pense que c’est la seule possibilté, en fait. Et je comprends mieux dans ce cas, le contenu de la structure et la restriction sur SendMessage vs PostMessage … tout s’éclairci.

Thx :stuck_out_tongue:

Note : franchement, je n’ai pas trouvé la doc très explicite. Une mention : les données pointées par lpData sont recopiées avant d’être délivrées à l’appli réceptrice aurait bien simplifé la (ma seulement ?) compréhension.

EDIT : pour avoir la honte, je dois reconnaître que le message s’appelle WM_COPYDATA :stuck_out_tongue: