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

Langage Delphi Discussion :

Delphi XE 2 : GetWindowText ne fonctionne pas


Sujet :

Langage Delphi

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    147
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Mars 2006
    Messages : 147
    Points : 84
    Points
    84
    Par défaut Delphi XE 2 : GetWindowText ne fonctionne pas
    Bonjour,

    Je crée une fenêtre dans une application console et dans une DLL afin d'échanger des messages entre processus.
    J'ai un exécutable "SVC" et une dll "DLL" (loadée dans un autre exécutable "EXE").

    J'utilise un thread qui va créer dans SVC et dans DLL une fenêtre en utilisant la fonction AllocateHWND, puis la nomme à l'aide de SetWindowText.
    Ensuite, les SendMessage sont fait en utilisant FindWindow(nil, 'text').

    Mais ça ne fonctionne pas, car FindWindow ne retrouve jamais la fenêtre en question.
    Comme FindWindow utilise en interne GetWindow, j'ai voulu vérifier ce que GetWindow renvoie :


    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
     
    Constructor TMes.Create(Proc: TBbProc);
    var dbg : array[0..MAX_PATH - 1] of char;
    begin
      WHO_AM_I := Proc; // SVC ou DLL
      WM_PRG_TO_DLL_CONNECT := RegisterWindowMessage('WM_PRG_TO_DLL_CONNECT');
      WM_PRG_TO_SVC_ALIVE := RegisterWindowMessage('WM_PRG_TO_SVC_ALIVE');
     
      FWinHandle := AllocateHWND(WndProc);
     
     
      if WHO_AM_I = DLL then SetWindowText(FWinHandle, 'dll');
      if WHO_AM_I = SVC then SetWindowText(FWinHandle, 'svc');
     
      // Pour vérifier :
      GetWindowText(FWinHandle, dbg, Length(dbg));
      trace('Windows text = ' + dbg);
      trace('Windows text = ' + String(dbg));
     
     
      inherited Create(False);
    end;
    Les 2 appels à trace fournissent ces deux lignes de log:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    [26/05/2014 16:27:10 TExternalThread] Windows text = 
    [26/05/2014 16:27:10 TExternalThread] Windows text =
    Une idée ?

    André.

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 693
    Points : 13 128
    Points
    13 128
    Par défaut
    Tu oublies que c'est à toi de coder la boucle de messages
    Donc à moins d'implémenter toi-même WM_SETTEXT, WM_GETTEXT, il faut appeler le gestionnaire par défaut.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    procedure TMes.WndProc(var Message: TMessage);
    begin
      with Message do
        if Msg = WM_PRG_TO_DLL_CONNECT then
          Result := ...
     
        else if Msg = WM_PRG_TO_SVC_ALIVE then
          Result := ...
     
        else Result := DefWindowProc(FWinHandle, Msg, wParam, lParam);
    end;

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    147
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Mars 2006
    Messages : 147
    Points : 84
    Points
    84
    Par défaut
    Ouio bien sûr, je n'ai pas mis ce code dans mon post mais il existe. Ce n'est pas le problème :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    procedure TMes.WndProc(var msg: TMessage);
    begin
      trace('WndProc : entrée (réception d''un message) ');
      if (Msg.Msg = WM_PRG_TO_DLL_CONNECT) and (WHO_AM_I = Dll) then begin
        trace('WndProc : Réception de WM_PRG_TO_DLL_CONNECT');
      end;
      if (Msg.Msg = WM_PRG_TO_SVC_ALIVE) and (WHO_AM_I = Svc) then begin
        trace('WndProc : Réception de WM_PRG_TO_SVC_ALIVE');
      end;
    end;
    Heu ... Je parle des API SetWindowText et GetWindowText, pas des messages inter-process.

    Pour faire simple, et en dehors du contexte Thread/Message, si je veux simplement créer une Windows et lui attribuer un titre, je fais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
     
    FWinHandle := AllocateHWND(WndProc); 
    SetWindowText(FWinHandle, 'Le titre que je veux');
     
    // Pour vérifier :
    GetWindowText(FWinHandle, dbg, Length(dbg));
    En dehors de tout Thread, WM etc., je devrais bien récupérer 'Le titre que je veux' dans ma variable dbg non ?

    En clair pour envoyer le message à la bonne fenêtre je faisais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SendMessageTimeout(nil,FindWindow(PChar(WinProc[TBbProc.SVC])), WM_PRG_TO_SVC_ALIVE, 0, 0, SMTO_BLOCK, 1000, nil);
    mais comme FindWindow ne marche pas (parce que GetWindowsText ou SetWindowText ne marchent pas), j'utilise :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SendMessageTimeout(FindWindow(PChar(WinProc[TBbProc.SVC]),nil), WM_PRG_TO_SVC_ALIVE, 0, 0, SMTO_BLOCK, 1000, nil);
    La ça fonctionne car le FindWindow est fait sur la classe de la fenêtre (mais j'ai dû refaire un AllocateHWND qui permet de passer la classe en paramètre quand on crée la Windows). Donc, j'ai contourné le problème en réécrivant un AllocateHWND maison, auquel je peux passer la class en paramètre.

    Mais pourquoi donc le SetWindowText/GetWindowsText ne fonctionnent-ils pas ?

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 693
    Points : 13 128
    Points
    13 128
    Par défaut
    Regarde mieux

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    procedure TMes.WndProc(var Message: TMessage);
    begin
      with Message do
        if Msg = WM_PRG_TO_DLL_CONNECT then
          Result := ...
     
        else if Msg = WM_PRG_TO_SVC_ALIVE then
          Result := ...
     
        else Result := DefWindowProc(FWinHandle, Msg, wParam, lParam);
    end;

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    147
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Mars 2006
    Messages : 147
    Points : 84
    Points
    84
    Par défaut
    Désolé Andnotor, je pense qu'on ne parle vraiment pas de la même chose, je dois mal m'exprimer.

    Je parle uniquement du FindWindow, qui utilise GetWindowText, ... qui ne fonctionne pas comme je m'y attends. Oublie les WM.

    Je reformule donc :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var dbg : array[0..MAX_PATH - 1] of char;
    
    // Je crée simplement une Windows
    FWinHandle := AllocateHWND(nil); 
    
    // Je définis son titre :  http://msdn.microsoft.com/en-us/library/windows/desktop/ms633546%28v=vs.85%29.aspx
    SetWindowText(FWinHandle, 'Le titre que je veux');
     
    // Je lis son titre : MARCHE PAS !!!! : http://msdn.microsoft.com/en-us/library/windows/desktop/ms633520%28v=vs.85%29.aspx
    GetWindowText(FWinHandle, dbg, Length(dbg));
    
    // dbg vaut '' après cet appel et non la chaîne passée dans SetWindowText
    SetWindowText et GetWindowText n'ont rien à voir avec les WM.

  6. #6
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 858
    Points : 11 301
    Points
    11 301
    Billets dans le blog
    6
    Par défaut
    FWinHandle := AllocateHWND(nil); crée bien une fenêtre, mais dont la procédure de traitement des messages est non assignée :
    The Method parameter specifies the window procedure that the generated window uses to respond to messages.
    Auquel cas a-t-elle sûrement du mal à répondre aux messages Windows, non ? Et j'imagine que les Get et Set Text se passent par messages...
    Delphi 5 Pro - Delphi 11.3 Alexandria Community Edition - CodeTyphon 6.90 sous Windows 10 ; CT 6.40 sous Ubuntu 18.04 (VM)
    . Ignorer la FAQ Delphi et les Cours et Tutoriels Delphi nuit gravement à notre code !

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 693
    Points : 13 128
    Points
    13 128
    Par défaut
    Dommage que tu ne veuilles même pas essayer

    Alors je vais t'expliquer cela plus en détail.

    Citation Envoyé par sinfoni Voir le message
    Je parle uniquement du FindWindow, qui utilise GetWindowText, ...
    C'est faux ! Si FindWindow passe par une autre API, ce sera InternalGetWindowText. FindWindow ne passe pas par des messages mais accède directement à la structure des fenêtres. Et encore heureux, interroger les milliers de handles du système par message serait une perte de temps considérable !

    L'aide sur GetWindowText :
    If the target window is owned by the current process, GetWindowText causes a WM_GETTEXT message to be sent to the specified window or control.
    Lorsque tu crées une fenêtre par AllocateHWND c'est à toi d'implémenter toute la logique dans WndProc et puisque GetWindowText envoie un WM_GETTEXT, il faut que ce message soit géré. A l'heure actuelle, ton gestionnaire de messages est vide (il ne traite que tes deux messages).

    Faisons un essai simple :
    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
    type
      TTest = class
      private
        Wnd :hWnd;
      protected
        procedure WndProc(var Message: TMessage);
      public
        constructor Create;
      end;
     
    implementation
     
    constructor TTest.Create;
    var
      s :array[0..MAX_PATH] of char;
     
    begin
      Wnd := AllocateHWND(WndProc);
     
      SetWindowText(Wnd, 'Hello world!');
      GetWindowText(Wnd, s, MAX_PATH);
     
      ShowMessage(s);
     
      DeallocateHWnd(Wnd);
    end;
     
    procedure TTest.WndProc(var Message: TMessage);
    begin
      //vide
    end;
    Résultat : WM_GETTEXT n'est pas traité, "s" est vide.

    Ajoutons maintenant une variable Text :string à la structure et implémentons les messages d'écriture/lecture :
    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
    type
      TTest = class
      private
        Wnd  :hWnd;
        Text :string;
      protected
        procedure WndProc(var Message: TMessage);
      public
        constructor Create;
      end;
     
    implementation
     
    constructor TTest.Create;
    var
      s :array[0..MAX_PATH] of char;
     
    begin
      Wnd := AllocateHWND(WndProc);
     
      SetWindowText(Wnd, 'Hello world!');
      GetWindowText(Wnd, s, MAX_PATH);
     
      ShowMessage(s);
     
      DeallocateHWnd(Wnd);
    end;
     
    procedure TTest.WndProc(var Message: TMessage);
    begin
      with Message do
        case Msg of
          WM_SETTEXT : begin
                         Text := PChar(lParam);
                         Result := 1;
                       end;
     
          WM_GETTEXT : begin
                         StrLCopy(PChar(lParam), PChar(Text), wParam);
                         Result := Length(Text);
                       end;
        end;
    end;
    Miracle ! GetWindowText retourne notre chaîne
    Par contre FindWindow ne fonctionne pas et c'est normal puisque la structure même de la fenêtre n'a pas été modifiée (Preuve que FindWindow ne passe pas par GetWindowText).

    Mais le but n'est pas de redéfinir l'ensemble du traitement des messages mais uniquement d'ajouter nos messages personnalisés. Ça ne veut cependant pas dire qu'il faut ignorer les messages standards et c'est pour cela qu'il faut faire appel au traitement par défaut, DefWindowProc. Un WndProc minimum se présente donc ainsi :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    procedure TTest.WndProc(var Message: TMessage);
    begin
      with Message do
        Result := DefWindowProc(Wnd, Msg, wParam, lParam);
    end;
    Et là, non seulement l'écriture et la lecture fonctionne mais FindWindow aussi

    En bref, SetWindowText et GetWindowText ne sont pas en cause et tu focalises trop là-dessus. C'est ta gestion des messages qui est incomplète !
    Si on parlait objet Delphi, par analogie, DefWindowProc serait comme le inherited d'une méthode surchargée

  8. #8
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    147
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Mars 2006
    Messages : 147
    Points : 84
    Points
    84
    Par défaut
    Et j'imagine que les Get et Set Text se passent par messages...
    Haaaaaa ! Là oui ! je comprends ! Double problème de communication Pigé... Si ces API utilisent elles-mêmes les WM, ce que j'ignorais, effectivement ça ne peut pas fonctionner...

    Dans tous les cas je préfère retrouver une Window par sa classe que par son titre, mais au moins j'ai compris le fin mot de l'histoire.

    Quant à FindWindow qui utilise GetWindowText, je n'ai fait que lire la doc Microsoft http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx :

    If the lpWindowName parameter is not NULL, FindWindow calls the GetWindowText function to retrieve the window name for comparison. For a description of a potential problem that can arise, see the Remarks for GetWindowText.


    Merci à vous deux !

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

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 693
    Points : 13 128
    Points
    13 128
    Par défaut
    Citation Envoyé par sinfoni Voir le message
    Dans tous les cas je préfère retrouver une Window par sa classe que par son titre
    Ce qui veut dire que tu passes maintenant par CreateWindow en fournissant un type de classe enregistré par RegisterClass. Mais dans les deux cas, pense tout de même que ce principe te limitera à une seule instance de la dll, FindWindow ne renverra que le premier handle trouvé, toujours le même
    Pour de multiple instances, il faudra soit avoir un message d'initialisation (lui passer une classe unique sous forme de texte à l'instar de WM_GETTEXT et gérer une liste), soit mettre en route un autre mode d'IPC, un pipe par exemple.

    Citation Envoyé par sinfoni Voir le message
    If the lpWindowName parameter is not NULL, FindWindow calls the GetWindowText function to retrieve the window name for comparison. For a description of a potential problem that can arise, see the Remarks for GetWindowText.
    En mettant un point d'arrêt dans WndProc de mon deuxième exemple, on remarque qu'on n'y passe pas à l'appel de FindWindow (en spécifiant ou non la classe). MSDN mériterait certainement une mise à jour, InternalGetWindowText n'étant publiée que depuis XP SP1

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    147
    Détails du profil
    Informations personnelles :
    Localisation : France, Nord (Nord Pas de Calais)

    Informations forums :
    Inscription : Mars 2006
    Messages : 147
    Points : 84
    Points
    84
    Par défaut
    ce principe te limitera à une seule instance de la dll
    C'est juste, et dans mon cas je n'ai qu'une seule instance.
    Mais je garde bien cela en tête

    MSDN mériterait certainement une mise à jour, InternalGetWindowText n'étant publiée que depuis XP SP1
    Effectivement !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Delphi, Compilé sous Oracle 10g ne fonctionne pas sous 9i
    Par l0pez dans le forum Bases de données
    Réponses: 1
    Dernier message: 15/12/2011, 10h11
  2. Réponses: 7
    Dernier message: 23/11/2010, 17h15
  3. UNION qui ne fonctionne pas
    Par r-zo dans le forum Langage SQL
    Réponses: 7
    Dernier message: 21/07/2003, 10h04
  4. Un Hint sur un PopupMenu ne fonctionne pas !!??
    Par momox dans le forum C++Builder
    Réponses: 6
    Dernier message: 26/05/2003, 16h48
  5. ca ne fonctionne pas (generateur auto-incrémentant)
    Par tripper.dim dans le forum SQL
    Réponses: 7
    Dernier message: 26/11/2002, 00h10

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