IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

API, COM et SDKs Delphi Discussion :

SendMessage et THandle non correct


Sujet :

API, COM et SDKs Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre expérimenté Avatar de Moez.B
    Homme Profil pro
    Développeur Delphi
    Inscrit en
    Mars 2006
    Messages
    219
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Tunisie

    Informations professionnelles :
    Activité : Développeur Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2006
    Messages : 219
    Par défaut SendMessage et THandle non correct
    Bonjour,
    Je suis devant la situation suivante: j'ai plusieurs instances de mon application ( en ligne de commande ) sur des sessions différentes sur ma machines.
    Exemple : Session de l'administrateur (utilisateur courant) : MonProg.exe -T -H
    Session de Client1 (en mode déconnecté) : MonProg -T -D -S
    Session de Client2 (en mode déconnecté) : MonProg -F -R -D -A
    Le but est de créer une application qui permet de transporter des données entre ces différentes instances de programmes lancés.
    L'idée est de récupérer le PID de chaque nom de l'image (exécutable) afin de pouvoir effectuer un traitement sur ce dernier selon le contenu du paquet de données envoyés.
    Tout va bien jusqu'à un SendMessage qui ne fonctionne pas avec le PID mais avec le THandle, donc il a fallu trouver le bon handle du process en question. Exemple
    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
     
    procedure TForm4.Button2Click(Sender: TObject);
    var
      packet: TDataTraites;
      CopyDataStruct:TCopyDataStruct;
      vyPid: string;
      h: THandle;
    begin
      vyPid := ListView1.Selected.SubItems[0];
      packet.Type:=0;
      packet.Champs:='CLOSE';
      packet.Valeur:='32767';
      packet.Table:=55;
      CopyDataStruct.dwData:=1; // Optionnel
      CopyDataStruct.cbData:=SizeOf(packet);
      CopyDataStruct.lpData:=@packet; 
      h := FindWindow(pchar('TFMain'), nil);
      SendMessage(h, WM_COPYDATA, application.Handle, LongInt(@CopyDataStruct));
    end;
    Jusqu'ici tout est bon, mais, voilà, le FindWindow peut faire l'affaire si une seule instance de mon programme est lancée mais il ne fera pas l'affaire si :
    - plusieurs instance de mon programmes sont lancées sur la session en cours.
    - Si une ou plusieurs instances sont lancées sur des sessions déconnectés du Client1 ou bien le Client2.
    Mon but est de pouvoir envoyer mon paquet de données à des instances selon le PID de chaque instance.
    J'ai essayé avec le OpenProcess
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    h := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, StrToInt(myPid));
    Mais ça ne fonctionne pas, le but étant de récupérer le bon Handle sur la base du PID du process à traiter.
    Alors, ma question est :
    - Est ce que le SendMessage peut se faire sur un processus inter-session ?
    - Si c'est faisable alors, comment récupérer le bon handle (qui sert pour le SendMessage) selon le PID du process qu'on veut traiter ?

    Merci d'avance.

  2. #2
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 933
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 933
    Par défaut
    Citation Envoyé par Moez.B Voir le message
    Est ce que le SendMessage peut se faire sur un processus inter-session ?
    Non, il faut utiliser d'autres formes d'IPC comme des Named Pipes mais ça entraîne pas mal de boulot.

    Dans la session en cours, tu peux passer par EnumWindows et ensuite contrôler que cette fenêtre appartient à un certain processus par GetWindowThreadProcessId.
    Tu pourrais même imaginer ajouter une propriété à tes fenêtres par SetProp contenant le PID pour éviter l'appel à GetWindowThreadProcessId.

  3. #3
    Membre expérimenté Avatar de Moez.B
    Homme Profil pro
    Développeur Delphi
    Inscrit en
    Mars 2006
    Messages
    219
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Tunisie

    Informations professionnelles :
    Activité : Développeur Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2006
    Messages : 219
    Par défaut
    Salut
    J'ai lancé mon programme dans la session courante à travers le planificateur de tâches : l'option que j'ai choisi est : "Exécuter même si aucun utilisateur n'a ouvert de session".
    Lorsque j'ai cette tâche qui est lancée, je la vois dans la liste des process actif et je récupère son PID.
    Je veux créer un SendMessage basique entre ce mon programme de commande et l'application qui est en arrière plan.
    Si je lance ce bout de code sur mon application en avant plan (lancée manuellement), le SendMessage est réussi :
    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
     
    function TForm4.GetHWndByPID(const hPID: THandle): THandle;
        type
        PEnumInfo = ^TEnumInfo;
        TEnumInfo = record
        ProcessID: DWORD;
        HWND: THandle;
      end;
     
        function EnumWindowsProc(Wnd: DWORD; var EI: TEnumInfo): Bool; stdcall;
        var
            PID: DWORD;
        begin
            GetWindowThreadProcessID(Wnd, @PID);
            Result := (PID <> EI.ProcessID) or
                    (not IsWindowVisible(WND)) or
                    (not IsWindowEnabled(WND));
     
            if not Result then EI.HWND := WND; //break on return FALSE
        end;
     
        function FindMainWindow(PID: DWORD): DWORD;
        var
            EI: TEnumInfo;
        begin
            EI.ProcessID := PID;
            EI.HWND := 0;
            EnumWindows(@EnumWindowsProc, Integer(@EI));
            Result := EI.HWND;
        end;
     
    begin
        if hPID<>0 then
            Result:=FindMainWindow(hPID)
        else
            Result:=0;
    end;
     
    procedure TForm4.Button5Click(Sender: TObject);
    var
      myPid: string;
      h: THandle;
    begin
      myPid := ListView1.Selected.SubItems[0];
      h :=  GetHWndByPID (StrToInt(myPid));
      SendMessage(h, WM_CLOSE, application.Handle, 0);
    end;
    Mais si l'application est lancée via le planificateur de tâches (et donc elle est en arrière plan), le retour de de mon GetHWndByPID est toujours 0.
    Je crois que c'est tout à fait normal car je suis entrain d'utiliser EnumWindowsProc qui cherche le MainWindow via le PID de l'application. Alors, évidement si l'application est arrière plan, la fenêtre principale est caché et je ne peux pas récupérer son Handle.
    Ce que je souhaite faire c'est de pouvoir échanger des messages entre mon application (que ce soit en arrière plan ou bien en avant plan) sur la session courante active ou bien sur des sessions déconnectées d'autres utilisateurs de la même machine.
    Est ce que un Process Windows en arrière plan ne peut pas recevoir les messages (WM_CLOSE, WM_COPYDATA ...) ?
    Merci d'avance

  4. #4
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 933
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 933
    Par défaut
    Affiche la colonne ID de session dans le gestionnaire des tâches. Si l'ID est différent de la session courante, SendMessage ne peut pas marcher.

  5. #5
    Membre expérimenté Avatar de Moez.B
    Homme Profil pro
    Développeur Delphi
    Inscrit en
    Mars 2006
    Messages
    219
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Tunisie

    Informations professionnelles :
    Activité : Développeur Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2006
    Messages : 219
    Par défaut
    Bonjour,

    Merci pour la réponse.

    Évidement, les échanges des messages Windows ne peuvent se faire qu'avec entre session et même avec les communications inter-processus, il faut échanger des messages avec des fenêtres visibles et actives de d'une session autre que celle courante.
    J'ai reprise le code de François Piette :
    http://francois-piette.blogspot.com/...ing-pipes.html
    et apparemment, il faut que les IsWindowVisible et IsWindowEnabled sont vérifiées pour qu'un SafeSendMessage puisse se faire :

    Extrait du code de l'unité Pipes.pas du composant IPC de François Piette :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    function TPipeThread.SafeSendMessage(AMsg : UINT; AWParam : WPARAM;
        ALParam : LPARAM): LRESULT;
    begin
        // Check notification window
        if IsWindow(FNotify) then
            // Send the message
            Result := SendMessage(FNotify, AMsg, AWParam, ALParam)
        else
            // Failure
            Result := 0;
    end;
    Si le Handle du Window à notifier n'est pas valide alors l'envoi du message ne sera pas fait.

    Bonne journée

  6. #6
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 933
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 933
    Par défaut
    Une fenêtre n'a pas besoin d'être visible et encore moins active pour recevoir des messages, il faut juste qu'elle possède une boucle de messages. Il est même très fréquent de créer des fenêtres message-only sans aucune partie graphique.

    Quant à cette exemple, il ne devrait pas être utilisé dans une communication inter-process. IsWindow contrôle simplement que le handle soit valide mais rien ne dit que le processus qui a servi à déterminer FNotify soit toujours en cours et s'il ne l'est plus, que le handle n'a pas déjà été réutilisé par une autre application. Le message risque d'être envoyé à la mauvaise fenêtre. Mieux vaut passer par un FindWindow avant SendMessage

    Le MSDN met d'ailleurs en garde sur l'utilisation de IsWindow entre threads. Dans une même application, les risques sont limités mais il faut y penser.

Discussions similaires

  1. Réponses: 11
    Dernier message: 05/04/2009, 11h31
  2. Valeur BCD non correcte
    Par JP.NUAGE dans le forum Bases de données
    Réponses: 1
    Dernier message: 28/11/2008, 11h26
  3. Affichage info-bulle non correct suivant le navigateur
    Par [ced] dans le forum Mise en page CSS
    Réponses: 9
    Dernier message: 18/06/2008, 09h38
  4. Affichage non correct d'une image
    Par AnonCoder dans le forum Langage
    Réponses: 2
    Dernier message: 03/11/2006, 13h51
  5. Variables javascript non correctement définies
    Par LLaurent dans le forum XMLRAD
    Réponses: 5
    Dernier message: 11/05/2004, 12h39

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo