Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi anderes Programm beenden (https://www.delphipraxis.net/39994-anderes-programm-beenden.html)

Gambit 9. Feb 2005 22:13


anderes Programm beenden
 
Hallo,
ich möchte eine .exe Datei bei Programmstart meines Programms starten und bei Beenden meines Programms auch wieder mit beenden.
Ein anderes Programm aus meinem Programm heraus starten krieg ich ja noch hin, aber wie beende ich es denn wieder?

Gruß

Gambit

Sprint 9. Feb 2005 22:59

Re: anderes Programm beenden
 
Du kannst z.B. an das Fensterhandle eine WM_CLOSE Nachricht schicken. Besser wäre eine SC_CLOSE Nachricht. Oder eine PostQuitMessage an den Thread. Und zu allerletzt kannst du das Programm brutal mit TerminateProcess beenden.

Gambit 10. Feb 2005 09:52

Re: anderes Programm beenden
 
Das Teil läuft aber nur im Tray ohne ein Fenster. Ein Fensterhandle habe ich da wohl nicht, oder?

Sprint 10. Feb 2005 13:44

Re: anderes Programm beenden
 
Zitat:

Zitat von Gambit
Ein Fensterhandle habe ich da wohl nicht, oder?

Da wird es auch ein Fensterhandle geben.

Gambit 11. Feb 2005 18:48

Re: anderes Programm beenden
 
Also folgendermaßen funzt es jetzt:

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
begin
  FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
  StartupInfo.cb := Sizeof(TStartupInfo);
  if CreateProcess(nil,               // Anwendungsname
                   PChar(edit1.text), // Parameter
                   nil,               // Security
                   nil,               // Security
                   False,
                   NORMAL_PRIORITY_CLASS, // Priorität
                   nil,                  // Environment
                   PChar(edit2.text),    // Verzeichnis
                   StartupInfo,
                   ProcessInfo) then begin
  end else Showmessage('Fehler!');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  TerminateProcess(ProcessInfo.hProcess,0);
  Showmessage('Prozess beendet!');
end;
Aber wie müsste denn meine Anweisung in Button2Click ausehen, wenn ich den Prozess "kontrolliert" beenden möchte?

Gruß

Gambit

Gambit 11. Feb 2005 20:26

Re: anderes Programm beenden
 
@Sprint:

Habe mal versucht dein Beispiel, welches du hier im Forum mal gepostet hattest auf meines umzusetzten, funzt damit aber nicht, ich muss wohl auf terminateProcess zurückgreifen und die Keule nehmen...

Hier noch Mal dein Code:

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);

  function EnumWindowsProc(hWnd: HWND; lParam: LPARAM): BOOL; stdcall;
  begin
    TList(lParam).Add(Pointer(hWnd));
    Result := True;
  end;

var
  SI: TStartupInfo;
  PI: TProcessInformation;
  List: TList;
  ProcessId: DWORD;
  AppHWnd: HWND;
  I: Integer;
begin

  AppHWnd := 0;
  FillChar(SI, SizeOf(TStartupInfo), 0);
  SI.cb := SizeOf(TStartupInfo);
  SI.dwFlags := STARTF_USESHOWWINDOW;
  SI.wShowWindow := SW_SHOW;
  if CreateProcess(nil, 'NOTEPAD.EXE', nil, nil, False, 0, nil, nil, SI, PI) then
  begin
    WaitForInputIdle(PI.hProcess, INFINITE);
    CloseHandle(PI.hProcess);
    CloseHandle(PI.hThread);
    List := TList.Create;
    try
      if EnumWindows(@EnumWindowsProc, Longint(List)) then
      begin
        for I := 0 to List.Count - 1 do
          if GetWindowThreadProcessId(HWND(List.Items[I]), @ProcessId) <> 0 then
            if ProcessId = PI.dwProcessId then
            begin
              AppHWnd := HWND(List.Items[I]);
              Break;
            end;
        if IsWindow(AppHWnd) then
        begin
          ShowMessage('Fensterhandle ist $' + IntToHex(AppHWnd, 8));
          SendMessage(AppHWnd, WM_SYSCOMMAND, SC_CLOSE, 0);
        end;
      end;
    finally
      List.Free;
    end;
  end;

end;
Gruß

Gambit

Sprint 11. Feb 2005 20:28

Re: anderes Programm beenden
 
Zitat:

Zitat von Gambit
Aber wie müsste denn meine Anweisung in Button2Click ausehen, wenn ich den Prozess "kontrolliert" beenden möchte?

Für ein sauberes beenden, muss man paar Zeilen mehr schreiben.
Delphi-Quellcode:
var
  AppPID: DWORD;
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  SI: TStartupInfo;
  PI: TProcessInformation;
begin

  FillChar(SI, SizeOf(TStartupInfo), 0);
  SI.cb := SizeOf(TStartupInfo);
  if CreateProcess(nil, 'NOTEPAD.EXE', nil, nil, False, 0, nil, nil, SI, PI) then
  begin
    AppPID := PI.dwProcessId;
    CloseHandle(PI.hProcess);
    CloseHandle(PI.hThread);
  end;

end;
Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);

  function EnumWindowsProc(hWnd: HWND; lParam: LPARAM): BOOL; stdcall;
  begin
    TList(lParam).Add(Pointer(hWnd));
    Result := True;
  end;

  procedure Wait(MilliSeconds: DWORD);
  var
    Stop: DWORD;
  begin
    Stop := GetTickCount + MilliSeconds;
    while Stop > GetTickCount do
    begin
      Sleep(100);
      Application.ProcessMessages;
    end;
  end;

var
  List: TList;
  I: Integer;
  ProcessId: DWORD;
  ThreadId: DWORD;
  ProcessHandle: THandle;
  ExitCode: DWORD;
begin

  if AppPID <> 0 then
  begin
    List := TList.Create;
    try
      EnumWindows(@EnumWindowsProc, LPARAM(List));
      for I := 0 to List.Count - 1 do
      begin
        ThreadId := GetWindowThreadProcessId(HWND(List.Items[I]), ProcessId);
        if ProcessId = AppPID then
        begin
          SendMessageTimeout(HWND(List.Items[I]), WM_SYSCOMMAND, SC_CLOSE, 0, SMTO_ABORTIFHUNG, 500, DWORD(nil^));
          Wait(3000);
          if IsWindow(HWND(List.Items[I])) then
          begin
            PostThreadMessage(ThreadId, WM_QUIT, 0, 0);
            Wait(3000);
            ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False, ProcessId);
            if ProcessHandle <> 0 then
            begin
              GetExitCodeProcess(ProcessHandle, ExitCode);
              TerminateProcess(ProcessHandle, ExitCode);
              CloseHandle(ProcessHandle);
            end;
          end;
          Break;
        end;
      end;
    finally
      List.Free;
    end;
  end;

end;
Ich musste da mal ein Wait einbauen. Normalerweise würde ich Sleep nehmen. Dann läuft der Code aber auch in einem eigenen Thread. So ähnlich sieht die Funktion in meinem nachprogrammierten Task-Manager aus, um Anwendungen zu schließen.

Gambit 11. Feb 2005 20:41

Re: anderes Programm beenden
 
OK, Danke! Dann versuch ichs mal damit...

Gruß

Gambit

Gambit 11. Feb 2005 23:03

Re: anderes Programm beenden
 
Ja, das funzt dann so auch. Einziger Schönheitsfehler - und das war auch bei meiner Haudraufundschluss-Version so - ist, dass das Icon des zu beendenden Programms nicht aus dem Tray verschwindet...erst wenn mann mit der Maus drüber fährt...

Gruß

Gambit

Luckie 11. Feb 2005 23:17

Re: anderes Programm beenden
 
Das wirst du nicht ändern könne, weil das Fenster kein WM_CLOSE oder WM_DESTROY mehr bekommt, in denen er wohl das Icon aus der TNA entfernt.

Sprint 11. Feb 2005 23:23

Re: anderes Programm beenden
 
Ist das immer das selbe Programm das zu beenden willst? Gibt es ein PopupMenu mit einem "Beenden" Menü`? Dann könnte man auch eine Tastenkombination schicken.

Gambit 11. Feb 2005 23:29

Re: anderes Programm beenden
 
Ja, es ist immer das selbe Programm. Ist ein SSL-Wrapper, den ich einsetzte um verschlüsselt an einen Datenbankserver zu senden. Nach Aufruf erscheint das Teil auch nur im Tray. Es hat nur ein Kontextmenu zum Beenden...ich wüßte nicht, was für eine Tastenkombination ich senden sollte, das Kontextmenu hat drei Menupunkte wobei der unterste "close" ist...korrektur, der Menupunkt ist "exit"...

Sprint 12. Feb 2005 08:33

Re: anderes Programm beenden
 
Um welches Programm handelt es sich? Stunnel?

Gambit 12. Feb 2005 09:27

Re: anderes Programm beenden
 
ganz genau! Ich habe auch den Source vorliegen aber mit C kenn ich mich nicht aus und ich habe auch keinen Compiler. Habe aber jetzt rausgefunden, dass man das Programm beenden kann über das PopUpMenu und dann mit der Taste "x" der Tastatur...kann man da was machen?

Gruß

Gambit

Sprint 12. Feb 2005 14:05

Re: anderes Programm beenden
 
Das schöne an solchen C Programm ist, das oft die Menus als Resource gespeichert sind. Und siehe da, das ExitMenu hat die ID 32.

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
const
  ID_EXIT = 32;
var
  AppHWnd: HWND;
begin

  AppHWnd := FindWindow('stunnel 4.07 on Win32', 'stunnel 4.07 on Win32');
  if IsWindow(AppHWnd) then
    SendMessage(AppHWnd, WM_COMMAND, MakeWParam(ID_EXIT, 0), 0);

end;
FindWindow musst du eventuell noch anpassen. Ich weiß ja nicht, welche Version du benutzt.

Gambit 12. Feb 2005 14:22

Re: anderes Programm beenden
 
Genial, funzt perfekt! Danke!!
Warum ist FindWindow eigentlich nicht in der Delphi Hilfe zu finden? Bei mir zumindest nicht? Was du da an Parametern übergibst ist doch der Titel des Fensters. Aber dank diesen Forums konnte ich es dann finden. Damit müsste es doch auch möglich sein, einem bestimmten Programm ferngesteuert den Focus zu geben, oder? Könnte ich gut für Premiere Pro von Adobe gebrauchen...

Aber nochmals vielen Dank!

Gambit

Luckie 12. Feb 2005 14:38

Re: anderes Programm beenden
 
Weil MSDN-Library durchsuchenFindWindow nichts mit der Sprache Delphi zu tun hat, sondern eine Windows API Funktion ist.

Gambit 12. Feb 2005 14:41

Re: anderes Programm beenden
 
naja, andere API-Funktionen werden ja auch erklärt...

Luckie 12. Feb 2005 14:42

Re: anderes Programm beenden
 
Welche?

Gambit 12. Feb 2005 14:50

Re: anderes Programm beenden
 
CreateProcess?

Luckie 12. Feb 2005 15:02

Re: anderes Programm beenden
 
Ist bei mir nicht drinne: Delphi 6 Personal.

Gambit 12. Feb 2005 15:08

Re: anderes Programm beenden
 
Bei mir schon, ist aber glaube ich auch nicht wirklich ne API Funktion...wirst wohl recht haben...
eine Frage noch Luckie, wenn ich durch Klicken auf ein inaktives Fenster dem Fenster den Focus gebe, wird dann auch ne Windows Botschaft gesendet bzw. welche?

Luckie 12. Feb 2005 15:11

Re: anderes Programm beenden
 
an das Fenster: WM_ACTIVATE und wohl noch WM_PAINT und WM_NCPAINT, Das dürften die wichtigsten sein.

Sprint 12. Feb 2005 15:24

Re: anderes Programm beenden
 
Zitat:

Zitat von Gambit
wenn ich durch Klicken auf ein inaktives Fenster dem Fenster den Focus gebe, wird dann auch ne Windows Botschaft gesendet bzw. welche?

Um ein Fenster in den Vordergrund zu holen, bietet Windows dir API Funktionen an. Also nicht mit SendMessage versuchen.

Gambit 12. Feb 2005 15:41

Re: anderes Programm beenden
 
Zitat:

Zitat von Sprint
[...bietet Windows dir API Funktionen an. Also nicht mit SendMessage versuchen.

Jau, habe ich auch gerade erfahren müssen...magst du mir die API-Funktion sagen?

So würde es gehen...zumindest versuchsweise...

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
  AppHWnd: HWND;
begin

  AppHWnd := FindWindow(nil, 'Unbenannt - Editor');
  if IsWindow(AppHWnd) then
    BringWindowToTop(AppHWnd)else ShowMessage('Pech gehabt')
end;

Sprint 12. Feb 2005 15:58

Re: anderes Programm beenden
 
Irgendwann mal war es BringWindowToTop. Dann SetForegroundWindow. Mit der Einführung von IE4, ging es nur noch, wenn du du dich in den Thread eingehackt hast. Kann das Verhalten von den Microsoft Entwicklern irgendwie nicht nachvollziehen. Aber die werden sich bestimmt etwas dabei gedacht haben.

Delphi-Quellcode:
procedure TTaskMgrForm.ForceSetForegroundWindow(AHandle: HWND);
var
  AppHWnd: HWND;
  AppThreadId: DWORD;
  ThisThreadId: DWORD;
  TimeOut: DWORD;
begin

  if GetForegroundWindow <> AHandle then
  begin
    BringWindowToTop(AHandle);
    SetForegroundWindow(AHandle);
    AppHWnd := GetForegroundWindow;
    if AppHWnd <> AHandle then
    begin
      AppThreadId := GetWindowThreadProcessId(AppHWnd, nil);
      ThisThreadId := GetCurrentThreadId;
      if (AppThreadId <> 0) and (ThisThreadId <> 0) then
      begin
        if AttachThreadInput(ThisThreadId, AppThreadId, True) then
        begin
          BringWindowToTop(AHandle);
          SetForegroundWindow(AHandle);
          AttachThreadInput(ThisThreadId, AppThreadId, False);
        end;
        if GetForegroundWindow <> AHandle then
        begin
          SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, @TimeOut, 0);
          SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(0), SPIF_SENDCHANGE);
          BringWindowToTop(AHandle);
          SetForegroundWindow(AHandle);
          SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, TObject(TimeOut), SPIF_SENDCHANGE);
        end;
      end;
    end;
  end;

end;
Hab ich jetzt so aus einem alten Projekt kopiert. Sollte aber immer noch funktionieren.

Gambit 12. Feb 2005 16:02

Re: anderes Programm beenden
 
Super, Danke!!

Und schönes Wochenende noch...

Gambit

Gambit 12. Feb 2005 17:51

Re: anderes Programm beenden
 
Shit, geht doch nicht, weil Premiere je nach Projekt seinen Fenstertitel ändert...

Gambit

Sprint 12. Feb 2005 21:30

Re: anderes Programm beenden
 
Zitat:

Zitat von Gambit
Shit, geht doch nicht, weil Premiere je nach Projekt seinen Fenstertitel ändert...

Aber du kannst immer noch per Klassenname suchen.

Gambit 12. Feb 2005 22:55

Re: anderes Programm beenden
 
Tja, da bin ich wieder dumm...bei STunnel hast du ja für den Klassennamen das Gleiche verwendet wie für den Fenstertitel. Bei Notepad beispielsweise klappt es, wenn ich als Klassenname nur 'notepad' eingebe.
Wie bekomme ich denn den Klassennamen heraus?

Gruß

Gambit

Sprint 12. Feb 2005 23:00

Re: anderes Programm beenden
 
Kann ich dir leider nichts sagen, da ich keine Adobe Produkte außer Acrobat Reader installiert habe.
Aber über EnumWindows und GetClassName kannst du dir selber eine Liste erstellen und guckst einfach nach. Oder du benutzt ein Tool wie Microsoft Spy++, Borland WinSight32 oder halt X-Spy von Motzi oder WinSpy von toms. Die Tools von toms und Motzi findest du hier im Forum.

Gambit 12. Feb 2005 23:01

Re: anderes Programm beenden
 
alles klar! Danke!

Moombas 21. Feb 2019 10:25

AW: anderes Programm beenden
 
Hallo zusammen,

ich hatte ein ähnliches Problem und mir den Codes aus Beitrag 7 (https://www.delphipraxis.net/272652-post7.html) "geklaut". Dieser hatte auch funktioniert, allerdings bekomme ich neuerdings eine Zugriffsverletzung im laufenden Programm bei
Delphi-Quellcode:
EnumWindows(@EnumWindowsProc, LPARAM(List));

//-->
function EnumWindowsProc(hWnd: HWND; lParam: LPARAM): BOOL; stdcall;
  begin
    TList(lParam).Add(Pointer(hWnd));
    Result := True;
  end;
Kann ich dabei den Grund irgendwie lokalisieren? Ich vermute das die Funktion hier irgendwie auf einen Fehler läuft.

Hier die einzelnen Codes von "mir":
Delphi-Quellcode:
//...global private
private
    { Private-Deklarationen }
    AppPID                      : DWORD;

//...Programm (als bestimmter Nutzer) starten
function TTools.CreateProcessAsLogon(const User, PW, Application, param, CmdLine: WideString): DWORD;
var
  s  : WideString;
  si : TStartupInfoW;
  pif : TProcessInformation;
begin
  ZeroMemory(@si, sizeof(si));
  si.cb := sizeof(si);
  si.dwFlags := STARTF_USESHOWWINDOW;
  si.wShowWindow := 1;

  if CmdLine = '' then
    s := Application
  else
    s := Application + ' "' + CmdLine + '"';

  SetLastError(0);

  SI.cb := SizeOf(TStartupInfo);
  if CreateProcessWithLogonW(PWideChar(User), nil, PWideChar(PW), 0, nil, PWideChar(s), CREATE_DEFAULT_ERROR_MODE, nil, PChar(param), @si, @pif) then
  begin
    AppPID := PIf.dwProcessId;
    CloseHandle(PIf.hProcess);
    CloseHandle(PIf.hThread);
  end;

  Result := GetLastError;
end;

//...gestartetes Programm beenden
procedure TTools.EscapeClick(Sender: TObject);
  function EnumWindowsProc(hWnd: HWND; lParam: LPARAM): BOOL; stdcall;
  begin
    TList(lParam).Add(Pointer(hWnd));
    Result := True;
  end;

var
  List        : TList;
  I, answer   : Integer;
  ProcessId   : DWORD;
  ThreadId    : DWORD;
  ProcessHandle: THandle;
  ExitCode    : DWORD;
begin
  if Instant.Enabled = true then
  begin
    answer := messagedlg('Wollen sie wirklich abbrechen?', mtConfirmation, [mbYes,mbNo], 0);
    if answer = mrYes then
    begin
      if AppPID <> 0 then
      begin
        List := TList.Create;
        try
          EnumWindows(@EnumWindowsProc, LPARAM(List));   //<---Hier kommt die Zugriffsverletzung
          for I := 0 to List.Count - 1 do
          begin
            ThreadId := GetWindowThreadProcessId(HWND(List.Items[I]), ProcessId);
            if ProcessId = AppPID then
            begin
              if IsWindow(HWND(List.Items[I])) then
              begin
                PostThreadMessage(ThreadId, WM_QUIT, 0, 0);
                ProcessHandle := OpenProcess(PROCESS_ALL_ACCESS, False, ProcessId);
                if ProcessHandle <> 0 then
                begin
                  GetExitCodeProcess(ProcessHandle, ExitCode);
                  TerminateProcess(ProcessHandle, ExitCode);
                  CloseHandle(ProcessHandle);
                end;
              end;
              Break;
            end;
          end;
        finally
          List.Free;
        end;
      end;
      //Log schreiben
      Logging(PfadP + Programs.items[Programs.itemindex] + ' wurde abgebrochen!');
      answer := messagedlg('Wollen sie die bisherigen Daten einlesen?', mtConfirmation, [mbYes,mbNo], 0);
      if answer = mrYes then
      begin
        FertigClick(Sender);
      end;
      AppPID := 0;
      Progress.Animate  := False;
      Run.Enabled       := False;
      Programs.Enabled  := True;
      Data.Enabled      := True;
      TxTLogs.Enabled   := True;
      Escape.Enabled    := False;
      LogL.Hide;
      LogA.Hide;
      Timer3.Enabled    := False;
      Instant.Enabled   := False;
      Kopieren.Enabled  := True;
      FilialeC.Enabled  := True;
      BezirkS.Enabled   := True;
      FilialenPS.Enabled := True;
      Manuell.Enabled   := True;
      Last.Enabled      := True;
    end;
  end else
  begin
    Progress.Animate  := False;
    Run.Enabled       := False;
    Programs.Enabled  := True;
    Data.Enabled      := True;
    TxTLogs.Enabled   := True;
    Escape.Enabled    := False;
    LogL.Hide;
    LogA.Hide;
    Timer3.Enabled    := False;
    Instant.Enabled   := False;
    Kopieren.Enabled  := True;
    FilialeC.Enabled  := True;
    BezirkS.Enabled   := True;
    FilialenPS.Enabled := True;
    Manuell.Enabled   := True;
    Last.Enabled      := True;
    Watch.Stop;
    watch.Destroy;
  end;
end;
Kann das durch meine Umstellung des Programms auf 64Bit kommen?
Edit: Wenn ich es als 32-Bit kompiliere funktioniert es einwandfrei. Wie kann ich den Teil für 64Bit ändern?

peterbelow 21. Feb 2019 11:41

AW: anderes Programm beenden
 
Zitat:

Zitat von Moombas (Beitrag 1426176)
Hallo zusammen,

ich hatte ein ähnliches Problem und mir den Codes aus Beitrag 7 (https://www.delphipraxis.net/272652-post7.html) "geklaut". Dieser hatte auch funktioniert, allerdings bekomme ich neuerdings eine Zugriffsverletzung im laufenden Programm bei
Delphi-Quellcode:
EnumWindows(@EnumWindowsProc, LPARAM(List));

Das Problem ist das hier:

Delphi-Quellcode:
procedure TTools.EscapeClick(Sender: TObject);
  function EnumWindowsProc(hWnd: HWND; lParam: LPARAM): BOOL; stdcall;
  begin
    TList(lParam).Add(Pointer(hWnd));
    Result := True;
  end;
Du kannst keine nested procedure als callback für eine API-Funktion verwenden, Du mußt die EnumWindowsProc aus der Methode herausnehmen und als eigenständige Funktion implementieren. Nested procedures erfordern einen besondere struktur des callstacks, damit sie auf die parameter und lokalen variabled der umgebenden Procedure zugreifen können. Damit sind sie nicht kompatibel mit einem API callback.

Moombas 21. Feb 2019 13:00

AW: anderes Programm beenden
 
Ok, habs.

Aber warum macht er das in 64-Bit anders? Ist das etwas was man generell wissen sollte oder einfach Willkür?

peterbelow 22. Feb 2019 12:26

AW: anderes Programm beenden
 
Zitat:

Zitat von Moombas (Beitrag 1426186)
Ok, habs.

Aber warum macht er das in 64-Bit anders? Ist das etwas was man generell wissen sollte oder einfach Willkür?

Das der Kode überhaupt jemals funktioniert hat ist blanker Zufall. Vermutlich ist der Stackaufbau und die Parameterübergabe bei 64-bit anders als bei 32 bit, daher knallt es da direkt.


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