unterschiedliches Schließverhalten bei unsichtbarer App im Traybereich

  • VB.NET

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von VaporiZed.

    unterschiedliches Schließverhalten bei unsichtbarer App im Traybereich

    Hallo zusammen.

    Vorgeschichte: Manche Apps von mir sind für den dauerhaften Betrieb nötig, sodass ich verhindern will, dass jemand sie über das [X] versehentlich schließt. Stattdessen sollen sie nur in den Traybereich verschwinden, genauso wenn man auf Minimieren klickt (also: klickt man auf Minimieren [_], verschwindet die App auch im Traybereich). Das tatsächliche Schließen der App geht dann nur über das TrayIcon. Im Anhang ist eine Beispiel-App, Rechtsklick auf das TrayIcon beendet das Programm.

    Meine Aufgabe: Ich will solch minimierte Apps zusätzlich durch ein anderes Programm schließen können, muss also der minimierten App ein Signal geben, dass sie sich schließen soll. Ein Kill ist zu heftig, da dann die App nicht sauber schließt und ggf. Daten verloren gehen.

    Das Problem: Schicke ich WM_CLOSE an die Ziel-App, reagiert diese darauf oder eben nicht. Wird die App nämlich per Minimieren in den Traybereich geschickt, reagiert die App auch auf WM_CLOSE. Wird sie hingegen per Klick auf [X] minimiert, reagiert die App nämlich nicht auf WM_CLOSE, auch wenn danach die App wieder angezeigt und dann per Klick auf [_] minimiert wird. Beim Klick auf [X] scheint also was zu geschehen, was das spätere, externe Schließen per WM_CLOSE-Versand unterbindet. Nur was? Und wie kann man das verhindern oder rückgängig machen?

    Der Schließen-Beispiel-Code:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Friend Class FrmMain
    2. <DllImport("user32.dll", CharSet:=CharSet.Auto)> Private Shared Function FindWindow(className As String, windowName As String) As IntPtr : End Function
    3. <DllImport("user32.dll", CharSet:=CharSet.Auto)> Private Shared Function SendMessage(hWnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr : End Function
    4. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    5. Dim NameOfProcess = "WinFormsVbNetFx"
    6. Dim TitleOfForm = "Hauptformular"
    7. Dim CountBeforeKill = Diagnostics.Process.GetProcessesByName(NameOfProcess).Length
    8. If CountBeforeKill = 0 Then MessageBox.Show("ZielApp läuft nicht") : Return
    9. Dim WindowHandle = FindWindow(Nothing, TitleOfForm)
    10. If WindowHandle = IntPtr.Zero Then MessageBox.Show("ZielApp läuft nicht") : Return
    11. Dim WM_CLOSE = &H10
    12. Dim WM_QUIT = &H12
    13. Dim WM_DESTROY = &H2
    14. For Each Message In {WM_CLOSE, WM_QUIT, WM_DESTROY}
    15. SendMessage(WindowHandle, Message, IntPtr.Zero, IntPtr.Zero)
    16. Threading.Thread.Sleep(1000)
    17. Dim CountAfterKill = Diagnostics.Process.GetProcessesByName(NameOfProcess).Length
    18. If CountBeforeKill > CountAfterKill Then MessageBox.Show(Message.ToString) : Close() : Return
    19. Next
    20. MessageBox.Show("das Schließen hat gar nicht funktioniert")
    21. End Sub
    22. End Class


    Das Setzen von ControlBox auf False und so das Entfernen der entsprechenden Boxen ist natürlich eine Option, aber ich will hier die Abläufe verstehen.
    Dateien
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    @VaporiZed Du kannst per RegisterWindowMessage() eine Message registrieren und eine solche dann vom anderen Programm per PostMessage(HWND_BROADCAST, ...) senden,
    die wird dann nur von diesem richtig verstanden und augeführt.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich könnt mir auch ein eigenes Form machen, welches oben rechts nur den send-to-tray-Button hat. Aber das beantwortet leider auch nicht meine WM_CLOSE-Frage.

    Allerdings habe ich gerade festgestellt, dass ich das Problem beheben kann, indem ich WM_QUIT per PostMessage statt mit SendMessage an die App schicke. Dann klappt es auch mit der per [X] minimierten App.

    Die ursprüngliche Frage bleibt aber.

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    VaporiZed schrieb:

    Die ursprüngliche Frage bleibt aber.
    Wie kann ich das reproduzieren?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich kann mich nur wiederholen: Beispiel-App starten und in VS oder einer EXE den Schließcode aus dem Post#1-Spoiler anwenden.

    Da sich nun mehrere Konstellationen ergeben, habe ich auch in der zu schließenden App mal die CloseReasons im FormClosing-EventHandler geloggt:

    Quellcode

    1. Form wird über den Minimieren-Button in den Traybereich geschickt
    2. SendMessage WM_CLOSE -> TaskManagerClosing, daher wird App geschlossen
    3. SendMessage WM_QUIT -> (keine Reaktion)
    4. PostMessage WM_CLOSE -> TaskManagerClosing, daher wird App geschlossen
    5. PostMessage WM_QUIT -> App wird ohne CloseReason geschlossen
    6. Form wird über den Schließen-Button in den Traybereich geschickt
    7. SendMessage WM_CLOSE -> -> UserClosing, daher wird App NICHT geschlossen
    8. SendMessage WM_QUIT -> -> (keine Reaktion)
    9. PostMessage WM_CLOSE -> -> UserClosing, daher wird App NICHT geschlossen
    10. PostMessage WM_QUIT -> -> App wird ohne CloseReason geschlossen

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    VaporiZed schrieb:

    Ich könnt mir auch ein eigenes Form machen, welches oben rechts nur den send-to-tray-Button hat.
    Alternativ könntest Du auch nur den Close-Button in der Titelbar deaktivieren So das Du nur noch Minimize bzw Maximize der Form nutzen kannst (inkl. SystemMenü wenn man auf das Icon in der Titelbar klickt).

    Auf die schnelle für VB6.

    Visual Basic-Quellcode

    1. Option Explicit
    2. Private Const MF_BYPOSITION As Long = &H400&
    3. Private Const MF_REMOVE As Long = &H1000&
    4. Private Const WM_CLOSE As Long = &H10
    5. Private Declare Function GetSystemMenu Lib "user32" ( _
    6. ByVal hwnd As Long, _
    7. ByVal bRevert As Long) As Long
    8. Private Declare Function GetMenuItemCount Lib "user32" ( _
    9. ByVal hMenu As Long) As Long
    10. Private Declare Function RemoveMenu Lib "user32" ( _
    11. ByVal hMenu As Long, _
    12. ByVal nPosition As Long, _
    13. ByVal wFlags As Long) As Long
    14. Private Declare Function DrawMenuBar Lib "user32" ( _
    15. ByVal hwnd As Long) As Long
    16. Private Declare Sub SendMessage Lib "user32" _
    17. Alias "SendMessageA" ( _
    18. ByVal hwnd As Long, _
    19. ByVal wMsg As Long, _
    20. ByVal wParam As Long, _
    21. lParam As Any)
    22. Private Sub Form_Load()
    23. Dim hMenu As Long
    24. Dim nCount As Long
    25. hMenu = GetSystemMenu(Me.hwnd, 0)
    26. nCount = GetMenuItemCount(hMenu)
    27. Call RemoveMenu(hMenu, nCount - 1, MF_REMOVE Or MF_BYPOSITION)
    28. Call RemoveMenu(hMenu, nCount - 2, MF_REMOVE Or MF_BYPOSITION)
    29. Call DrawMenuBar(Me.hwnd)
    30. End Sub
    31. Private Sub Command1_Click()
    32. Call SendMessage(Me.hwnd, WM_CLOSE, ByVal 0&, ByVal 0&)
    33. End Sub

    Der einzige Unterschied zwischen SendMessage und PostMessage, der mir bekannt ist, ist: SendMessage arbeitet synchron und PostMessage asynchron.
    Mfg -Franky-
    Ich hab versucht, den Code nach VB.NET zu übersetzen. Der [X]-Button wird deaktiviert, wie geschrieben. Der Code hat mich vermuten lassen, dass er verschwindet. War aber ne falsche Vermutung.

    Ah, jetze. Durch Löschen der Menüeinträge im AppIcon-Systemmenü wird die Funktionalität beschnitten. Jetze.

    ##########

    Dann werd ich das provisorisch (was bekanntlich am längsten hält) so machen: Ich schließe die App auch bei CloseReason.UserClosing plus Form ist minimiert. Denn das ist ja bei Klick auf [X] definitiv nicht zutreffend. Und kann somit bei dem Szenario nur sein, wenn WM_CLOSE von Außen kommt.

    VB.NET-Quellcode

    1. If e.CloseReason <> CloseReason.UserClosing OrElse WindowState = FormWindowState.Minimized OrElse AppClosingIsDesired Then Return

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()