[gelöst] Externes Prog Focus wegnehmen und schließen

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

[gelöst] Externes Prog Focus wegnehmen und schließen

Beitrag von Michl »

Hallo wertes Forum,

ich suche nun schon eine Weile, konnte aber nicht die Antworten auf meine Fragen finden.

Ich nutze/nutzte zum mitloggen meiner Programme eine selbst geschriebene Status-Form. Das ganze habe ich nun geändert und aus der Form ist ein eigenständiges Programm geworden, was mittels TSimpleIPC kommuniziert. Bringt für mich verschmerzbare Performanceeinbußen mit sich, hat aber den Riesenvorteil, daß die Information, wo ein Fehler auftaucht für mich nachvollziehbarer ist, da das Mitschnittprogramm noch aktiv ist (eine Division durch null führt zum Absturz des Hauptprogramms, nicht des Mitschnittprogramms, Historie ist dadurch sehr leicht nachvollziehbar).

Also ich starte mit

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
begin
  AProcess:=TProcess.Create(nil);
  try
    AProcess.CommandLine:='Statusneu.exe';
    AProcess.Execute;
  finally
  end;
 
//  ShellExecute(Form1.Handle,'open',pchar('Statusneu.exe'),nil,nil, SW_SHOWNORMAL);
end;
das Mitschnittprogramm.

Frage1: Wie kann ich den Focus, der dadurch auf das Mitschnittprogramm gesetzt wird wieder zurückholen und auf das Hauptprogramm setzen?

Frage2: Beim normalen Beenden des Hauptprogramms will ich, dass das Mitschnittprogramm ebenfalls normal beendet wird. Ich kann mit

Code: Alles auswählen

procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  AProcess.Terminate(0);
  AProcess.Free;
end;
das Externe Programm beenden, doch es sollte "normal" geschlossen werden. D.h. ich habe den Destructor überschrieben (Speichern der letzten Daten in eine Datei, SimpleIPC-Server schließen). Dieser Destructor wird aber bei dem .Terminate nicht mehr durchlaufen. Gibt es eine andere Möglichkeit das externe Mitschnittprogramm zu schließen?
Zuletzt geändert von Michl am Mo 24. Jun 2013, 20:49, insgesamt 1-mal geändert.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Externes Prog Focus wegnehmen und schließen

Beitrag von Michl »

Gerade gefunden, doch

Code: Alles auswählen

    AProcess.ShowWindow:=swoHIDE;
//                            swoNone,swoHIDE,swoMaximize,swoMinimize,swoRestore,swoShow,
//                            swoShowDefault,swoShowMaximized,swoShowMinimized,
//                            swoshowMinNOActive,swoShowNA,swoShowNoActivate,swoShowNormal)
 
hat auch keinen Erfolg!

im Forum http://forum.lazarus.freepascal.org/ind ... ic=17525.0 war das zielführend, funktioniert bei mir aber nicht. Ich nutze Win32, sollte lt. http://lazarus-ccr.sourceforge.net/docs ... tions.html doch eigntlich gehen?!

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

DonMigos
Beiträge: 52
Registriert: Mi 15. Mai 2013, 21:21
OS, Lazarus, FPC: Win7 (L 1.0.8 FPC 2.6.2)
CPU-Target: 32Bit

Re: Externes Prog Focus wegnehmen und schließen

Beitrag von DonMigos »

Zu Frage 2: Du könntest mal Active http://lazarus-ccr.sourceforge.net/docs ... ctive.html auf False setzen, das schließt normal auch das Programm.

Wegen Frage 1, bei mir klappt auch nichts bei diesem Versuch (also fenster wird immer angezeigt):

Code: Alles auswählen

 
begin
  AProcess := TProcess.Create(nil);
 
  AProcess.Executable := 'calc.exe';
 
  AProcess.StartupOptions := AProcess.StartupOptions + [suoUseShowWindow]; 
  AProcess.ShowWindow := swoShowNoActivate; //keine Wirkung
  AProcess.ShowWindow := swoHIDE; //keine Wirkung
  AProcess.Options := AProcess.Options + [poNoConsole]; //keine Wirkung (eigent. klar weil keine Konsole)
 
  AProcess.Execute;   
  AProcess.Free;    
end;
 
Ist das ein Bug ?

Mfg

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Externes Prog Focus wegnehmen und schließen

Beitrag von Michl »

Zu Frage 2: Du könntest mal Active http://lazarus-ccr.sourceforge.net/docs ... ctive.html auf False setzen, das schließt normal auch das Programm.
Geht leider auch nicht, der Destructor des Log-Programms wird dadurch auch nicht aufgerufen. Schließe ich das Log-Programm normal mit ALT+F4 wird er aufgerufen. :(
Ich habe auch versucht, die notwendigen Befehle beim Schließen in das OnClose-Event zu packen, das hatte ebenfalls keinen Erfolg.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6780
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Externes Prog Focus wegnehmen und schließen

Beitrag von af0815 »

Frage2: Wenn du schon mit TSimpleIPC arbeitest, warum schickst du nicht gleich den schliessen Befehl per IPC.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Externes Prog Focus wegnehmen und schließen

Beitrag von Michl »

Frage2: Wenn du schon mit TSimpleIPC arbeitest, warum schickst du nicht gleich den schliessen Befehl per IPC.
Ja, genau das war mir gestern, bevor ich ins Traumland verschwunden bin, auch noch in den Sinn gekommen - danke dafür :arrow: :)

Code: Alles auswählen

procedure TStatus.IPCServerMessage(Sender: TObject);
begin
  if IPCServer.MsgType=1 then Status.Write(IPCServer.StringMessage)
  else case IPCServer.MsgType of
    2:Status.Exec(strtoint(IPCServer.StringMessage));
    else Status.Write('[Status-IPCServerMessage] der angeforderte Typ ist kein String sondern vom Typ['+inttostr(IPCServer.MsgType)+']');
  end;
end;
 
procedure TStatus.Exec(nr:integer);        //Führt Befehle aus
begin
  case nr of
    -1:Application.Terminate;
  end;
end;
Funktioniert!

Warum geht das ganze aber von außen nicht? Wenn ich mit AProcess.Terminate das Programm schließe müsste doch ebenfalls der Destructor durchlaufen werden, damit der Speicher wieder ordentlich frei gegeben wird?!

Ich habe eben mal statt meinem Log-Programm einfach irgendein Textfile mit dem Windows-Editor geöffnet und getestet:

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
begin
  AProcess:=TProcess.Create(Self);
  try
    AProcess.CommandLine:='notepad c:\1\test.txt';
    AProcess.ShowWindow:=swoShowNA;
    AProcess.Execute;
  finally
  end;
end;
und auch da kann das Programm von außen nicht ordentlich geschlossen werden (Normalerweise wird bei einer Text-Änderungen abgefragt, ob sie gespeichert werden soll, bei AProcess.terminate kommt keine Abfrage und Notepad wird "hart" geschlossen).
Scheinbar wird in diesem Fall der Destructor komplett übergangen und der genutzte Speicher vom Betriebssystem freigegeben?!


Eine weitere Frage (zurück zu Frage1) drängt sich hier auf. Während bei meinem Programm bzw. auch der Taschenrechner "calc.exe" der Befehl

Code: Alles auswählen

AProcess.ShowWindow:=swoShowNA;
keinen Effekt hatte, funktioniert dies beim Windowseditor "Notepad" so, wie es sein soll :?: :?: :?:

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

DonMigos
Beiträge: 52
Registriert: Mi 15. Mai 2013, 21:21
OS, Lazarus, FPC: Win7 (L 1.0.8 FPC 2.6.2)
CPU-Target: 32Bit

Re: Externes Prog Focus wegnehmen und schließen

Beitrag von DonMigos »

Korrektes Schließen habe ich bis jetzt nur so hinbekommen:

Code: Alles auswählen

 uses ..., windows;
 
SendMessage(FindWindow('notepad', nil), WM_CLOSE, 0, 0); 
Beim Notepad kommt dann die Abfrage nach Speichern der Änderung.

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Externes Prog Focus wegnehmen und schließen

Beitrag von Michl »

SendMessage(FindWindow('notepad', nil), WM_CLOSE, 0, 0);
Ja, das ist dann so, als würde ich das Programm per Hand (Alt+F4 oder Mausklick) schließen.:) Ich habe aber in div. Foren gelesen, dass es da Ärger geben kann, wenn mehrere Instanzen von dem Programm geöffnet sind.

Das mit dem Schließen funktioniert jetzt soweit (ich nutze jetzt für meine Zwecke die Kommunikation mittels TSimpleIPC). Mich hat hauptsächlich gewundert, dass per ".Terminate" ein "harter" Abbruch stattfindet (wie z.B. mit Task-Manager) und nicht der Destructor durchlaufen wird.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

DonMigos
Beiträge: 52
Registriert: Mi 15. Mai 2013, 21:21
OS, Lazarus, FPC: Win7 (L 1.0.8 FPC 2.6.2)
CPU-Target: 32Bit

Re: Externes Prog Focus wegnehmen und schließen

Beitrag von DonMigos »

Man müsste eigentlich über TProcess.ProcessHandle an das Fenster Handle dran kommen, dann könnte man das am Anfang auch minimiert starten und ordentlich schließen. Mit dem Fenster-Handle hätte man ja einen eindeutigen Idenentifikator (müsste dann auch bei mehreren gleichnamigen Fenstern das richtige finden).
Ich habe es mal Versucht, klappt aber nicht ... Aber irgendwie so ähnlich könnte es gehen.

Code: Alles auswählen

var
  AProcess: TProcess;
    WindowHandle: THandle;
 
function MyEnumWindowProc(AHandle: THandle; LParam: LongInt): LongBool; stdcall;
var
  ProcessID: THandle;
begin
  ProcessID := 0;
  GetWindowThreadProcessID(AHandle, ProcessID);
  Result := not (ProcessID = LParam);
  if not Result then
    WindowHandle := AHandle;
end;
 
function GetWindowByHandle(const PrcHandle: THANDLE): THandle;
begin
  Result := 0;
  WindowHandle := 0;
 
  EnumWindows(@MyEnumWindowProc, PrcHandle);
  Result := WindowHandle;
end;  

DonMigos
Beiträge: 52
Registriert: Mi 15. Mai 2013, 21:21
OS, Lazarus, FPC: Win7 (L 1.0.8 FPC 2.6.2)
CPU-Target: 32Bit

Re: Externes Prog Focus wegnehmen und schließen

Beitrag von DonMigos »

Die Lösung, die ich gefunden habe, ist zwar nicht wirklich schön, sollte aber jetzt mit jedem Programm funktionieren.
Bis jetzt läuft es nur im Delphi-Mode.

Code: Alles auswählen

{$mode delphi}{$H+}     
 
uses windows,process;
 
type
  PEnumInfo = ^TEnumInfo;
  TEnumInfo = record ProcessID: DWORD; HWND: THandle; end;
 
var
  AProcess: TProcess;
  ProcWindowHandle: THandle;
 
function EnumWindowsProc(Wnd: DWORD; var EI: TEnumInfo): Bool; stdcall;
var
  PID: DWORD;
begin
  GetWindowThreadProcessID(Wnd, @PID);
  Result := (PID <> EI.ProcessID) or (not IsWindowVisible(WND)) or (not IsWindowEnabled(WND));
  if not Result then EI.HWND := WND;
end;
 
function FindMainWindow(PID: DWORD): DWORD;
var
  EI: TEnumInfo;
begin
  EI.ProcessID := PID;
  EI.HWND := 0;
  EnumWindows(@EnumWindowsProc, Integer(@EI));
  Result := EI.HWND;
end;
 
procedure TForm2.Button1Click(Sender: TObject);
begin
  AProcess := TProcess.Create(nil);
 
  AProcess.Executable := 'calc.exe';
  AProcess.Execute;
 
 //Warten bis Programm geöffnet, sonst ProcWindowHandle -> 0
  while ProcWindowHandle = 0 do //Timeout wäre hier sicher noch sinnvoll oder statt while nur sleep(...) benutzen
  begin
    ProcWindowHandle:= FindMainWindow(AProcess.ProcessID);
    Application.ProcessMessages;
  end;
 
  SendMessage(ProcWindowHandle,WM_SYSCOMMAND,SC_MINIMIZE,0); //Fenster minimieren
end;
 
procedure TForm2.Button2Click(Sender: TObject);
begin
  //Fenster korrekt schließen -> z.B. Abfrage auf Speichern bei notepad wird aufgerufen, wenn Text geändert wurde
  SendMessage(ProcWindowHandle,WM_CLOSE,0,0);
  FreeAndNil(AProcess);
end;
 
procedure TForm2.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
  if Assigned(AProcess) then AProcess.Free;
end;                                                              
Mfg

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Externes Prog Focus wegnehmen und schließen

Beitrag von Michl »

Wow, bist ein Schatz! :D :D :D

Habe es eben auf mein Prog angepasst (SW_SHOWNOACTIVATE statt SC_MINIMIZE) und alles in OnCreate und OnClose gepackt und es läuft, genauso wie ich das wollte!

Vielen Dank nochmal!!!!

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

DonMigos
Beiträge: 52
Registriert: Mi 15. Mai 2013, 21:21
OS, Lazarus, FPC: Win7 (L 1.0.8 FPC 2.6.2)
CPU-Target: 32Bit

Re: [gelöst] Externes Prog Focus wegnehmen und schließen

Beitrag von DonMigos »

Ja kein Problem, hat mich interessiert das Thema, weil ich TProcess auch oft benutze. Eigentlich dürfte das aber nicht so umständlich gehen, vielleicht sollte ich mal auf dem Bugtracker einen Eintrag machen...

Antworten