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

  1. #1
    Membre averti 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 : 42
    Localisation : Tunisie

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

    Informations forums :
    Inscription : Mars 2006
    Messages : 219
    Points : 370
    Points
    370
    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.
    "L'homme supérieur est celui qui a une bienveillance égale pour tous, et qui est sans égoïsme et sans partialité." [Confucius]
    "Celui qui n'évolue pas disparaît." [Charles Darwin]
    “Without requirements or design, programming is the art of adding bugs to an empty text file.” [Louis Srygley]

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 685
    Points : 13 102
    Points
    13 102
    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 averti 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 : 42
    Localisation : Tunisie

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

    Informations forums :
    Inscription : Mars 2006
    Messages : 219
    Points : 370
    Points
    370
    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
    "L'homme supérieur est celui qui a une bienveillance égale pour tous, et qui est sans égoïsme et sans partialité." [Confucius]
    "Celui qui n'évolue pas disparaît." [Charles Darwin]
    “Without requirements or design, programming is the art of adding bugs to an empty text file.” [Louis Srygley]

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 685
    Points : 13 102
    Points
    13 102
    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 averti 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 : 42
    Localisation : Tunisie

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

    Informations forums :
    Inscription : Mars 2006
    Messages : 219
    Points : 370
    Points
    370
    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
    "L'homme supérieur est celui qui a une bienveillance égale pour tous, et qui est sans égoïsme et sans partialité." [Confucius]
    "Celui qui n'évolue pas disparaît." [Charles Darwin]
    “Without requirements or design, programming is the art of adding bugs to an empty text file.” [Louis Srygley]

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 685
    Points : 13 102
    Points
    13 102
    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.

  7. #7
    Membre averti 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 : 42
    Localisation : Tunisie

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

    Informations forums :
    Inscription : Mars 2006
    Messages : 219
    Points : 370
    Points
    370
    Par défaut
    Bonjour,
    Dans une session courante, on peut lancer une application en mode normal en avant plan comme on peut la lancer en arrière plan avec les tâches planifiées.
    Si elle est lancée en mode utilisateur déconnecté ( ce qui est mon cas ) , mon application est dans le gestionnaire de tâche comme étant un processus actif mais il n'y a rien comme fenêtre active ( pas de fenêtre principale ni de traitement lancé vu par l'utilisateur ) . Comment alors récupérer le bon THandle de mon TMainForm par exemple lorsque cette dernière est en arrière plan ?
    Si j'arrive à le trouver, je peux donc échanger un message avec ce TMainForm via le SendMessage, non ?
    Merci d'avance.
    "L'homme supérieur est celui qui a une bienveillance égale pour tous, et qui est sans égoïsme et sans partialité." [Confucius]
    "Celui qui n'évolue pas disparaît." [Charles Darwin]
    “Without requirements or design, programming is the art of adding bugs to an empty text file.” [Louis Srygley]

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 685
    Points : 13 102
    Points
    13 102
    Par défaut
    Je ne suis même pas encore assez précis lorsque je parle de sessions, les messages Windows ne peuvent être échangés que par des applications s'exécutant sur le même bureau. En lançant la tâche avec l'option Exécuter même si aucun utilisateur n'a ouvert de session, cette application est exécutée dans la session 0 (dédiée aux services) dans laquelle est créé un nouvel environnement sécurisé (Windows Station)

    Un petite essai pour s'en convaincre. Ce code renvoie l'identificateur de session, le Window Station et le desktop. Si une seule donnée diffère, pas de dialogue par message possible:

    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
    function GetObjectName(aHandle :THandle) :string;
    var
      Len :cardinal;
    begin
      if aHandle <> 0 then
      begin
        GetUserObjectInformation(aHandle, UOI_NAME, nil, 0, Len);
        SetLength(Result, Len div SizeOf(Char) -1);
        GetUserObjectInformation(aHandle, UOI_NAME, PChar(Result), Len, Len);
      end
      else Result := '?';
    end;
     
    procedure TForm1.FormCreate(Sender: TObject);
    var
      s  :TStringList;
      SessionID :cardinal;
    const
      FileName = 'D:\Temp\ProcessInfo.txt';
    begin
      s := TStringList.Create;
     
      try
        ProcessIdToSessionId(GetCurrentProcessID, SessionID);
     
        s.Add('Session ID: ' +IntToStr(SessionID));
        s.Add('Window Station: ' +GetObjectName(GetProcessWindowStation));
        s.Add('Desktop name: ' +GetObjectName(GetThreadDesktop(GetCurrentThreadId)));
     
        s.SaveToFile(FileName);
      finally
        s.Free;
      end;
    end;

  9. #9
    Membre averti 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 : 42
    Localisation : Tunisie

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

    Informations forums :
    Inscription : Mars 2006
    Messages : 219
    Points : 370
    Points
    370
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    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
    function GetObjectName(aHandle :THandle) :string;
    var
      Len :cardinal;
    begin
      if aHandle <> 0 then
      begin
        GetUserObjectInformation(aHandle, UOI_NAME, nil, 0, Len);
        SetLength(Result, Len div SizeOf(Char) -1);
        GetUserObjectInformation(aHandle, UOI_NAME, PChar(Result), Len, Len);
      end
      else Result := '?';
    end;
     
    procedure TForm1.FormCreate(Sender: TObject);
    var
      s  :TStringList;
      SessionID :cardinal;
    const
      FileName = 'D:\Temp\ProcessInfo.txt';
    begin
      s := TStringList.Create;
     
      try
        ProcessIdToSessionId(GetCurrentProcessID, SessionID);
     
        s.Add('Session ID: ' +IntToStr(SessionID));
        s.Add('Window Station: ' +GetObjectName(GetProcessWindowStation));
        s.Add('Desktop name: ' +GetObjectName(GetThreadDesktop(GetCurrentThreadId)));
     
        s.SaveToFile(FileName);
      finally
        s.Free;
      end;
    end;
    Salut,
    J'ai copié/collé cette source , j'ai comme informations dans le fichier ProcessInfo.txt

    • Session ID: 0
    • Window Station: WinSta0
    • Desktop name: Default

    Mais la Session ID est (via le gestionnaire de tâches) est par exemple 3.
    Je n'ai pas trop saisi les valeurs de retours du code car tout se joue sur la valeur du PID du process actuel (par GetCurrentProcessID) donc normalement, je dois avoir 3 comme Session ID
    Merci
    "L'homme supérieur est celui qui a une bienveillance égale pour tous, et qui est sans égoïsme et sans partialité." [Confucius]
    "Celui qui n'évolue pas disparaît." [Charles Darwin]
    “Without requirements or design, programming is the art of adding bugs to an empty text file.” [Louis Srygley]

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 685
    Points : 13 102
    Points
    13 102
    Par défaut
    C'est que tu as lancer plusieurs fois le programme depuis des sessions différentes et écrasé le fichier

    Lance-le une fois depuis ton bureau et regarde ce que ça donne. Puis lance-le une fois par tâche planifiée et regarde encore (quitte à tuer la tâche par le gestionnaire entre chaque essai, ça évitera les quiproquos) ou encore mieux, renomme aléatoirement ce fichier.
    Tu es manifestement sous XP, une interaction pourrait être possible avec le premier utilisateur (pas les suivants) mais à oublier depuis Vista. Tu devrais vraiment oublier ces messages et passer par une autre forme d'IPC.

  11. #11
    Membre averti 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 : 42
    Localisation : Tunisie

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

    Informations forums :
    Inscription : Mars 2006
    Messages : 219
    Points : 370
    Points
    370
    Par défaut
    Citation Envoyé par Andnotor Voir le message
    C'est que tu as lancer plusieurs fois le programme depuis des sessions différentes et écrasé le fichier .
    Je l'ai lancé une plusieurs fois mais dans la session "Administrateur" actuelle.
    Citation Envoyé par Andnotor Voir le message
    Lance-le une fois depuis ton bureau et regarde ce que ça donne.
    j'ai mis l'exe généré sur le bureau, j'ai lancé pour voir, ça donne toujours : Session ID: 0; Window Station: WinSta0; Desktop name: Default
    Citation Envoyé par Andnotor Voir le message
    Puis lance-le une fois par tâche planifiée et regarde encore (quitte à tuer la tâche par le gestionnaire entre chaque essai, ça évitera les quiproquos) ou encore mieux, renomme aléatoirement ce fichier.
    ça donne toujours les mêmes résultats dans le fichier de sortie.
    Citation Envoyé par Andnotor Voir le message
    Tu es manifestement sous XP, une interaction pourrait être possible avec le premier utilisateur (pas les suivants) mais à oublier depuis Vista. Tu devrais vraiment oublier ces messages et passer par une autre forme d'IPC.
    c'est la solution idéale : en faite, j'ai repris les composants que François Piette a créé à la base de l'unité Pipes.pas qui a été créée pour la première fois par Russell Libby en 2003. (Lien: http://francois-piette.blogspot.com/...ing-pipes.html). L'avantage de ces composants, c'est que je vais utiliser directement des méthodes du serveur pour se connecter et envoyer des messages à un/plusieurs clients. Pour le moment, ce message est un Stream et donc je récupère des entêtes de types chaînes. Après je vais essayer de voir si je peux échanger d'autres types de messages ( pourquoi pas des booléens dont j'ai absolument besoin ).
    "L'homme supérieur est celui qui a une bienveillance égale pour tous, et qui est sans égoïsme et sans partialité." [Confucius]
    "Celui qui n'évolue pas disparaît." [Charles Darwin]
    “Without requirements or design, programming is the art of adding bugs to an empty text file.” [Louis Srygley]

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