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/)
-   -   Datei mit Standard-App öffnen und auf Beendigung warten (https://www.delphipraxis.net/189890-datei-mit-standard-app-oeffnen-und-auf-beendigung-warten.html)

RaSoWa1 4. Aug 2016 13:19

Datei mit Standard-App öffnen und auf Beendigung warten
 
Hallo,

ich möchte unter Windows 10 aus einem Delphi 2010-Programm heraus Dateien mit der zugewiesenen Standard-App öffnen und warten bis diese Standard-App (z.B. die Foto-App) wieder geschlossen wird.
Eine Datei mit einer bekannten Anwendung öffnen und warten mache ich mit "CreateProcess" und "WaitForInputIdle". Das funktioniert.

Um die Datei mit der zugewiesenen Standard-App zu öffnen verwenden ich "ShellExecute". Das öffnen klappt, nur wie kann ich auf die Beendigung warten?
Gibt es andere Methoden oder Verfahrensweisen?

Die Suche hat leider nichts für mich brauchbares ergeben, zumal ich mit Englisch so meine Probleme habe.

Gruß Klaus

FBrust 4. Aug 2016 13:26

AW: Datei mit Standard-App öffnen und auf Beendigung warten
 
Hallo,

vielleicht hilft Dir "ShellExecuteAndWait" weiter, z. b. http://www.delphipraxis.net/32234-sh...-abfragen.html

Gruß
Frank

Union 4. Aug 2016 13:31

AW: Datei mit Standard-App öffnen und auf Beendigung warten
 
Du solltest die externe Anwendung mit MSDN-Library durchsuchenCreateProcess starten und das resultierende Processhandle in einer Schleife mit MSDN-Library durchsuchenWaitForSingleObject abfragen. Und Englisch lernen ;)

Bambini 4. Aug 2016 13:32

AW: Datei mit Standard-App öffnen und auf Beendigung warten
 
Zitat:

Um die Datei mit der zugewiesenen Standard-App zu öffnen verwenden ich "ShellExecute". Das öffnen klappt, nur wie kann ich auf die Beendigung warten?
Gibt es andere Methoden oder Verfahrensweisen?
Die Raize Komponenten haben dafür eine Komponente TRzLauncher.

Aber ob das für alle Dateien klappt, wage ich zu bezweifeln, da mache Anwendungen die "Datei" an andere Instanzen weitergeben und sich dann beenden.

RWarnecke 4. Aug 2016 13:51

AW: Datei mit Standard-App öffnen und auf Beendigung warten
 
Eine andere Alternative.

himitsu 4. Aug 2016 13:54

AW: Datei mit Standard-App öffnen und auf Beendigung warten
 
Erstmal schauen was die Standard-Anwendung ist. (HKEY_CLASSES_ROOT)
z.B. in der Registry die Dateiendung suchen und prüfen ob es etwas zum OPEN gibt

Wenn du "Glück" hast, dann ist da eine EXE verlinkt und wie man diese ausruft (meistens "xxxxx.exe %1")

Das kannst du dann mit ShellExecute ausführen.

Ist ein DDE-Server verlinkt, dann startest du Diesen (MSDN-Library durchsuchenFindExecutable)
und zu dem sollte dann auch geschrieben stehen mit welcher Aktion darin dann das Document übergeben wird.




PROBLEMe wird es immer geben, wenn die gewünschte Anwendung nicht direkt gestartet wird, sondern "nur" ein Launcher, denn diese starten dann die eigentlich Anwendung, übergeben die Daten und beenden sich vorzeitig.
z.B. siehe MS Office, OpenOffice oder Delphi (das nutzt aktuell einen DDE-Server in der bdsLauncher.exe der die bds.exe startet)

RaSoWa1 4. Aug 2016 14:12

AW: Datei mit Standard-App öffnen und auf Beendigung warten
 
Zitat:

Zitat von FBrust (Beitrag 1344157)
Hallo,

vielleicht hilft Dir "ShellExecuteAndWait" weiter, z. b. http://www.delphipraxis.net/32234-sh...-abfragen.html

Gruß
Frank

"ShellExecuteAndWait" kenne und nutze ich schon. Dies funktioniert leider nur mit "normalen" Anwendungen (z.B. eine .txt öffnet mit NotePad.exe und wartet auf Beendigung. Aber eine jpg-Datei wird mit der Foto-App zwar geöffnet aber das warten funktioniert nicht. Es wird leider kein .hProcess zurück gegeben.

RaSoWa1 4. Aug 2016 14:32

AW: Datei mit Standard-App öffnen und auf Beendigung warten
 
Zitat:

Zitat von Bambini (Beitrag 1344159)
Die Raize Komponenten haben dafür eine Komponente TRzLauncher.

Aber ob das für alle Dateien klappt, wage ich zu bezweifeln, da mache Anwendungen die "Datei" an andere Instanzen weitergeben und sich dann beenden.

Ich bin nur ein Hobby-Programmierer und verdiene damit kein Geld, deshalb scheiden kostenpflichtige Komponeneten aus.

Danke trotzdem für den Rat.
Gruß Klaus

Bambini 4. Aug 2016 14:40

AW: Datei mit Standard-App öffnen und auf Beendigung warten
 
Zitat:

Zitat von RaSoWa1 (Beitrag 1344166)
Ich bin nur ein Hobby-Programmierer und verdiene damit kein Geld, deshalb scheiden kostenpflichtige Komponeneten aus

Die Raize Komponenten gibt es zur zeit kostenlos zu Delphi 10.1 Berlin.
Laut Roadmap kommen die Zukünftig als Standard ins Delphi.
Ich habe noch nicht geschaut wie diese Komponente es macht, aber ich denke das läuft genauso über die hier beschriebenen API's

RaSoWa1 4. Aug 2016 14:43

AW: Datei mit Standard-App öffnen und auf Beendigung warten
 
Zitat:

Zitat von RWarnecke (Beitrag 1344160)
Eine andere Alternative.

Nee, geht leider auch nicht. Wenn ich nur den Dateinamen übergebe liefert "CreateProcess" false und es passiert leider auch nichts. Die Anwendung wird nicht einmal gestartet.

Dank.
Gruß Klaus

RaSoWa1 4. Aug 2016 14:48

AW: Datei mit Standard-App öffnen und auf Beendigung warten
 
Zitat:

Zitat von Bambini (Beitrag 1344168)
Zitat:

Zitat von RaSoWa1 (Beitrag 1344166)
Ich bin nur ein Hobby-Programmierer und verdiene damit kein Geld, deshalb scheiden kostenpflichtige Komponeneten aus

Die Raize Komponenten gibt es zur zeit kostenlos zu Delphi 10.1 Berlin.
Laut Roadmap kommen die Zukünftig als Standard ins Delphi.
Ich habe noch nicht geschaut wie diese Komponente es macht, aber ich denke das läuft genauso über die hier beschriebenen API's

Eine neue Delphi-Version kostet noch viel viel mehr. Soviel will ich nicht für mein Hobby ausgeben. Für neue Projekt werde ich wahrscheinlich Visual Studio verwenden. Ich schnuppere gerade mal. Aber wir schweifen ab.

Gruß
Klaus

hoika 4. Aug 2016 15:45

AW: Datei mit Standard-App öffnen und auf Beendigung warten
 
Hallo,
ShellExecuteEx liefert dir das Prozess-Handle (Feld hProcess) der Standard-App.
Danach weiter mit deinem WaitForXXX


Heiko

RaSoWa1 4. Aug 2016 19:15

AW: Datei mit Standard-App öffnen und auf Beendigung warten
 
Zitat:

Zitat von himitsu (Beitrag 1344161)
Erstmal schauen was die Standard-Anwendung ist. (HKEY_CLASSES_ROOT)
z.B. in der Registry die Dateiendung suchen und prüfen ob es etwas zum OPEN gibt

Wenn du "Glück" hast, dann ist da eine EXE verlinkt und wie man diese ausruft (meistens "xxxxx.exe %1")

Das kannst du dann mit ShellExecute ausführen.

Dies funktioniert nur bei einer normalen EXE. Da liefert mir ShellExecute auch das entsprechende .hProcess für WaitForSingleObject.
Bei einer WindowsApp (z.B.: Foto-App) funktioniert dies leider nicht. Auch in der Registry finde ich nichts, wie ich die App erkennen und starten kann.

Zitat:

Ist ein DDE-Server verlinkt, dann startest du Diesen (MSDN-Library durchsuchenFindExecutable)
und zu dem sollte dann auch geschrieben stehen mit welcher Aktion darin dann das Document übergeben wird.
FindExecutable findet leider auch nur EXE-Dateien, keine Apps.

Zitat:

PROBLEMe wird es immer geben, wenn die gewünschte Anwendung nicht direkt gestartet wird, sondern "nur" ein Launcher, denn diese starten dann die eigentlich Anwendung, übergeben die Daten und beenden sich vorzeitig.
z.B. siehe MS Office, OpenOffice oder Delphi (das nutzt aktuell einen DDE-Server in der bdsLauncher.exe der die bds.exe startet)
Da hast du natürlich Recht.
Mir ist deshalb folgende Idee gekommen, für die ich gerade ein Testprogramm schreibe:
Ich schreibe unmittelbar vor und nach dem Aufruf mit ShellExecute alle ProzessID in jeweils eine Liste. Die Differenz beider Listen müsste mit hoher wahrscheinlichkeit die ProzessID der gestarteten Anwendung sein. Ich kann dann darauf reagieren. Ich kann dies aber erst am Wochenende weiter testen und prüfen ob und wie mir Windows dazwischen funkt.
Ich melde mich dann wieder.

Danke,
Gruß Klaus.

RaSoWa1 7. Aug 2016 11:29

AW: Datei mit Standard-App öffnen und auf Beendigung warten
 
Hallo,
ich viel probiert und getestet und verwende jetzt folgenden Code:
Delphi-Quellcode:
function ExecAndWait2(Filename, Params: String; WindowState: Word = SW_SHOWNORMAL): Integer;
const
  cError    = -1;
  cWait     = 0;
  cNoWait   = -2;
var
  ShExecInfoW : SHELLEXECUTEINFOW;
  ExitInfo   : PExitThreadDebugInfo;
  wndLstAll,
  wndLst     : TStringList;
  i, a       : Integer;
  procedure AllProcessToLst(Lst: TStringList);
    function EnumWindowsProcA(Wnd: HWND; LParam: TStringList): BOOL; stdcall;
    begin
      result := True;
      if IsWindowVisible(Wnd) then
      begin
        LParam.Add(IntToStr(Wnd));
      end;
    end;
  begin
    EnumWindows(@EnumWindowsProcA, integer(Lst));
  end;
begin
  Result := cError;
  if (Filename <> '') and FileExists(FileName) then
  begin
    ShExecInfoW.Wnd         := GetForegroundWindow;
    ShExecInfoW.cbSize      := SizeOf(SHELLEXECUTEINFOW);
    ShExecInfoW.fMask       := SEE_MASK_NOCLOSEPROCESS;
    ShExecInfoW.lpVerb      := 'open';
    ShExecInfoW.lpFile      := PWideChar(WideString(Filename));
    ShExecInfoW.lpParameters := PWideChar(WideString(Params));
    ShExecInfoW.lpDirectory := PWideChar(WideString(ExtractFileDir(Filename)));
    ShExecInfoW.nShow       := WindowState;
    wndLstAll               := TStringList.Create;
    try
      AllProcessToLst(wndLstAll);  
      if ShellExecuteExW(@ShExecInfoW) then
      begin
        result := cNoWait;
        try
          if ShExecInfoW.hProcess <> 0 then
          begin
            WaitForInputIdle(ShExecInfoW.hProcess, INFINITE);
            New(ExitInfo);
            FillChar(ExitInfo.dwExitCode, SizeOf(ExitInfo.dwExitCode), 0);
            repeat
              Application.ProcessMessages;
              sleep(500);
              GetExitCodeProcess(ShExecInfoW.hProcess, ExitInfo.dwExitCode);
            until not(ExitInfo.dwExitCode = STILL_ACTIVE);
            Dispose(ExitInfo);
            Result := cWait;
          end
          else
          // neues Handle ermitteln und auf dessen Beendigung warten:
          begin
            wndLst := TStringList.Create;
            try
              sleep(500);          // warten bis App geöffnet ist
              AllProcessToLst(wndLst);
              for i := wndLst.Count - 1  downto 0 do
                if wndLstAll.IndexOf(wndLst[i]) > -1 then
                  wndLst.Delete(i);
              if wndLst.Count > 0 then
               // auf Beendigung warten:
              begin
                repeat
                  Application.ProcessMessages;
                  sleep(500);
                  wndLstAll.Clear;
                  AllProcessToLst(wndLstAll);
                  a := 0;
                  for i := 0 to wndLst.Count - 1 do
                    if wndLstAll.IndexOf(wndLst[i]) > -1 then
                      inc(a);
                until a < wndLst.Count;
                result := cWait;
              end;
            finally
              wndLst.Free
            end;
          end;
        finally
           CloseHandle(ShExecInfoW.hProcess);
        end;
      end;
    finally
      wndLstAll.Free;
    end;
  end;
end;
Die bisherigen Tests auf meinem Rechner liefen alle erfolgreich. Die Funktion wartet immer (auch bei Windows10-Apps) auf deren Beendigung.

Danke an alle Helfer.
Ein schönes Wochenende.
Gruß Klaus.


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

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