Autor |
Beitrag |
Hoffensterchen
Hält's aus hier
Beiträge: 5
Win XP, Win 7
Delphi 2005
|
Verfasst: Mi 25.05.11 19:41
Hallo liebe Leutz,
ich schreibe ein Programm, dass in bestimmten Situationen ein laufenden Fremdprogramm beendet, ein paar Operationen durchführt und danach das Fremdprogramm wieder startet.
Leider läßt sich das Fremdprogramm nicht "still" beenden, d.h. für ein "geordnetet" Beenden muß immer eine Sicherheitsfrage geklickt werden. Das Programm läuft unsichtbar, es zeigt sich nur durch mehrere Icons im Systray.
Mein Programm beendet das Fremdprogramm also "hart" per TerminateProcess(...), was auch problemlos funktioniert, da keine Daten gespeichert oder Dateien geschlossen werden müssen.
Allerdings gibt es ein kosmetisches Problem: Die Icons im Systray verschwinden erst, wenn man mit der Maus drüberfährt. Meine Frage also:
Wie kann ich den Systray "aufräumen", d.h. per Code alle "toten" Icons zu entfernen? Gibt es dafür sowas wie ein "Refresh" oder etwas in der Art?
Danke für Eure Hilfe!
Hoffensterchen
Edith sagt: Wasse nich im Koppe has'... Sorry, ich vergaß zu erwähnen, daß mein Programm unter Windows XP SP3 läuft...
|
|
jaenicke
Beiträge: 19302
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 25.05.11 20:03
Hallo und
Nein, dafür gibt es keine Möglichkeit. Du bist nicht der erste, der danach fragt.
Manche haben versucht den Mauszeiger per Programm herumzuschubsen und sowas, aber wirklich zuverlässig funktionieren tut das nicht.
Hast du denn schon versucht die Sicherheitsabfrage einfach durch dein Programm zu bestätigen? Das wäre doch wohl der sinnvollere und vor allem einfachere Weg...
|
|
Gerd Kayser
Beiträge: 632
Erhaltene Danke: 121
Win 7 32-bit
Delphi 2006/XE
|
Verfasst: Mi 25.05.11 20:10
jaenicke hat folgendes geschrieben : | Hast du denn schon versucht die Sicherheitsabfrage einfach durch dein Programm zu bestätigen? Das wäre doch wohl der sinnvollere und vor allem einfachere Weg... |
Ich würde eine WM_Quit-Message an das Programm schicken. Dann dürfte die Sicherheitsabfrage nicht erscheinen. Bei TerminateProcess könnten Reste im System verbleiben.
|
|
Hoffensterchen
Hält's aus hier
Beiträge: 5
Win XP, Win 7
Delphi 2005
|
Verfasst: Mi 25.05.11 20:14
Danke erstmal!
Mpft...
jaenicke hat folgendes geschrieben: | Hast du denn schon versucht die Sicherheitsabfrage einfach durch dein Programm zu bestätigen? Das wäre doch wohl der sinnvollere und vor allem einfachere Weg... |
Ist mein Plan B...
Gerd Kayser hat folgendes geschrieben: | Ich würde eine WM_Quit-Message an das Programm schicken. |
Ömm... Wie geht das...?
|
|
jaenicke
Beiträge: 19302
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 25.05.11 20:20
Stichworte:
FindWindow oder EnumWindows sowie SendMessage oder PostMessage.
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 25.05.11 20:26
|
|
Gerd Kayser
Beiträge: 632
Erhaltene Danke: 121
Win 7 32-bit
Delphi 2006/XE
|
Verfasst: Do 26.05.11 00:32
Hoffensterchen hat folgendes geschrieben : | Wie kann ich den Systray "aufräumen", d.h. per Code alle "toten" Icons zu entfernen? Gibt es dafür sowas wie ein "Refresh" oder etwas in der Art? |
Was man in der TaskbarNotification-Area sieht, sind keine Icons oder Buttons. Der Bereich ist nichts anderes als ein Bildchen mit mehreren gemalten "Icons". Deshalb kann man die einzelnen "Icons" auch nicht mit Tastaturbefehlen ansteuern, sondern nur mit der Maus. Ob nun ein Eintrag noch gültig ist, wird von dem TNA-Prozess nur geprüft, wenn der Mauszeiger direkt auf einem der Bildchen steht.
Vorbemerkung zu meiner Lösung:
1. Getestet habe ich es nur unter Windows 7 32-Bit. Bei anderen Windows-Versionen sind die Parameter bei FindWindow(Ex) anzupassen.
2. Fehlerbehandlung habe ich keine eingebaut. Das überlasse ich Euch (vorgerückte Stunde und mit meiner Schäferhündin muß ich noch eine Runde Gassi gehen).
3. Ob man besondere Rechte benötigt, habe ich nicht geprüft.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93:
| procedure TForm1.Button1Click(Sender: TObject); var Anzahl : integer; begin Anzahl := TNA_Refresh; Label1.Caption := 'Gelöschte Icon-Leichen: ' + IntToStr(Anzahl); end;
function TForm1.TNA_Refresh: integer; var wnd : HWND; AnzahlIcons : integer; Schleife : integer; Koordinaten : TPoint; AltKoordinaten : TPoint; pID : cardinal; hProg : THandle; Rect : TRect; pAdr : Pointer; Gelesen : cardinal; AnzahlBytes : integer; begin Result := - 1;
wnd := FindWindow('Shell_TrayWnd', nil); wnd := FindWindowEx(wnd, 0, 'TrayNotifyWnd', nil); wnd := FindWindowEx(wnd, 0, 'SysPager', nil); wnd := FindWindowEx(wnd, 0, 'ToolbarWindow32', nil);
if wnd <> 0 then begin GetCursorPos(AltKoordinaten);
AnzahlIcons := SendMessage(wnd, TB_ButtonCount, 0, 0);
GetWindowThreadProcessID(wnd, @pID);
hProg := OpenProcess(Process_VM_Operation or Process_VM_Read or Process_VM_Write, false, pID);
AnzahlBytes := SizeOf(Rect);
pAdr := VirtualAllocEx(hProg, nil, AnzahlBytes, MEM_Reserve or MEM_Commit, Page_ReadWrite);
for Schleife := AnzahlIcons - 1 downto 0 do begin SendMessage(wnd, TB_GetRect, Schleife, integer(pAdr));
ReadProcessMemory(hProg, pAdr, @Rect, AnzahlBytes, Gelesen);
Koordinaten.X := Rect.Left + 3; Koordinaten.Y := Rect.Top + 3; Windows.ClientToScreen(wnd, Koordinaten); SetCursorPos(Koordinaten.X, Koordinaten.Y); end;
VirtualFreeEx(hProg, pAdr, 0, MEM_Release); CloseHandle(hProg);
SetCursorPos(AltKoordinaten.X, AltKoordinaten.Y);
Result := AnzahlIcons - SendMessage(wnd, TB_ButtonCount, 0, 0); end; end; |
Edit-1: Schleife in eine downto-Schleife geändert.
Zuletzt bearbeitet von Gerd Kayser am Do 26.05.11 11:24, insgesamt 1-mal bearbeitet
|
|
thepaine91
Beiträge: 763
Erhaltene Danke: 27
Win XP, Windows 7, (Linux)
D6, D2010, C#, PHP, Java(Android), HTML/Js
|
Verfasst: Do 26.05.11 08:37
Ich würde empfehlen mal dem Link von Luckie zu folgen dort sind ein paar gute Lösungsansätze die vor allem sauberer sind als die Maus hin und her zu schieben.
|
|
Gerd Kayser
Beiträge: 632
Erhaltene Danke: 121
Win 7 32-bit
Delphi 2006/XE
|
Verfasst: Do 26.05.11 11:34
thepaine91 hat folgendes geschrieben : | dort sind ein paar gute Lösungsansätze die vor allem sauberer sind als die Maus hin und her zu schieben. |
Wenn ich den Thread dort richtig gelesen habe, geht es dort um das Verstecken eines Icons und nicht um das Aktualisieren des TNA-Bereichs. Auch wenn man eine Leiche im Keller versteckt, so ändert das nichts daran, dass im Keller eine Leiche liegt.
Zur Mausschieberei: Ich habe die Ausführungszeit der Funktion mit GetTickCount gemessen. Sie beträgt 0 MSek. Der Cursor ist weder am Flackern noch sonstwas. Der Anwender bekommt davon überhaupt nichts mit.
Meine Lösung funktioniert jedenfalls unter Windows 7 32-Bit. Das müssen andere Lösungsansätze erst einmal zeigen. Wenn ich nachher mal etwas Zeit habe, werde ich es unter XP mal testen (nach dem Heraussuchen der FindWindow-Parameter).
|
|
thepaine91
Beiträge: 763
Erhaltene Danke: 27
Win XP, Windows 7, (Linux)
D6, D2010, C#, PHP, Java(Android), HTML/Js
|
Verfasst: Do 26.05.11 12:27
Ja aber auf Seite 4 ist eine Lösung zu finden die laut Author die Icons nicht nur versteckt sondern entfernt. Genauer hab ich es mir auch nicht angeguckt.
Nebenbei das Mauszeigerverschieben mag wunderbar funktionieren wenn aber ein Topmost Fenster drüber liegt trifft der Mauszeiger das Icon nicht also wird es auch nicht entfernt. Sobald das Fenster dann aber verschoben wird kommt es wieder zum Vorschein. Der Fall ist zwar selten aber dennoch kann es vorkommen.
|
|
Gerd Kayser
Beiträge: 632
Erhaltene Danke: 121
Win 7 32-bit
Delphi 2006/XE
|
Verfasst: Do 26.05.11 15:30
thepaine91 hat folgendes geschrieben : | Ja aber auf Seite 4 ist eine Lösung zu finden die laut Author die Icons nicht nur versteckt sondern entfernt. Genauer hab ich es mir auch nicht angeguckt. |
Meinst Du tna_181.zip? An die Datei komme ich leider nicht heran, weil ich dort im Forum keinen Account habe. Und überall Accounts anlegen möchte ich auch nicht gerade. Aber vielleicht kannst Du mir das per Mail schicken.
Zitat: | wenn aber ein Topmost Fenster drüber liegt trifft der Mauszeiger das Icon nicht also wird es auch nicht entfernt. |
Werde ich nachher mal versuchen hier nachzustellen. Bislang ist es mir auf die Schnelle nur gelungen, Fenster hinter die Taskbar zu verschieben. Dabei ist mir allerdings aufgefallen, dass ich einen Fall übersehen habe. Nämlich: Wenn die Taskbar auf automatisches Ausblenden eingestellt ist. Aber das Problem lässt sich ja lösen.
|
|
thepaine91
Beiträge: 763
Erhaltene Danke: 27
Win XP, Windows 7, (Linux)
D6, D2010, C#, PHP, Java(Android), HTML/Js
|
Verfasst: Do 26.05.11 15:36
Jo hab einen Account in der DP, schicke es dir per PN.
Ich hab es getestet und es passiert das was ich beschrieben habe. Ist aber nach wie vor ein seltener Fall denke ich.
|
|
Gerd Kayser
Beiträge: 632
Erhaltene Danke: 121
Win 7 32-bit
Delphi 2006/XE
|
Verfasst: So 29.05.11 12:51
Heute habe ich mal ausgiebig das Programm aus tna_181.zip unter Windows 7 32-Bit getestet. Als Testobjekt habe ich TV-Guide (Fernsehprogramm von Sky) benutzt. Die Anwendung erzeugt zusätzlich einen Prozess (iSaverCtrl.exe) mit einem TNA-Icon. Bei meinen Tests ist mir Folgendes aufgefallen:
1. Das Programm löscht nur das TNA-Icon eines vorgegebenen, laufenden Prozesses und lässt eine Prozessleiche zurück (Icon wird gelöscht, Prozess aber nicht beendet).
2. Ein verwaistes Icon (Prozess gekillt) wird nicht gelöscht.
3. Das Programm enthält eine Stolperfalle.
Delphi-Quelltext 1:
| ProcPath:= 'C:\Programme\iSaver\iSaverCtrl.exe'; |
Das nachfolgende FileExists funktioniert zwar, aber
Delphi-Quelltext 1:
| if SameText(ProcPath, GetProcPath(IconData.Wnd)) then |
wird immer fehlschlagen, weil GetProcPath "C:\Program Files\..." zurückliefert.
Fazit: In der vorliegenden Form ist das Programm völlig ungeeignet, weil es die Icons im TNA-Bereich nicht aufräumt (siehe Punkt 2). Und das Löschen eines Icons, ohne den dazugehörigen Prozess zu beenden, ist eigentlich sinnlos (siehe Punkt 1).
Aber das Programm hat mich auf eine Idee gebracht, wie man das Refreshen realisieren könnte (auch unter XP und Vista). Dazu brauche ich aber einige Tage Zeit. Auch als Ruheständler ist man nicht immer Herr seiner Zeit.
|
|
Gerd Kayser
Beiträge: 632
Erhaltene Danke: 121
Win 7 32-bit
Delphi 2006/XE
|
Verfasst: Do 02.06.11 13:13
Hier ist die Lösung. Hat zwar einige Stunden gedauert, weil ich mal wieder zu kompliziert gedacht hatte. Dabei ist die Lösung eigentlich recht simpel.
Die Lösung funktioniert auch bei einer Taskleiste, die ausgeblendet ist. Ich habe zwar das Programm nur unter Windows 7 32-Bit getestet, aber es sollte ab Windows XP funktionieren. Ob das Programm unter 64-Bit funktioniert, kann ich hier leider nicht testen.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128:
| unit Main;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, CommCtrl, ShellAPI;
type TMainform = class(TForm) Refresh: TButton; Label1: TLabel; procedure RefreshClick(Sender: TObject); procedure FormCreate(Sender: TObject); private public function TNA_Refresh : integer; function SuchenTNAWindow : HWnd; end;
var Mainform: TMainform;
implementation
{$R *.dfm}
function TMainform.TNA_Refresh : integer; var Wnd : HWnd; AnzahlIcons : integer; Schleife : integer; pID : THandle; HProcess : THandle; pMemory : Pointer; AnzahlBytes : integer; Gelesen : cardinal; TB : TTBButton; IconData : TNotifyIconData; begin Result := - 1; AnzahlBytes := 256; IconData.cbSize:= SizeOf(TNotifyIconData);
Wnd := SuchenTNAWindow; if Wnd <> 0 then begin AnzahlIcons := SendMessage(Wnd, TB_ButtonCount, 0, 0);
GetWindowThreadProcessId(Wnd, @pID);
HProcess := OpenProcess(Process_VM_Operation or Process_VM_Read or Process_VM_Write, false, pID);
pMemory := VirtualAllocEx(HProcess, nil, AnzahlBytes, MEM_Reserve or MEM_Commit, Page_ReadWrite);
for Schleife := AnzahlIcons - 1 downto 0 do begin if SendMessage(Wnd, TB_GetButton, Schleife, Cardinal(pMemory)) <> 0 then if ReadProcessMemory(HProcess, pMemory, @TB, SizeOf(TB), Gelesen) then if ReadProcessMemory(HProcess, Pointer(TB.dwData), @IconData.Wnd, SizeOf(IconData.Wnd) + SizeOf(IconData.uID), Gelesen) then if not IsWindow(IconData.Wnd) then Shell_NotifyIcon(NIM_Delete, @IconData); end;
VirtualFreeEx(HProcess, pMemory, 0, MEM_Release); CloseHandle(HProcess);
Result := AnzahlIcons - SendMessage(wnd, TB_ButtonCount, 0, 0); end; end;
procedure TMainform.FormCreate(Sender: TObject); begin ReportMemoryLeaksOnShutdown := true; end;
function TMainform.SuchenTNAWindow : HWnd; begin Result := FindWindow('Shell_TrayWnd', nil); Result := FindWindowEx(Result, 0, 'TrayNotifyWnd', nil); Result := FindWindowEx(Result, 0, 'SysPager', nil); Result := FindWindowEx(Result, 0, 'ToolbarWindow32', nil); end;
procedure TMainform.RefreshClick(Sender: TObject); var Ergebnis : integer; begin Ergebnis := TNA_Refresh; if Ergebnis > - 1 then Label1.Caption := 'Gelöschte Icons: ' + IntToStr(Ergebnis) else Label1.Caption := 'TNA-Fenster nicht gefunden!'; end;
end. |
|
|
Luckie
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Do 02.06.11 13:59
Na, das sieht doch nach einer sauberen Lösung aus.
|
|
jaenicke
Beiträge: 19302
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 02.06.11 15:14
Gerd Kayser hat folgendes geschrieben : | Ob das Programm unter 64-Bit funktioniert, kann ich hier leider nicht testen. |
Nein, so nicht, da die Datenstruktur logischerweise bei einem 64-Bit Prozess anders aussieht. (64-Bit Pointer, ...)
Diese Version funktioniert immer (wobei eigentlich die Prüfung sein müsste, ob der Zielprozess 64-bittig ist, aber da war ich zu faul nachzuschauen wie das geht und für 32-Bit Delphi geht es auch so ): 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91:
| function FindTNAWindow: HWND; begin Result := FindWindow('Shell_TrayWnd', nil); Result := FindWindowEx(Result, 0, 'TrayNotifyWnd', nil); Result := FindWindowEx(Result, 0, 'SysPager', nil); Result := FindWindowEx(Result, 0, 'ToolbarWindow32', nil); end;
function IsWow64Process: Boolean; type TIsWow64Process = function(hProcess: THandle; var Wow64Process: BOOL): BOOL; stdcall; var Value: BOOL; IsWow64ProcessFunc: TIsWow64Process; begin IsWow64ProcessFunc := GetProcAddress(GetModuleHandle('kernel32'), 'IsWow64Process'); Result := Assigned(IsWow64ProcessFunc); if Result then begin if not IsWow64ProcessFunc(GetCurrentProcess, Value) then raise Exception.Create('Bad process handle'); Result := Value; end; end;
function RefreshTNAIcons: Integer; type TTBButton64 = packed record iBitmap: Integer; idCommand: Integer; fsState: Byte; fsStyle: Byte; bReserved: array [1 .. 6] of Byte; dwData: Int64; iString: Int64; end; var TNAWindow: HWND; i, IconCount: Integer; TrayProcessID: THandle; TrayProcessHandle: THandle; pMemory: Pointer; BufferSize, ReadBytes: Cardinal; TB: TTBButton; TB64: TTBButton64; IconData: TNotifyIconData; ReadSuccessful, UseWow64Data, CanProcessEntry: Boolean; begin Result := -1; BufferSize := 256; IconData.cbSize := SizeOf(TNotifyIconData);
TNAWindow := FindTNAWindow; if TNAWindow <> 0 then begin IconCount := SendMessage(TNAWindow, TB_BUTTONCOUNT, 0, 0); GetWindowThreadProcessId(TNAWindow, @TrayProcessID); TrayProcessHandle := OpenProcess(PROCESS_VM_OPERATION or PROCESS_VM_READ or PROCESS_VM_WRITE, False, TrayProcessID); pMemory := VirtualAllocEx(TrayProcessHandle, nil, BufferSize, MEM_RESERVE or MEM_COMMIT, PAGE_READWRITE); try UseWow64Data := IsWow64Process; for i := IconCount - 1 downto 0 do begin if SendMessage(TNAWindow, TB_GETBUTTON, i, lParam(pMemory)) <> 0 then if UseWow64Data then CanProcessEntry := ReadProcessMemory(TrayProcessHandle, pMemory, @TB64, SizeOf(TB64), ReadBytes) and ReadProcessMemory(TrayProcessHandle, Pointer(TB64.dwData), @IconData.Wnd, SizeOf(IconData.Wnd) + SizeOf(IconData.uID), ReadBytes) else CanProcessEntry := ReadProcessMemory(TrayProcessHandle, pMemory, @TB, SizeOf(TB), ReadBytes) and ReadProcessMemory(TrayProcessHandle, Pointer(TB.dwData), @IconData.Wnd, SizeOf(IconData.Wnd) + SizeOf(IconData.uID), ReadBytes); if CanProcessEntry and not IsWindow(IconData.Wnd) then Shell_NotifyIcon(NIM_DELETE, @IconData); end; finally VirtualFreeEx(TrayProcessHandle, pMemory, 0, MEM_RELEASE); CloseHandle(TrayProcessHandle); end; Result := IconCount - SendMessage(TNAWindow, TB_BUTTONCOUNT, 0, 0); end; end; | Wie wäre es, wenn du das gleich einmal in der Library als neuen Eintrag einstellst?
|
|
ral24092
Hält's aus hier
Beiträge: 1
|
Verfasst: Di 07.05.19 07:04
Man muss "NOTIFYICONOVERFLOWWINDOW" finden, dann "NOTIFYICONOVERFLOWWINDOW" aktivieren und zum Schluss die Maus über "NOTIFYICONOVERFLOWWINDOW" laufen lassen.
Im Anhang der vollständige Code.
LG Rale
Einloggen, um Attachments anzusehen!
|
|
|