Autor |
Beitrag |
Äfan
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Fr 06.03.15 23:48
Hallo,
ich habe ein kleines Tool gebastelt, welches sich nach dem Start in den Tray minimiert, HTTP-Requests entgegen nimmt und bei einem eingehenden Request ein Explorer-Fenster im Vordergrund öffnet.
Dies funktioniert grundsätzlich problemlos, wenn ich das Tool per Hand starte.
Mein Problem ist jetzt, dass dieses Tool im Autostart liegen soll (Verknüpfung im Autostart-Ordner), damit dies bei einer Anmeldung bei Windows gestartet wird.
Bei einem ankommenden Request geht das Explorer-Fenster zwar auf, allerdings immer im Hintergrund.
In der Taskleiste blinkt dann nur das Explorer-Icon und muss per Hand in den Vordergrund geholt werden, was nervig ist.
Ich möchte, dass der Explorer - wie auch beim händischen Start - immer im Vordergrund geöffnet wird.
Folgendes habe ich schon probiert, geholfen hat es aber leider nicht.
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:
| [DllImport("User32.dll")] private static extern bool ShowWindow(IntPtr handle, int nCmdShow);
[DllImport("user32.dll", SetLastError = true)] static extern bool BringWindowToTop(IntPtr hWnd);
[DllImport("user32.dll")] private static extern bool SetForegroundWindow(IntPtr hWnd);
const int SW_RESTORE = 9;
protected void Open() { string path = @"c:\";
try { using (Process process = new Process()) { process.StartInfo.FileName = "explorer"; process.StartInfo.Arguments = "/e," + path; process.Start(); process.WaitForInputIdle(1000); ShowWindow(process.Handle, SW_RESTORE); SetForegroundWindow(process.Handle); BringWindowToTop(process.Handle); } } catch(Exception e) {
} } |
Hat jemand eine Idee, warum das Tool durch den Autostart den Explorer nur im Hintergrund öffnet?
LG
Auch hier gefragt: www.mycsharp.de/wbb2....php?threadid=114105
Zuletzt bearbeitet von Äfan am So 08.03.15 17:38, insgesamt 1-mal bearbeitet
|
|
Th69
      

Beiträge: 4796
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Sa 07.03.15 10:23
Ist das Programm denn in dem Autostart des angemeldeten Benutzers oder für alle Benutzer (System)? Schau mal mit dem TaskManager unter Prozesse nach, wer der Benutzer ist.
Ich könnte mir vorstellen, daß es da Unterschiede gibt, wenn vom System-Benutzer ein Programm geöffnet wird.
Hier noch ein Link zu den zwei verschiedenen Autostart-Verzeichnissen: Where is the startup folder located in Windows 7 for a user and all users profile (wie es bei Win8 aussieht weiß ich allerdings nicht).
|
|
Äfan
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Sa 07.03.15 18:00
Es liegt im Autostart des jeweiligen Benutzers. Laut Taskmanager läuft es auch unter dem jeweiligen Benutzer.
Dachte auch erst, dass evtl. nicht der eigentliche Benutzer das Programm ausführt.
Die Verzeichnisse sind bei Win 7 und Win 8.1 identisch. Kann das Problem auch auf beiden Systemen reproduzieren.
Hatte auch schon den Virenscanner im Verdacht, nach kompletter Löschung besteht das Problem aber weiterhin.
|
|
Th69
      

Beiträge: 4796
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: So 08.03.15 14:44
Da gibt es dann wohl nur zwei Möglichkeiten:
1. das Fenster als TopMost setzen
2. Force Window to Front (not blink in taskbar) (müsstest du entsprechend nach C# bzw. p/Invoke konvertieren)
PS: Bitte Crossposts (entsprechend unserer Richtlinien) immer angeben: myCSharp.de - Anwendung im Autostart startet Explorer nur im Hintergrund
|
|
Äfan
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: So 08.03.15 17:55
Zu 1:
Du meinst mein Fenster als TopMost setzen oder das Explorer-Fenster?
Habe hier noch gefunden, dass ich das Explorer-Fenster evtl. meinem Fenster als Parent zuweisen muss.
stackoverflow.com/qu...st-window-csharp-wpf
Zu 2:
Das mache ich im Prinzip doch aktuell schon.
C#-Quelltext 1: 2: 3:
| ShowWindow(process.Handle, SW_RESTORE); SetForegroundWindow(process.Handle); BringWindowToTop(process.Handle); |
|
|
Th69
      

Beiträge: 4796
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: So 08.03.15 19:33
Hallo,
zu 1: ich meinte schon dein eigenes Fenster
zu 2: Zitat: | The key to this whole thing is the AttachThreadInput API calls. |
(hier die p/Invoke-Deklaration: AttachThreadInput)
|
|
Äfan
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: So 08.03.15 21:07
Oh mist, das habe ich übersehen. OK, werde das morgen gleich mal ausprobieren.
Evtl. funktioniert das ja  Danke erstmal für die Hilfe.
|
|
Äfan
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mo 09.03.15 22:37
TopMost hat leider keine Veränderung gebracht.
Auch das "Force Window to Front" konnte nicht wirklich Veränderung bringen.
Ich habe es so wie in dem verlinkten Beispiel probiert und dann noch 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:
| [DllImport("User32.dll")] static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("User32.dll")] public static extern int AttachThreadInput(IntPtr idAttach, IntPtr idAttachTo, bool fAttach);
[DllImport("User32.dll")] public static extern IntPtr GetForegroundWindow();
[DllImport("User32.dll")] public static extern IntPtr GetWindowThreadProcessId(IntPtr hwnd, IntPtr lpdwProcessId);
public static void SetForegroundWindowEx(IntPtr hndl) { IntPtr threadID1 = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero); IntPtr threadID2 = hndl;
if (threadID1 == threadID2) { SetForegroundWindow(hndl); } else { AttachThreadInput(threadID2, threadID1, true); SetForegroundWindow(hndl); AttachThreadInput(threadID2, threadID1, false); } }
protected void RunExplorer() { Thread.Sleep(3000);
SetForegroundWindowEx(this.hwnd); using (Process process = new Process()) { process.StartInfo.FileName = "explorer"; process.StartInfo.Arguments = "/e"; process.Start(); SetForegroundWindowEx(GetWindowThreadProcessId(IntPtr.Zero, (IntPtr)process.Id));
} } |
Das juckt den Explorer aber irgendwie gar nicht.
Bin echt überfragt, warum das nicht hinhaut. Evtl. hab ich das auch falsch verwendet?
|
|
Th69
      

Beiträge: 4796
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Di 10.03.15 20:55
Warum hast du WaitForInputIdle wieder aus dem Code herausgenommen? Solange die Anwendung nicht gestartet ist (und bereit ist für Eingaben), solange kann das Fenster auch nicht angesprochen werden (Stichwort: Message-Loop).
Kann aber auch generell am Explorer liegen (da dieser ja eine spezielle Systemanwendung ist).
Evtl. mal für den Explorer unter "Ordneroptionen"/"Ansicht" die Einstellung "Ordnerfenster in einem eigenen Prozess starten" aktivieren.
|
|
Äfan
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 11.03.15 00:14
Da hast du natürlich recht. Das habe ich durch das ganze rumgeteste rausgeworfen und nicht mehr bedacht. Probier ich morgen gleich nochmal.
Message-Loop sagt mir was. Mir ist das schon mal in einem anderen Projekt mit WindowMessages untergekommen.
Wo ich mir unsicher bin, ist die Übergabe des Handles bzw. das ermitteln der Thread-ID's.
Ist das so überhaupt korrekt? Hätte ja noch process.Handle zur Auswahl.
Das Beispiel habe ich von hier: dotnet-snippets.de/s...esktops-bringen/1005
Da wird die Form übergeben und davon das Handle verwendet. Ich denke, da ist meine process.Id (Zeile 42) ist eh falsch und process.Handle korrekt?
Habe hier bisschen mit dem Process Monitor versucht die Handles zu verfolgen und konnte zumindestens das Fenster im Vordergrund anhand des Handles finden.
Der Handle stellt doch den Hauptthread des jeweiligen Prozesses dar, oder? Finde dazu immer nur den Begriff "Window Handle", es stand im Proess Monitor aber als "Thread-ID" drin, wenn ich jetzt nicht völlig irre.
Den Explorer als eigenen Prozess zu starten ist schon aktiv.
|
|
Äfan
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Mi 11.03.15 18:44
Ich habe jetzt noch ein bisschen Probiert und ein grundsätzliches Problem gefunden:
Wenn ich den Explorer über ein Process-Objekt starte, habe ich einen Prozess, der den eigentlichen Explorer startet, sich selber aber sofort wieder Beendet.
Dadurch habe ich keinen gültiges Handle mehr.
Über folgenden Code komme ich an den eigentlichen Prozess. Nicht schön, aber ich wüsste nicht wie sonst.
Verwende ich hier das MainWindowHandle es Explorer-Prozesses, funktioniert es auch.
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:
| [DllImport("User32.dll")] static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("User32.dll")] public static extern int AttachThreadInput(IntPtr idAttach, IntPtr idAttachTo, bool fAttach);
[DllImport("User32.dll")] public static extern IntPtr GetForegroundWindow();
[DllImport("User32.dll")] public static extern IntPtr GetWindowThreadProcessId(IntPtr hwnd, IntPtr lpdwProcessId);
[DllImport("user32.dll")] static extern bool AllowSetForegroundWindow(int dwProcessId);
public void SetForegroundWindowEx(IntPtr hwnd) { IntPtr threadForeground = GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero); IntPtr threadExplorer = hwnd;
if (threadForeground == threadExplorer) { SetForegroundWindow(threadExplorer); } else { AttachThreadInput(threadExplorer, threadForeground, true); SetForegroundWindow(threadExplorer); AttachThreadInput(threadExplorer, threadForeground, false); } }
public Process GetExplorerProcess() { var all = Process.GetProcessesByName("explorer"); Process process = null; foreach (var p in all) { if (process == null || p.StartTime > process.StartTime) process = p; } return process; }
protected void OpenExplorer(string path, string args) { int currentPID = Process.GetCurrentProcess().Id; IntPtr currentHwnd = Process.GetCurrentProcess().MainWindowHandle;
AllowSetForegroundWindow(currentPID); SetForegroundWindowEx(currentHwnd);
using (Process process = new Process()) { process.StartInfo.FileName = "explorer"; process.StartInfo.Arguments = "/e," + args + path; process.Start(); process.WaitForExit(5000);
Thread.Sleep(3000); Process processEx = this.GetExplorerProcess();
if (processEx.MainWindowHandle != IntPtr.Zero) { AllowSetForegroundWindow(processEx.Id); SetForegroundWindowEx(processEx.MainWindowHandle); } } } |
In Windows 8.1 scheint das soweit alles schön zu funktionieren, auch wenn es nicht wirklich toll ist.
Bei Windows 7 klappt es aber wieder nicht mehr, bzw. nur sporadisch.
Solange mein Tool nach dem Autostart im Tray liegt, kommen auch die Explorer-Fenster in den Hintergrund.
Erst wenn ich mein Tool in den Vordergrund hole, sind auch nachfolgende Explorer-Fenster im Vordergrund.
Starte ich mein Tool nicht im Autostart, sondern per Hand und nach dem Internet-Browser, dann kommen neue Explorer-Fenster immer in den Vordergrund, auch wenn mein Tool im Tray liegt. Hier allerdings auch nur direkt beim öffnen. Das nach vorne Rufen nach dem Sleep klappt hier nicht, wenn ich vorher schnell ein anderes Fenster in den Vordergrund rufe.
Bin überfragt, wie und ob ich sowas überhaupt irgendwie lösen kann.
Infos zu dem Verhalten habe ich hier gefunden, was ich soweit nachvollziehen kann: blogs.msdn.com/b/old...9/02/20/9435239.aspx
Das verlinkte Beispiel habe ich ja oben schon so eingebaut: blogs.msdn.com/b/old...8/08/01/8795860.aspx
Einzige Möglichkeit das global zu umgehen, wäre den ForegroundLockTimeout über die Registry auf 0 zu setzen ( technet.microsoft.co...brary/cc957208.aspx)
Das klappt offenbar und behebt das Problem komplett. Das ist aber keine tolle Lösung, da was in der Registry zu verändern, zumal das ja für alle Fenster gilt. Wäre Murks.
Bin ratlos 
|
|
Th69
      

Beiträge: 4796
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Do 12.03.15 15:06
Ich würde dir gerne noch weiterhelfen, weiß aber jetzt nicht wie?!
Einzig was mir auffällt - und was du selber ja auch geschrieben hast - daß du noch ein wenig mit Prozess- und Window-Handles durcheinanderkommst.
Bei AttachThreadInput mußt du ja die Prozess-Id übergeben, aber du benutzt das Window-Handle (auch wenn du die Variable threadExplorer genannt hast) und die ersten beiden Parameter scheinst du vertauscht zu haben (zumindestens im Vergleich zu dem Code aus meinem verlinkten Artikel).
Ich weiß, daß die Benutzung der WinAPI nicht ganz einfach ist, aber wenn du so spezielle Anforderungen an dein Programm hast, dann mußt du da durch und die einzelnen Funktionen verstehen. Fast jede WinAPI-Funktion bietet als Rückgabewert (oder per GetLastError()) die Möglichkeit einen Fehlercode, denn du dann abfragen kannst. Per P/Invoke dann einfach als Attribut SetLastError=true setzen, s.a. Calling Win32 DLLs in C# with P/Invoke.
Viel Erfolg noch und gib nicht auf!
|
|
Äfan
Ehemaliges Mitglied
Erhaltene Danke: 1
|
Verfasst: Do 12.03.15 22:28
Danke für die ausführliche Antwort.
Ich habe die Funktionen alle im grob mal in der Doku nachgelesen, z.B. hier www.pinvoke.net/defa...32.attachthreadinput und dann im MSDN.
Bin da aber tatsächlich etwas unbewusst damit umgegangen und so auch die vertauschten Parameter bei AttachThreadInput. Ist mir selber nicht aufgefallen.
Mir fehlt da noch ein wenig die Routine auf dem Gebiet  Komme eigentlich aus der Webentwicklung.
Zitat: | Bei AttachThreadInput mußt du ja die Prozess-Id übergeben |
Bist du sicher, dass hier die Prozess-ID gefordert ist? Die verwendete Überladung von GetWindowThreadProcessId müsste doch die Thread-ID liefern und nicht die Process-ID?
Siehe hier: www.pinvoke.net/defa...indowthreadprocessid
Das Handle zu verwenden ist aber natürlich Quatsch. Lag am Unverständnis von Handle/Thread-ID/Process-ID.
Ich habe jetzt aber eine Lösung gefunden, die Funktioniert. Siehe hier: stackoverflow.com/a/22737820
Der ganze Trick liegt in der Funktion SetWindowPos und diese tut das, was ich möchte.
Hier sieht man auch nochmal, dass hier mit den Thread-ID's gearbeitet wird, wenn ich das richtig verstehe.
Das Ermitteln des gestarteten Explorer-Prozess habe ich nun auch etwas angepasst, da mir das Sleep zu unsicher war.
Damit geht es deutlich besser:
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:
| public static Process GetLastStarted() { Process[] processes = Process.GetProcessesByName("explorer"); Process process = null;
foreach (Process p in processes) { if (process == null || p.StartTime > process.StartTime) process = p; }
try { int count = 0;
while (process.MainWindowHandle == IntPtr.Zero && count < 30) { Thread.Sleep(100); process.Refresh(); count++; } if(count >= 30) return null; return process; } catch { }
return null; } |
Damit habe ich scheinbar alles, was ich eigentlich wollte
Meine Tests waren jedenfalls erfolgreich.
Werde mir jetzt noch ein bisschen PInvoke zur Gemüte führen und versuche da einfach noch bisschen mehr Routine zu bekommen.
Hatte das schon mal in Verbindung mit der Nutzung der Zwischenablage, aber das ist auch schon Jahre her.
Danke jedenfalls für die Hilfe, Geduld und die ganzen Infos 
|
|
Th69
      

Beiträge: 4796
Erhaltene Danke: 1059
Win10
C#, C++ (VS 2017/19/22)
|
Verfasst: Fr 13.03.15 11:22
Hallo,
ja, du hast Recht mit der Thread-Id (anstatt Process-Id) - aber eben kein Windows-Handle.
Freut mich, daß du jetzt eine Lösung gefunden hast.
Ich verstehe nur nicht, warum du diese Sleep()-Schleife benutzt, anstatt WaitForInputIdle()?
|
|
|