Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi ShellExcecute/FindWindow/MoveWindow (https://www.delphipraxis.net/217308-shellexcecute-findwindow-movewindow.html)

geldis 8. Jun 2025 07:56

ShellExcecute/FindWindow/MoveWindow
 
Ich starte mit folgender Prozedur (hier aus dem Forum) ein Programm (C) das eine Anwendung mit Fenster
öffnet:

Delphi-Quellcode:
procedure RunAndWaitShell(Executable, Parameter: STRING; ShowParameter: INTEGER);
var
  Info: TShellExecuteInfo;
  pInfo: PShellExecuteInfo;
  exitCode: DWord;
  HANDLE : THANDLE;
begin
  {Pointer to Info}
  pInfo := @Info;
  {Fill info}
  with Info do
  begin
    cbSize := SizeOf(Info);
    fMask := SEE_MASK_NOCLOSEPROCESS;
    wnd  := application.Handle;
    lpVerb := NIL;
    lpFile := PChar(Executable); // emperormapper.exe
    {Parametros al ejecutable}
    {Executable parameters}
    lpParameters := PChar(Parameter + #0);
    lpDirectory := NIL;
    nShow      := ShowParameter;
    hInstApp   := 0;
  end;
  {Execute}
  ShellExecuteEx(pInfo);
  {Wait to finish}
  repeat
    exitCode := WaitForSingleObject(Info.hProcess, 500);
    Application.ProcessMessages;
  until (exitCode <> WAIT_TIMEOUT);
end;
Die Anwendung /das Fenster taucht planmäßig im Task auf.


Dann möchte ich das Fenster der Anwendung verschieben:

Delphi-Quellcode:
   HANDLE := FindWindow(Nil,'EmperorMapper');
   MoveWindow(HANDLE, 600, 600, 400, 400, True);
Geht nicht. Fehlt was?

geldis

jaenicke 8. Jun 2025 08:38

AW: ShellExcecute/FindWindow/MoveWindow
 
Wird denn das Handle gefunden? Hast du den Wert nach dem Aufruf von FindWindow geprüft?

gubbe 8. Jun 2025 09:38

AW: ShellExcecute/FindWindow/MoveWindow
 
Du rufst das Programm auf und wartest auf Beendigung. An welcher Stelle willst du jetzt das Fenster verschieben?

Es könnte schon funktionieren, aber Du musst warten, bis das Programm das Fenster geöffnet hat. Schau Dir mal die API-Funktion WaitForInputIdle an.

Wenn das Programm ein Fenster anzeigt und sich nicht direkt wieder beendet, würde ich auch möglichst nicht auf Beendigung des Programms warten. Das ProcessMessages, um Dein Programm nicht einfrieren zu lassen, ist eher schlechter Stil.

geldis 8. Jun 2025 10:02

AW: ShellExcecute/FindWindow/MoveWindow
 
Tja,

das Programm startet, hat den Focus, der Anwender muss eine Datei laden, die wird bearbeitet, dann muss der Anwender sie woandes speichern.
Ergo benutze ich das Fenster.

Wenn ich jetzt prüfe ob das Fenster bereits vorhanden ist via WaitforInputIdle muss ich das Handle der Anwendung haben. Oder? Gut:



Delphi-Quellcode:
function processExists(exeFileName: string): Boolean;
var
  ContinueLoop: BOOL;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
begin
  FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
  ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
  Result := False;
  while Integer(ContinueLoop) <> 0 do
  begin
    if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
      UpperCase(ExeFileName)) or (UpperCase(FProcessEntry32.szExeFile) =
      UpperCase(ExeFileName))) then
    begin
      Result := True;
    end;
    ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
  end;
  CloseHandle(FSnapshotHandle);
end;
Delphi-Quellcode:
  RunAndWaitShell(XMLPAth + 'emperormapper.exe','',1);
  If ProcessExists('emperormapper.exe') then
  begin
   WaitforInputIdle(FSnapshotHandle,10000);
   HANDLE := FindWindow(Nil,'EmperorMapper');
   MoveWindow(HANDLE, 600, 600, 400, 400, True);
  end;

Ich werde jetzt nicht ganz schlau aus dem ProcessExists. Wird da geprüft ob der Process existiert bis er existiert?

Jedenfalls geht es so auch nicht.


geldis

gubbe 8. Jun 2025 10:13

AW: ShellExcecute/FindWindow/MoveWindow
 
Natürlich geht das nicht. Wenn RunAndWaitShell auf Beendigung des Programms wartet, wird Dein weiterer Code erst danach ausgeführt.
Du musst die Funktion RunAndWaitShell ändern und dort nicht auf Beendigung, sondern auf "Input Idle" warten. Das Process-Handle hast Du dort auch bereits und musst es nicht später umständlich mit ProcessExists wieder ermitteln.

Aber mal ganz von vorne: Hast Du probiert mit einem kleinen Testprogramm das Fenster der zuvor manuell gestarteten "EmpororMapper.exe" zu verschieben? Wenn das nicht geht, kannst Du Dir den Rest sparen.

jaenicke 8. Jun 2025 12:32

AW: ShellExcecute/FindWindow/MoveWindow
 
Zitat:

Zitat von geldis (Beitrag 1549293)
Ich werde jetzt nicht ganz schlau aus dem ProcessExists. Wird da geprüft ob der Process existiert bis er existiert?

Ich hatte gehofft, dass du auf den Debugger verwendest. Wenn du einen Haltepunkt auf die Zeile mit dem FindWindow setzt, stellst du einerseits fest, wann dein Programm dort ankommt. Andererseits kannst du dann mit F8 einen Schritt weiter debuggen und den Rückgabewert prüfen, indem du die Maus über deine Variable Handle hältst.

Aber vermutlich wirst du dort gar nicht zur richtigen Zeit ankommen. Das wurde ja schon gesagt. Um das zu prüfen, ist der Debugger genau wichtig.

geldis 8. Jun 2025 15:33

AW: ShellExcecute/FindWindow/MoveWindow
 
Also : ich mache das jetzt so :

Delphi-Quellcode:
// der Anwender ruft das Programm auf
ShellExecute(self.handle, 'open',Pchar(XMLPAth + 'emperormapper.exe'), '', nil, SW_SHOW);
  // .. delphi wartet ein bisschen bis der Prozess soweit ist das das Fenster steht ..
  Sleep(25);
  // jetzt funzt der Handle Zugriff und damit auch das verschieben
  If ProcessExists('emperormapper.exe') then
    begin
     HANDLE := FindWindow(Nil,'EmperorMapper');
     MoveWindow(HANDLE, Left, Top, 461, 430, True);
    end;
    // das Delphiprogramm wartet bis der Anwender die emperormapper.exe beendet
    While ProcessExists('emperormapper.exe') do
    begin
      Application.ProcessMessages;
    end;
// es geht weiter
Das funktioniert problemlos.

Aber Elegant ist das nicht. Gibt es da irgendwelche ernsthaften Probleme?

geldis

jaenicke 8. Jun 2025 17:37

AW: ShellExcecute/FindWindow/MoveWindow
 
Zitat:

Zitat von geldis (Beitrag 1549299)
Aber Elegant ist das nicht.

Warum machst du es denn so?

Im ersten Beitrag hast du doch selbst Code gezeigt, mit dem du deinen externen Prozess startest, dessen Handle bekommst (Info.hProcess) und dann auf dessen Ende wartest.

Was hindert dich denn daran, den Prozess zu starten, dir das Handle zu merken, deine Aktionen durchzuführen und dann genauso auf das Ende des Prozesses zu warten?

geldis 9. Jun 2025 07:40

AW: ShellExcecute/FindWindow/MoveWindow
 
Moin.

Seien wir ehrlich : Das funktioniert problemlos und ich habe es einigermaßen verstanden.

Die Abteilung ShellExecute und vor allem die diversen Informationen wie
>> FProcessEntry32, Process32First(FSnapshotHandle, FProcessEntry32), FProcessEntry32.szExeFile, WaitForInputIdle ......

und das ganze andere Zeug - das steht jetzt auf der ToDo Liste. Ich muss und möchte das lernen und verstehen.

Bis dahin lasse ich das jetzt so. Falls niemand ensthafte Einwände hat.

Ich danke jedenfalls allen hier Beteiligten für die Unterstützung und vor allem für die Denkanstösse und Hinweise wo man denn überhaupt mal nachschaut.

geldis

jaenicke 9. Jun 2025 10:08

AW: ShellExcecute/FindWindow/MoveWindow
 
Hier mal ein Beispiel angelehnt an den ursprünglichen Code:
Delphi-Quellcode:
procedure RunAndWaitShell(const AExecutable, AParameter: string; AShowParameter: Integer; AOnAfterStart: TProc);
var
  ExecInfo: TShellExecuteInfo;
  WaitResult: DWord;
begin
  FillChar(ExecInfo, SizeOf(ExecInfo), 0);

  ExecInfo.cbSize := SizeOf(ExecInfo);
  ExecInfo.fMask := SEE_MASK_NOCLOSEPROCESS;
  ExecInfo.Wnd := Application.Handle;
  ExecInfo.lpVerb := 'open';
  ExecInfo.lpFile := PChar(AExecutable);
  ExecInfo.lpParameters := PChar(AParameter);
  ExecInfo.lpDirectory := nil;
  ExecInfo.nShow := AShowParameter;
  ExecInfo.hInstApp := 0;

  if ShellExecuteEx(@ExecInfo) then
  begin
    WaitResult := WaitForInputIdle(ExecInfo.hProcess, 10000);
    if WaitResult = WAIT_FAILED then
      ShowMessage('WaitForInputIdle ist fehlgeschlagen!');

    if Assigned(AOnAfterStart) then
      AOnAfterStart;

    repeat
      WaitResult := WaitForSingleObject(ExecInfo.hProcess, 500);
      Application.ProcessMessages;
    until WaitResult <> WAIT_TIMEOUT;

    CloseHandle(ExecInfo.hProcess);
  end
  else
    ShowMessage('Prozess konnte nicht gestartet werden!');
end;

// Aufruf:
  RunAndWaitShell('emperormapper.exe', '', SW_SHOW,
    procedure
    var
      WinHandle: THandle;
    begin
      WinHandle := FindWindow(nil, 'EmperorMapper');
      MoveWindow(WinHandle, 600, 600, 400, 400, True);
    end
  );
Zur Erklärung:
Der Prozess wird gestartet. Dann wird gewartet, bis der Prozess alle anstehenden Eingaben abgeschlossen hat und auf Eingaben wartet. Dann wird eine anonyme Methode aufgerufen, in der du deine Aktionen durchführen kannst. Und danach wird gewartet, bis der Prozess beendet ist.


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:37 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