Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Wie findet man Fenster, deren Namen man nicht genau kennt? (https://www.delphipraxis.net/163055-wie-findet-man-fenster-deren-namen-man-nicht-genau-kennt.html)

Codehunter 14. Sep 2011 09:57

Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Hallo!

Ich möchte ein Programm schreiben, das für das Update einer Anwendung zuständig ist. Das Update-Tool muss natürlich nachschauen, ob noch irgendwelche Instanzen der zu updatenden Anwendung laufen und eine Liste dieser Instanzen anzeigen. Eben so wie man das von Firefox-Installationen etc. kennt die beim Update warten bis das letzte Browserfenster geschlossen ist.

Das Problem dabei: FindWindow findet ja nur Fenster, wenn man den exakten Title-String angibt. Der ändert sich aber, je nach dem welche Datei in der Anwendung gerade geöffnet ist. Der Classname ist auch nicht eindeutig, der verändert sich da beim Anwendungsstart eine GUID als Classname gesetzt wird.

Wie kann man eine Anwendung noch ausfindig machen?

Grüße
Cody

chaosben 14. Sep 2011 10:10

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Ich würde über die Process-Liste und dann über den Pfad zur .exe gehen. Letzteren musst du ja sowieso kennen, um ein Update zu machen.

DeddyH 14. Sep 2011 10:14

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Könnte man nicht die Taskliste durchgehen (z.B. mit CreateToolhelp32Snapshot), nach dem entsprechenden Prozess suchen und bei Fund die ProcessID ermitteln? Anschließend dann mit EnumWindows alle Fenster durchlaufen und mit GetWindowThreadProcessID vergleichen. Allerdings bin ich mir gerade nicht sicher, ob die erforderlichen Angaben auch wirklich im TProcessEntry32-Record stehen, denke aber schon.

Singlepin 14. Sep 2011 10:33

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Meine Programme erstelle ich in der Regel so, daß sie nicht doppelt gestartet werden können.
Dazu erzeuge ich ein Memory-Mapped File in dem ich gleich noch das Handle der Application ablege.
Dein Setup-Programm braucht dann nur zu prüfen ob das Memory.Mapped File existiert
und könnte über das Handle die Anwendung z.b. automatisch schließen oder in den Fordergrund holen usw.

Codehunter 14. Sep 2011 11:16

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Danke Jungs, manchmal reicht einem ein Stichwort damit man weiterkommt :-) In dem Fall CreateToolhelp32Snapshot (übrigens wirklich kein treffender Name für diese Routine). Hier eine Delphi-Übersetzung des Sample-Codes der im PSDK hinterlegt ist:
Delphi-Quellcode:
uses
  tlhelp32;

procedure TForm1.Button1Click(Sender: TObject);
var
  hProcessSnap: THandle;
  hProcess: THandle;
  pe32: PROCESSENTRY32;
  dwPriorityClass: DWord;
begin
  Memo1.Lines.BeginUpdate;
  try
    hProcessSnap:= CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
    if hProcessSnap = INVALID_HANDLE_VALUE then begin
      Memo1.Lines.Add(#13#10#13#10'CreateToolhelp32Snapshot (of processes)');
      Exit;
    end;
    pe32.dwSize:= SizeOf(PROCESSENTRY32);

    if not Process32First(hProcessSnap, pe32) then begin
      Memo1.Lines.Add(#13#10#13#10'Error: Process32First');
      CloseHandle(hProcessSnap);
      Exit;
    end;

    repeat
      Memo1.Lines.Add(#13#10'=====================================================');
      Memo1.Lines.Add(Format('PROCESS NAME: %s', [pe32.szExeFile]));
      Memo1.Lines.Add('-----------------------------------------------------');

      dwPriorityClass:= 0;
      hProcess:= OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
      if hProcess = NULL then begin
        Memo1.Lines.Add(#13#10'Error: OpenProcess');
      end else begin
        dwPriorityClass:= GetPriorityClass(hProcess);
        if dwPriorityClass = 0 then begin
          Memo1.Lines.Add(#13#10'Error: GetPriorityClass');
          CloseHandle(hProcess);
        end;
      end;

      Memo1.Lines.Add(Format(' Process ID       = 0x%08X', [pe32.th32ProcessID]));
      Memo1.Lines.Add(Format(' Thread count     = %d',  [pe32.cntThreads]));
      Memo1.Lines.Add(Format(' Parent process ID = 0x%08X', [pe32.th32ParentProcessID]));
      Memo1.Lines.Add(Format(' Priority base    = %d', [pe32.pcPriClassBase]));
      Memo1.Lines.Add(Format(' Priority base    = %d', [pe32.pcPriClassBase]));
      Memo1.Lines.Add(Format(' Executable       = %s', [pe32.szExeFile]));

      if dwPriorityClass > 0 then begin
        Memo1.Lines.Add(Format(' Priority class   = %d', [dwPriorityClass]));
      end;

    until not Process32Next(hProcessSnap, pe32);

    CloseHandle(hProcessSnap);
  finally
    Memo1.Lines.EndUpdate;
  end;
end;
Ist erstmal nur eine Dummy-Routine mit Ausgabe in ein Memo. Ich werde mir das jetzt so adaptieren dass ich über den Exe-Namen gehe, an die Process-ID eine Message schicke und mir von dort mit einer Message antworten lasse. So finde ich meine ganzen Ziel-Prozesse.

ASM 14. Sep 2011 11:27

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Es geht auch so:

Code:
function FindWindowEx(partialTitle: string): HWND;
var
  hWndTemp: hWnd;
  iLenText: Integer;
  cTitletemp: array [0..254] of Char;
  sTitleTemp: string;
begin
  hWndTemp := FindWindow(nil, nil);
  while hWndTemp <> 0 do begin
    iLenText := GetWindowText(hWndTemp, cTitletemp, 255);
    sTitleTemp := cTitletemp;
    sTitleTemp := UpperCase(copy( sTitleTemp, 1, iLenText));
    partialTitle := UpperCase(partialTitle);
    if pos( partialTitle, sTitleTemp ) <> 0 then Break;
    hWndTemp := GetWindow(hWndTemp, GW_HWNDNEXT);
  end;
  result := hWndTemp;
end;
Beispiel:
Winword ist geöffnet mit irgendeinem Dokument. Das Fenster hat z.B. gerade den Titel "Dokument3 - Microsoft Word".
Das Word-Fenster trägt neben dem Titel des gerade geöffneten Dokuments immer auch den Textzusatz "Microsoft Word".

Du kannst also einfach mit
Code:
if FindWindowEx('Microsoft Word')<>0 then showmessage('Hi, Winword. Nice to meet you.')
else Showmessage('nix los hier');
feststellen, ob gerade irgendeine Instanz von Winword aktiv ist.

Entsprechend funktioniert das auch mit dem Adobe Reader, Mozilla Firefox, usw.

SirThornberry 14. Sep 2011 12:12

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Dann doch lieber nach dem Dateinamen suchen. Ansonsten schließt man im schlimmsten Fall ein Fenster das einen ähnlichen Title hat.

ASM 14. Sep 2011 13:16

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Zitat:

Zitat von SirThornberry (Beitrag 1124275)
Dann doch lieber nach dem Dateinamen suchen. Ansonsten schließt man im schlimmsten Fall ein Fenster das einen ähnlichen Title hat.


Die konkrete Frage war aber nun vielmehr folgende:
Zitat:

Das Update-Tool muss natürlich nachschauen, ob noch irgendwelche Instanzen der zu updatenden Anwendung laufen und eine Liste dieser Instanzen anzeigen.
Ich kann in dieser Frage von Codehunter keine Absicht erkennen, das Fenster per brute-force von außerhalb schließen zu wollen.

Delphi-Laie 14. Sep 2011 13:19

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Zitat:

Zitat von DeddyH (Beitrag 1124239)
Könnte man nicht die Taskliste durchgehen (z.B. mit CreateToolhelp32Snapshot), nach dem entsprechenden Prozess suchen und bei Fund die ProcessID ermitteln? Anschließend dann mit EnumWindows alle Fenster durchlaufen und mit GetWindowThreadProcessID vergleichen. Allerdings bin ich mir gerade nicht sicher, ob die erforderlichen Angaben auch wirklich im TProcessEntry32-Record stehen, denke aber schon.

Ja, stehen sie (sofern ich Dich recht verstand), wobei man GetWindowThreadProcessID nach meiner Erinnerung allerdings extra aufrufen muß. In meinem Programm mit dem einfallslosen Namen "Prozesse" (auch hier im Forum zu finden) wird auch demonstriert, wie man die Fenster selektiv eines Prozesses finden bzw. sich auflisten lassen kann (ein einfacher Filter tut es, alternativ könnte man mit Enmthreadwindows alle Fenster aller Threads eines Prozesses erhalten). Ansonsten empfehle ich Luckies Programm "WinInfo", von dem ich einige Anregungen übernahm.

himitsu 14. Sep 2011 13:48

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Wobei es auch sein kann, daß es andere Programme mit dem selben Fensternamen gibt,
oder das gefundene Fenster gehört zur gleichen EXE, aber in einem anderen Verzeichns.

Der einzige Weg, um ein bestimmtes Programm zu finden, geht also nur über den EXE Namen, inkl. Pfad.



Wenn man, bzw. das Setup, auf etwas zugreifen muß, welches gemeinsam genutzt wird (z.B. eine Datenbank),
dann muß man alle Instanzen finden, welche irgendwo laufen (unabhängig vom Pfad).

Für Letzteres könnte man bestimmt mit 'nem Mutex, oder mit anderen globalen möglichst eindeutigen Markern arbeiten.

CCRDude 14. Sep 2011 13:50

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Dateinamen können natürlich auch von anderen Anwendungen verwendet werden, und Pfade sind öft vom Benutzer wählbar.

Mutexe und Atome sind noch ein Stichwort.

Auch ein Tipp: mit Terminal Services siehst Du evtl. Prozesse, zu Denen Du keine Windows findest.

ASM 14. Sep 2011 14:46

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Zitat:

Wobei es auch sein kann, daß es andere Programme mit dem selben Fensternamen gibt,
oder das gefundene Fenster gehört zur gleichen EXE, aber in einem anderen Verzeichns.

Der einzige Weg, um ein bestimmtes Programm zu finden, geht also nur über den EXE Namen, inkl. Pfad.
Zitat:

Dateinamen können natürlich auch von anderen Anwendungen verwendet werden, und Pfade sind öft vom Benutzer wählbar.

Ganz recht.
Und dazu schaut man eben über die vorgeschlagene Funktion FindWindowEx() zunächst nach, ob überhaupt ein Fenster mit dem partiellen Titel der zu erfragenden Applikation existiert.
Falls nämlich nicht, hat sich die Sache bereits erledigt.
Falls ja, dann kann man sich entscheiden:
(a) die Zuordnung des Fensters ist aus dem Kontext unzweifelhaft eindeutig, womit das ohne weitere Abfragen geklärt wäre.
(b) man muss/möchte auf Nummer Sicher gehen und etwaige Zweifel an der Zuordnung des Fensters zur erfragten Applikation beseitigen: man hat ja mit dieser Funktion FindWindowEx() gleichzeitig das zu eben diesem Fenster zugehörige Handle und holt sich nun mit Hilfe dieses Handles vermittels des Prozesses den Filenamen inkl. kompletten Pfad der Applikation, zu dem das Fenster gehört. Womit das dann - ohne "Wenn" und "Aber" - definitiv und ggf. auch für mehrere Fenster mehrerer Instanzen, geklärt wäre.
Code:
function GetProcessNameFromWnd(Wnd: HWND): string;
var
  List: TStringList;
  PID: DWORD;
  I: Integer;
begin
  Result := '';
  if IsWindow(Wnd) then
  begin
    PID := INVALID_HANDLE_VALUE;
    Windows.GetWindowThreadProcessId(Wnd, @PID);
    List := TStringList.Create;
    try
      if RunningProcessesList(List, True) then
      begin
        I := List.IndexOfObject(Pointer(PID));
        if I > -1 then
          Result := List[I];
      end;
    finally
      List.Free;
    end;
  end;
end;
Im weiteren (insbes. zur noch benötigten RunningProcessesList()) siehe http://www.delphitricks.com/source-c...ow_handle.html

CCRDude 14. Sep 2011 15:21

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Zitat:

Zitat von ASM (Beitrag 1124324)
Und dazu schaut man eben über die vorgeschlagene Funktion FindWindowEx() zunächst nach, ob überhaupt ein Fenster mit dem partiellen Titel der zu erfragenden Applikation existiert.
Falls nämlich nicht, hat sich die Sache bereits erledigt.

Desewegen erwähnte ich Terminal Services. Die als Remote-Desktop-Verbindung inzwischen ja nicht mehr nur im großen Firmenumfeld vorkommen. Oder war sogar Fast User Switching darüber?

MSDN sagt zu MSDN-Library durchsuchenFindWindowEx:
Zitat:

If hwndParent is NULL, the function uses the desktop window as the parent window. The function searches among windows that are child windows of the desktop.
Als Wurzel der Suche kann man also nur einen Desktop verwenden; ist parallel eine Person zwei angemeldet und benutzt das Programm, würde FindWindowEx aber scheitern.

Deswegen mein Tipp zu Techniken, die session-übergreifend funktionieren.

ASM 14. Sep 2011 17:05

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Zitat:

Zitat von CCRDude (Beitrag 1124342)
MSDN sagt zu FindWindowEx: ...

Als Wurzel der Suche kann man also nur einen Desktop verwenden; ist parallel eine Person zwei angemeldet und benutzt das Programm, würde FindWindowEx aber scheitern.


Nur um keine Verwirrung aufkommen zu lassen: die von mir oben gepostete Funktion FindWindowEx() ist nicht die in der Unit deklarierte Funktion FindWindowEx() aus der MS-Systemlibrary user32.dll. Besser hätte ich die von mir gepostete Funktion wohl FindWindowPart() oder sonstwie nennen sollen, um diesen Unterschied klar zu machen. Letztlich stellt der neu angemerkte potentielle Remote-Effekt aber wohl das gleiche Problem für beide Funktionen dar.

Davon allerdings, dass es sich hier in der Diskussion um ein Server-Client spezifisches Problem handeln könnte, war bisher jedoch nicht erkennbar die Rede. Dadurch wird die Fragestellung jetzt unerwartet mit einer erheblich anderen Zielrichtung aufgeworfen als ursprünglich gestellt. Oder ich hatte dahingehend etwas falsch verstanden ...

himitsu 14. Sep 2011 17:53

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Versuche einfach die nötigen Dateien exklusiv und mit Schreibrechten zu öffnen.
Geht das nicht, dann "Update nicht möglich, da Programm läuft oder Zugriffsrechte fehlen".

CCRDude 14. Sep 2011 19:28

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Zitat:

Zitat von ASM (Beitrag 1124379)
Davon allerdings, dass es sich hier in der Diskussion um ein Server-Client spezifisches Problem handeln könnte, war bisher jedoch nicht erkennbar die Rede.

Deswegen ja meine Ergänzung, daß Terminal Services früher vllt. mal Client-Server-Architektur only gewesen sein mögen, heute aber dank Fast User Switching recht verbreitet angewendet sind (und deutlich seltener, aber auch noch im Home-Bereich, mit dem Remote Desktop).

MSDN lädt sich bei mir heute leider meist tot, daher kann ich nur blind die Lektüre zu den WTS*-Funktionen empfehlen :)

Sir Rufo 15. Sep 2011 01:51

AW: Wie findet man Fenster, deren Namen man nicht genau kennt?
 
Soll dieses Updateprogramm unbedingt selber geschrieben werden oder darf es auch was fertiges sein?

Dann würde ich z.B. InnoSetup vorschlagen.

Laufende Instanzen des Programms werden von InnoSetup über einen Mutex erkannt.
InnoSetup kann Dateien nachladen
InnoSetup kann Dateien kopieren, auch wenn die Datei gesperrt ist (Neustart erforderlich)

Für TerminalServices sollte ein globaler Mutex verwendet werden!
Prefix
Code:
Global/


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:53 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz