Announcement

Collapse
No announcement yet.

Überprüfen, ob ein Fenster einer anderen Anwendung angezeigt wird.

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Überprüfen, ob ein Fenster einer anderen Anwendung angezeigt wird.

    Hallo zusammen,
    erst mal ein gesundes,neues Jahr.
    Wie kann ich mit Delphi herrausfinden, ob ein Fenster aus einem anderen Programm angezeigt wird und noch besser, wie kann ich in diesem Fenster die OK-Button drücken.
    Ich erstelle automatisch PDF-Dateien, muss aber immer von Hand den Dateinamen reinschreiben und dann den OK-Button drücken.

    Danke für die Hilfe,

    Maik

  • #2
    Moin Maik,<br>
    <br>
    eine Mögliche Variante für Dein Problem:<br>
    Mit FindWindow feststellen, ob das gewünschte Fenster da ist, dann mit EnumChildWindows die darin befindlichen Children ermitteln.<br>
    Innerhalb der Callback Function kannst Du schon auf die Control ID prüfen (GetWindowLong mit GWL_ID), und ggf. abbrechen, wenn Du das Handle auf das Edit Feld bzw. den Button hast.<br>
    Dann noch mit SendMessage/WM_SETTEXT den Namen eintragen (Achtung: SetWindowText funktioniert prozessübergreifend nicht sauber), und mit SendMessage/BM_CLICK den Button drücken.<br>
    Die ID's für Edit/Button kannst Du z.B. mit WinSight32 aus Delphi ermitteln, oder Du machst einen Probelauf und liest in der EnumChildWindow Callback Funktion auch noch die Klassen der Child Fenster (GetClassName) aus und lässt sie Dir zusammen mit der ID anzeigen (geht bei den Massen die Winsight so liefert vielleicht sogar einfacher)<br>
    <br>
    Ciao<br>
    Chri

    Comment


    • #3
      Danke für die Hilfe,

      ich habe noch nie mit Fensterhandle gearbeitet, daher habe ich leichte Startschwierigkeiten. Es ist ein Dialogfenster mit dem Namen 'PDF-Dateien speichern unter' im Taskmanaker wird es aber nicht angezeigt. jetzt habe ich versucht hwnd:=findwindow(PCHAr(''),PChar(PDF-DA...)). da bekomme ich aber kein ergebnis.
      Hast du vielleicht mal ein kleines Beispiel. Fenster finden, Editfeld und Wert rein schreiben und ok-Button drücken.

      Danke dir für die Bemühungen.

      Ciao
      Mai

      Comment


      • #4
        Das Fensterhandl habe ich jetzt gefunden. Nach 2 Stunden suchen.
        Wie bekomme ich jetzt noch die Id von Edit und Button raus?
        Wenn ich die ID suche, welche Suchwerte aus WinSight gebe ich dann ein?
        Danke für die Hilfe

        Comment


        • #5
          Moin Maik,<br>
          <br>
          Ich hab' Dir mal ein Muster gebastelt (getestet). Schau's Dir am Besten erst einmal an, wieweit Du damit klarkommst (vermutlich werden Fragen offenbleiben). Um eine Liste ALLER Klassennamen und ID's des betreffenden Fensters zu bekommen, musst Du nur die Abfragen nach BUTTON / EDIT usw. weglassen (Standardklassennamen, können variieren) und alle Texte und ID's in die Ergebnisliste schreiben, und dann, z.B. mit ShowMessage(slResult.Text) ausgeben. Dann siehst Du schneller was Du brauchst, als mit WinSight, denke ich mal.<br>
          <br>
          <pre>
          function
          EnumChildProc(
          const p_hChild : DWord;
          const p_lParam : lParam
          ) : Boolean; stdcall;

          var
          sWindowText : string;
          sClassName : string;
          dwChildID : DWord;

          begin
          Result := true;
          sClassName := StringOfChar(#00,256);
          if GetClassName(p_hChild,PChar(sClassName),255) > 0 then
          begin
          if UpperCase(Trim(sClassName)) = 'BUTTON' then
          begin
          dwChildID := GetWindowLong(p_hChild,GWL_ID);
          sWindowText := StringOfChar(#00,256);
          SendMessage(p_hChild,WM_GETTEXT,255,lParam(PChar(s WindowText)));
          if UpperCase(Trim(sWindowText)) = 'OK' then
          begin
          TStrings(Pointer(p_lParam)^).Add('BUTTON=$'+IntToH ex(p_hChild,8));
          TStrings(Pointer(p_lParam)^).Add('BUTTONID='+IntTo Str(dwChildID));
          end;
          end
          else
          begin
          if UpperCase(Trim(sClassName)) = 'EDIT' then
          begin
          dwChildID := GetWindowLong(p_hChild,GWL_ID);
          TStrings(Pointer(p_lParam)^).Add('EDIT=$'+IntToHex (p_hChild,8));
          TStrings(Pointer(p_lParam)^).Add('EDITID='+IntToSt r(dwChildID));
          end;
          end;
          end;
          end;

          procedure
          SetAndClick(
          const p_sFileName : string
          );

          var
          hDialog : DWord;
          dwThreadIDCurrent : DWord;
          dwThreadIDWindow : DWord;
          slResult : TStringList;
          hEdit : DWord;
          hButton : DWord;

          begin
          try
          slResult := TStringList.Create;
          hDialog := FindWindow(nil,PChar('PDF-Dateien speichern unter'));
          if hDialog <> 0 then
          begin
          dwThreadIDCurrent := GetCurrentThreadID;
          dwThreadIDWindow := GetWindowThreadProcessID(hDialog,nil);
          if AttachThreadInput(dwThreadIDCurrent,dwThreadIDWind ow,true) then
          begin
          SetActiveWindow(hDialog);

          EnumChildWindows(hDialog,@EnumChildProc,Integer(@s lResult));
          if slResult.Values['EDIT'] <> '' then
          begin
          hEdit := StrToInt(slResult.Values['EDIT']);
          SendMessage(hEdit,WM_SETTEXT,Length(p_sFileName),l Param(PChar(p_sFileName)));
          end;
          if slResult.Values['BUTTON'] <> '' then
          begin
          hButton := StrToInt(slResult.Values['BUTTON']);
          SendMessage(hButton,BM_CLICK,0,0);
          end;

          AttachThreadInput(dwThreadIDCurrent,dwThreadIDWind ow,false);
          SetActiveWindow(Form1.Handle);
          end;
          end;
          finally
          FreeAndNil(slResult);
          end;
          end;
          </pre>
          <br>
          Ciao<br>
          Chri

          Comment


          • #6
            Hallo Chris,

            bei mir hängt das Programm immer in der Zeile: EnumChildWindows(hDialog,@EnumChildProc,Integer(@s lResult)); mit der Fehlermeldung Variable erforderlich hinter @EnumChildProc,. Kannst du mir bitte noch einmal helfen?

            Danke dir

            Mai

            Comment


            • #7
              Wenn ich die Fensternummer weiß, und ich den Klassennamen habe und ich den Fenstertext habe, wieso ist es dann so schwer die Id rauszubekommen. Gibt es keine Funktion
              getChildWindows(Handl,Classe,Name)?

              Würde mich freuen, wenn mir jemand helfen kann.

              Bis dann

              Mai

              Comment


              • #8
                Moin Maik,<br>
                <br>
                zum zweiten:<br>
                dwChildID := GetWindowLong(p_hChild,GWL_ID);<br>
                finde ich persönlich nicht so sehr lang.<br>
                Die ID zu bekommen ist also nicht so schwer, solange man das Handle hat. Sie muss dann allerdings noch zugeordnet werden. Und wenn Du die ID einmal hast, kannst Du auch direkt damit arbeiten, denn diese ändert sich nicht, nach meiner Erfahrung, nicht einmal, wenn sich die Sprachversion ändert. Da sich manchmal die Texte ändern, ist die ID damit die sicherere Methode das richtige Element anzusprechen.<br>
                <br>
                zum ersten:<br>
                Den Fehler kann ich nicht nachvollziehen. Tritt bei mir nicht auf.<br>
                <br>
                Welche Delphi Version benutzt Du denn? Ich verwende D5 pro SP1 dt.<br>
                <br>
                Ciao<br>
                Chri

                Comment


                • #9
                  Die Antwort ist super. Ist echt nicht zu lang :-).
                  Nur was ist p_HChild. Wie bekomme ich in die Variable meine Classe und den Fensternamen. Mir ist das schon langsam Peinlich, dass ich das nicht raffe.
                  Ich habe auch d5 pro

                  Comment


                  • #10
                    Moin Maik,<br>
                    <br>
                    ich finde nicht, dass es Dir peinlich sein muss nachzufragen. Wenn Du bislang noch nichts in der Richtung gemacht hast ist der Ablauf auch nicht unbedingt leicht nachzuvollziehen. Ich habe das ganze ja auch, vorsichtig formuliert, nicht so arg umfassend dokumentiert, das versuche ich jetzt mal nachzuholen.<br>
                    <br>
                    Funktion SetAndClick:<br>
                    Die wichtigen Funktionen um dass gewünschte auszuführen sind<br>
                    <br>
                    - FindWindow<br>
                    Dient, wie Du ja schon weisst, dazu das Handle des gewünschten Fensters zu ermitteln.<br>
                    Ist dessen Titel eindeutig, kann der Name der Klasse weggelassen werden und wird auf nil gesetzt.<br>
                    <br>
                    - EnumChildWindows<br>
                    Ruft für alle Unterfenster des mit dem Handle hDialog indentifizierten Fensters die Funktion EnumChildProc auf.<br>
                    Der dritte Parameter steht zur freien Verfügung. Ich habe ihn dazu benutzt die Adresse einer StringListe zu<br>
                    übergeben, in der EnumChildProc die Ergebnisse ablegen kann.<br>
                    Ersatzweise hätte es auch auch eine globale Variable getan, in die dann EnumChildProc direkt schreibt.<br>
                    <br>
                    - SendMessage<br>
                    Mit WM_SETTEXT wird der Wert des Editfeldes auf den Dateinamen (p_sFileName) gesetzt.<br>
                    Mit BM_CLICK wird der Button gedrückt.<br>
                    <br>
                    Das Drumherum, dient nur dazu sicherzustellen, dass die Daten auch richtig ankommen. Oft funktioniert es auch ohne.<br>
                    Wichtig ist noch, nicht ersatzweise SetWindowText zu verwenden, da diese Funktion prozessübergreifend nicht zuverlässig funktioniert.<br>
                    <br>
                    Function EnumChildProc:<br>
                    Diese Funktion wird, wie oben schon erwähnt, für jedes Unterfenster aufgerufen.<br>
                    Dabei übergibt Windows das Handle dieses Fensters in p_hChild, sowie den frei gewählten Wert, der dem dritten Parameter in EnumChildWindows entspricht. Bliebe noch anzumerken, dass, im Prinzip, jedes Unterelement, dass ein Handle hat einem ChildWindow entspricht. Ganz hart gesehen ist (z.B.) ein Button ein Fenster mit ganz speziellen Eigenschaften.<br>
                    In dieser Funktion kann man dann tun und lassen was man will.<br>
                    Mit GetClassName wird hier der Klassenname des Fensters ermittelt, das durch p_hCild repräsentiert wird. Ist dies ein BUTTON, so wird die ID dieses Buttons ermittelt, und mit WM_GETTEXT auch die Beschriftung (für WM_GETTEXT/GetWindowText gilt das gleiche wie für WM_SETEXT/SetWindowText). Anschliessend werden diese Werte dann in die Ergebnisliste übernommen.<br>
                    Die Abfrage des Textes dient nur dazu die Zuordnung zwischen ID und Button herzustellen, weil das vermutlich leichter ist, als mit WinSight.<br>
                    War es kein Button, wird auf Edit geprüft, und ggf. dessen ID zurückgegeben.<br>
                    Ein WM_GETTEXT macht hier keinen Sinn, da das Feld vermutlich leer ist.<br>
                    <br>
                    In der eigentlichen Version, die Du benötigst, genügt es dann auf die ID abzufragen und einfach nur das Handle zurückzugeben.<br>
                    <br>
                    Damit das übersichtlicher wird, werde ich noch mal eine vereinfachte Version basteln.<br>
                    <br>
                    Ich hoffe, dass das ganze jetzt etwas klarer geworden ist, wenn nicht, hmm..., muss ich mir noch was anderes einfallen lassen.<br>
                    <br>
                    Ciao, bis später<br>
                    Chri

                    Comment


                    • #11
                      Moin Maik,<br>
                      <br>
                      so, und nun die überarbeitete und kommentierte Version.<br>
                      (ebenfalls getestet)
                      <br>
                      <pre>
                      var
                      g_fButtonFound : Boolean; // Flag Button gefunden
                      g_fEditFound : Boolean; // Flag Edit gefunden
                      g_hButton : DWord; // Handle des Button
                      g_hEdit : DWord; // Handle des Edit

                      const // Hier die Werte eintragen, die mit CollectChildInfoTemp ermittelt
                      // werden konnten. Die hier angegebenen Werte stammen aus meinem Test
                      // und müssenw wohl ersetzt werden.
                      _BUTTONID = 197860;
                      _EDITID = 197858;

                      // Variante 1 für EnumChildProc
                      // Diese dient NUR zur Ermittlung der ID's für Button und Edit.
                      // Sie wird im eigentlichen Programm nicht mehr gebraucht!
                      function
                      CollectChildInfoTemp(
                      const p_hChild : DWord; // Handle des gerade gefundenen Childwindows
                      const p_lParam : lParam // Anwendungsdefinierter Parameter
                      ) : Boolean; stdcall;

                      // Variante 2 für EnumChildProc
                      // Diese Variante gehört dauerhaft ins Programm um die jeweils aktuellen Werte
                      // für die beiden Handle zu ermitteln.
                      function
                      GetControlHandle(
                      const p_hChild : DWord; // Handle des gerade gefundenen Childwindows
                      const p_lParam : lParam // Anwendungsdefinierter Parameter
                      ) : Boolean; stdcall;

                      procedure
                      SetAndClick(
                      const p_sFileName : string
                      );

                      implementation

                      {$R *.DFM}

                      // Wie EnumChildProc genannt wird spielt keine Rolle, wichtig ist der Aufbau der
                      // der Funktionsdeklaration.
                      // stdcall ist wichtig, damit die Aufrufkonventionen eingehalten werden
                      // (betrifft die Parameterübergabe an Windows)

                      // Diese Variante von EnumChildProc soll nur alle Button, Edits usw. des Fensters
                      // ermitteln, und kann später entfallen. Da Buttons usw. als Controls bezeichnet
                      // werden, habe ich die ID dwControlID genannt.
                      // Deshalb wird auch der zweite Parameter nicht mehr benutzt, sondern das Ergebnis
                      // direkt ausgegeben.
                      // Da je nach Sprache mit der das abzufragende Programm erstellt wurde die Klassen-
                      // namen variieren können, werden auch diese mit ausgegeben.

                      function
                      CollectChildInfoTemp(
                      const p_hChild : DWord; // Handle des gerade gefundenen Childwindows
                      const p_lParam : lParam // Anwendungsdefinierter Parameter
                      ) : Boolean; stdcall;

                      var
                      sWindowText : string; // Fenstertitel/Caption
                      sClassName : string; // Klassenname
                      dwControlID : DWord; // Control ID

                      begin
                      Result := true; // wenn TRUE wird weiter aufgezählt bis alle durch sind.
                      sClassName := StringOfChar(#00,256);
                      if GetClassName(p_hChild,PChar(sClassName),255) > 0 then
                      begin
                      dwControlID := GetWindowLong(p_hChild,GWL_ID);
                      sWindowText := StringOfChar(#00,256);
                      SendMessage(p_hChild,WM_GETTEXT,255,lParam(PChar(s WindowText)));
                      ShowMessage('Klasse: '+trim(sClassName)+#13#10+
                      'Text/Caption: '+trim(sWindowText)+#13#10+
                      'ID: '+IntToStr(dwControlID));
                      end;
                      end;

                      &#10

                      Comment


                      • #12
                        <pre>
                        <pre>
                        // Diese Variante von EnumChildProc dient dazu die aktuellen Handle der beiden
                        // gewünschten Controls zu ermitteln.
                        // Handles ändern sich bei jedem Erzeugen eines Fensters (meist also beim Start des
                        // Programmes), die ID's bleiben jedoch immer gleich
                        function
                        GetControlHandle(
                        const p_hChild : DWord; // Handle des gerade gefundenen Childwindows
                        const p_lParam : lParam // Anwendungsdefinierter Parameter
                        ) : Boolean; stdcall;

                        var
                        dwControlID : DWord; // Control ID

                        begin
                        // Es wird nur weitergemacht, wenn noch nicht beide Handle gefunden wurden
                        Result := not(g_fButtonFound and g_fEditFound);
                        case GetWindowLong(p_hChild,GWL_ID) of
                        _BUTTONID : // Oben definierte Konstante mit der ID des Buttons
                        begin
                        g_hButton := p_hChild; // Handle des Button setzen
                        g_fButtonFound := true; // Flag setzen Button gefunden
                        end;
                        _EDITID : // Oben definierte Konstante mit der ID des Edit
                        begin
                        g_hEdit := p_hChild; // Handle des Edit setzen
                        g_fEditFound := true; // Flag setzen Edit gefunden
                        end;
                        end;
                        end;

                        </pre&gt

                        Comment


                        • #13
                          <pre>
                          procedure
                          SetAndClick(
                          const p_sFileName : string
                          );

                          var
                          hDialog : DWord;
                          dwThreadIDCurrent : DWord;
                          dwThreadIDWindow : DWord;

                          begin
                          // Fensterhandle ermitteln
                          hDialog := FindWindow(nil,PChar('PDF-Dateien speichern unter'));
                          if hDialog <> 0 then
                          begin
                          dwThreadIDCurrent := GetCurrentThreadID;
                          dwThreadIDWindow := GetWindowThreadProcessID(hDialog,nil);
                          if AttachThreadInput(dwThreadIDCurrent,dwThreadIDWind ow,true) then
                          begin
                          SetActiveWindow(hDialog);

                          // NUR um die ID's von Button und Edit zu ermitteln
                          // entfällt im eigentlichen Programm
                          // Bei einer neuen Programmversion des Fremdprogrammes können sich
                          // die ID's natürlich ändern, und müssen angepasst werden
                          EnumChildWindows(hDialog,@CollectChildInfoTemp,0);

                          // Dies ist der eigentliche Funktionsaufruf.
                          // Nachdem die ID's ermittelt, und oben als Konstanten eingetragen wurden
                          // kann der Aufruf mit CollectChildInfoTemp gelöscht werden, dafür muss dan
                          // ab hier die Auskommentierung entfernt werden.

                          { g_fButtonFound := false; // Flags initialisieren
                          g_fEditFound := false;
                          // Handle der Controls ermitteln
                          EnumChildWindows(hDialog,@GetControlHandle,0);
                          if g_fButtonFound
                          and
                          g_fEditFound then
                          begin // Nur wenn die Button und Edit gefunden wurden
                          SendMessage(g_hEdit,WM_SETTEXT,Length(p_sFileName) ,lParam(PChar(p_sFileName)));
                          SendMessage(g_hButton,BM_CLICK,0,0);
                          end;
                          }
                          AttachThreadInput(dwThreadIDCurrent,dwThreadIDWind ow,false);
                          SetActiveWindow(Form1.Handle);
                          end;
                          end;
                          end;

                          procedure TForm1.Button1Click(Sender: TObject);
                          begin
                          SetAndClick('TEST');
                          end;

                          </pre>
                          <br>
                          Ciao<br>
                          Chri

                          Comment

                          Working...
                          X