QT - Wenn PC heruntergefahren wird, Slot ausführen



  • Hallo zusammen,
    bin ganz neu hier im Forum und hoffe es kann mir jemand helfen.

    Ich habe ein Programm welches via UDP-Anfrage nach einem bestimmten "Arduino" im Netzwerk sucht, wird dieser gefunden baue ich eine TCP-Verbindung auf und kommuniziere so mit diesem. Das funktioniert auch soweit alles.

    Jetzt geht es aber darum, wenn das Programm läuft und der PC heruntergefahren wird, soll zuvor noch ein Befehl via TCP gesendet werden um den Arduino in Standby zu schalten.

    Ich habe bereits versucht "closeEvent" zu überschreiben:

    void MainWindow::closeEvent(QCloseEvent *event)
    {
        TcpWrite("Standby");
        event->accept();
    }
    

    -> Wird das Programm normal beendet wird auch der Befehl gesendet, jedoch nicht beim Herunterfahren des PC's

    Desweiteren habe ich versucht über "aboutToQuit" den Slot "close" auszuführen:

    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        MainWindow window;
        window.show();
    
        QObject::connect(&app, SIGNAL(aboutToQuit()), &window, SLOT(close()));
    
        return app.exec();
    }
    

    -> Der Gedanke dabei war es, kommt das Signal "aboutToQuit" (laut Doku, wenn der PC Heruntergefahren wird) führe ich den Slot "close" (also auch mein closeEvent) aus.
    -> funktioniert beim normalen schließen über "X" auch, jedoch nicht beim Herunterfahren

    Ich hoffe man kann mir in etwa folgen und vielleicht weiß jemand eine Lösung

    Gruß
    Dominik



  • Das wird nicht so einfach sein ... weil jeder sich da anders verhält.

    Windows: es gibt ne Nachricht WM_ENDSESSION bzw WM_QUERYENDSESSION
    darauf kann dein Prog hören, muss aber nicht ...
    Wenn es nicht hört ... kriegt es WM_CLOSE, wenn es dann immer noch nicht kooperiert, kommt kill signal.

    Linux:
    Standard, ohne GUI ... was beim shutdown noch läuft kriegt nen SigTerm, nach ner Weile SigKill .

    Mit Glück:
    Dein system unterstützt DBUs, der sollte lustig signale umherschicken beim closen. Keine Ahnung ob die Standardisiert sind.
    KDE und Gnome nutzen das eh glaub ich ...

    Mit GUI:
    XWindows/MIR ... keine Ahnung, das ding sendet auch messages, Keine Ahnung ob da nen Session close dabei ist. Thoerethisch sollten aber sogar aps das Sterben des XWindows überleben können, von daher vielleicht unwahrscheinlich das garantiert vorm session close was abbekommst.

    Idee 1.
    Nen Daemon / dienst statt nen "Normalen" Programm. Einschalten kann man Usergesteuert uber IPC machen, Das kannst mit nem normalen Prog dann ansteuern.
    Und ausschalten geht ueber systemshutdown, bevor netz runterfährt natürlich.

    Idee 2.
    mem GUI Programm was sich im Tray einnistet und mit der Usersession im windowmanager lebt. Normal muesste der XServer vorm Netz gekillt werden. Iss aber für KDE/Gnome/Windows unnerschiedlich. WObei Qt Tray anbindung bietet, die auch unter windows geht, in KDE Sowieso ... wie es mit Gnome aussieht, ka.

    Idee 3.
    Keine Ahnung was der Ardino für ne Netzwerkkarte hat, was die kann ...
    Aber router/FW davor, der die ganzen 0815 Broadcasts blockt, so das wirklich nur gezielte Ethernet frames bei dem ankommen.
    und WOL / einschlafen passend configurieren ...

    Ciao ...



  • Ich bin mir nicht sicher ob das funktioniert, aber hast du mal versucht eine Klasse von QApplication abzuleiten und den Code im Destruktor aufzurufen?



  • hast du mal versucht eine Klasse von QApplication abzuleiten und den Code im Destruktor aufzurufen?

    funktioniert beim normalen schließen über "X" auch, jedoch nicht beim Herunterfahren

    Das ist doch sein Problem ...

    DIe meisten Desktop Apps "brauchen" das shutdown verhalten nicht, deshalb reagieren sie auf die Signale des OS nichts. Ne App die noch offen ist wenn der WM closed, zumindest unter windows, die macht nix produktives mehr sondern die wird einfach gekillt.
    Wenn das kill kommt, ists eh zu spät, weil das system "parallel" heruntergefahren wird, d.h. ob der Netzwerk-Stack noch vorhanden ist, ist gar ned garantiert.
    Du wirst nicht mal richtig "testen" können, ob dein DTor aufgerufen wird, weil Deine App und Monitor/Debugger gleichzeitig sterben 🙂

    D.h. er muss vorher reagieren und sich selbst sauber runterfahren, und das geht nur ueber "Signale" oder Nachrichten vom OS ....

    Ciao ...



  • ~~[code]
    #ifndef APPLICATION_H
    #define APPLICATION_H

    #include <QApplication>

    class Application : public QApplication
    {
    public:
    Application(int &argc, char **argv);
    ~Application();
    };

    #endif // APPLICATION_H
    [/code]

    [code]
    #include "application.h"
    #include <QDir>

    Application::Application(int &argc, char **argv) : QApplication(argc, argv)
    {
    }

    Application::~Application()
    {
    QDir dir;
    dir.mkpath("C:/Users/shold/Documents/xyy");
    }
    [/code]

    Mal schnell gebastelt und ausprobiert. Der Destruktor erstellt hier einfach nur ein Verzeichnis, ich sehe aber keinen Grund wieso man hier nicht auch noch einen TCP-Befehl senden könnte.

    Unter Windows habe ich das erfolgreich getestet.~~

    edit: Sorry funktioniert doch nicht, der Ordner war vorher schon da 🙄





  • Hallo zusammen,

    erst mal danke für eure Hilfe. 👍

    Dein Tipp:

    RHBaum schrieb:

    Windows: es gibt ne Nachricht WM_ENDSESSION bzw WM_QUERYENDSESSION
    darauf kann dein Prog hören, muss aber nicht ...
    Wenn es nicht hört ... kriegt es WM_CLOSE, wenn es dann immer noch nicht kooperiert, kommt kill signal.

    hat mir schon mal weitergeholfen! Folgendermaßen funktioniert es beim Herunterfahren

    Ich habe eine neue Klasse "MyEventFilter" erstellt:

    #ifndef MYEVENTFILTER_H
    #define MYEVENTFILTER_H
    
    #include <QAbstractNativeEventFilter>
    #include <QApplication>
    #include <windows.h>
    
    class MyEventFilter : public QAbstractNativeEventFilter
    {
    
    private:
        virtual bool nativeEventFilter(const QByteArray &eventType, void *pMessage, long *pResult);
    };
    
    #endif // MYEVENTFILTER_H
    

    myeventfilter.h

    #include "myeventfilter.h"
    
    bool MyEventFilter::nativeEventFilter(const QByteArray &eventType, void *pMessage, long *pResult)
    {
        const MSG &msg = *static_cast<MSG *>(pMessage);
    
        if(msg.message == ENDSESSION_CLOSEAPP)
        {
            qApp->quit();
            *pResult = 1;
            return true;
        }
        else return false;
    }
    

    myeventfilter.cpp

    und den Filter "installiert":

    #include "mainwindow.h"
    #include <QApplication>
    #include <QCoreApplication>
    #include "myeventfilter.h"
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        MainWindow window;
        window.show();
    
        //QObject::connect(&app, SIGNAL(aboutToQuit()), &window, SLOT(close()));
    
        MyEventFilter * pMyEventFilter = new MyEventFilter;
        app.setQuitOnLastWindowClosed(false);
        app.installNativeEventFilter(pMyEventFilter);
    
        return app.exec();
    }
    

    main.cpp

    Ich verstehe jetzt nur nicht warum ich "setQuitOnLastWindowClosed(false)" auf false setzen muss. Dass führt nämlich dazu, dass beim Herunterfahren die Nachricht zwar gesendet wird, wenn ich das Programm aber schließe der Task im Hintergrund weiterläuft und somit die Nachricht natürlich nicht raus geht.

    Setze ich es aber auf true, funktioniert zwar das Schließen wieder aber das Herunterfahren nicht mehr.

    Windows macht mich fertig 🙄

    Gruß Dominik



  • Ich hab nochmal experimentiert und die Methode QApplication::event() überschrieben:

    bool Application::event(QEvent *e)
    {
        if (e->type() == QEvent::Quit) {
            QDir dir;
            dir.mkpath("C:/Users/shold/Documents/xyy");
        }
    
        return QApplication::event(e);
    }
    

    Das hat jetzt funktioniert 🙂



  • Ich verstehe jetzt nur nicht warum ich "setQuitOnLastWindowClosed(false)" auf false setzen muss.

    setQuitOnLastWindowClosed ist ne komfort funktion der QT Message Queue.
    Wenn das aktiviert ist bedeutet:
    Sobald das letzte Window(Mainwindow, Dialog, Widget im Window mode) geschlossen ist, springt er aus der Message Loop raus.
    D.h. du kommst aus dem QApplication::exec() raus.
    Meist heisst das, dein Prozess beendet sich (muss aber nicht, mann kann auch nach dem exec noch zeugs machen)

    setQuitOnLastWindowClosed auf false bedeutet.
    Sind alle windows zu, bleibt der noch lustig in der QT Message Queue. DU musst Expliziet QApplication::quit() aufrufen um da rauszukommen.

    Nützlich für nonGui anwendungen die trotzdem die QT Message Quueue verwenden (Wegen Signal / Slot z.b.) oder GUI anwendungen die definiert zustände haben wo kein fenster offen ist (Aka kein Hauptfenster).

    Zu deutsch:
    ist setQuitOnLastWindowClosed true und du schliesst das letzte Fentser, geht er aus der Message queue raus.
    D.H. alles was du danach noch reinstellst wird nicht mehr ausgeführt.
    Achtung: Signal/Slot funktioniert ueber die Message queue. Sprich ist lestztes fenster zu, werden auch deine connections nimmer abgearbeitet.
    Und ich vermute dein Senden wird auf Signale Slots aufbauen ...

    Ciao ...



  • Nochmal Danke für eure Hilfe. 👍

    partsoft schrieb:

    Ich hab nochmal experimentiert und die Methode QApplication::event() überschrieben:

    bool Application::event(QEvent *e)
    {
        if (e->type() == QEvent::Quit) {
            QDir dir;
            dir.mkpath("C:/Users/shold/Documents/xyy");
        }
    
        return QApplication::event(e);
    }
    

    Das hat jetzt funktioniert 🙂

    Leider habe ich da wieder das gleiche Problem wie mit "closeEvent", beim Schließen funktioniert es zwar, beim Herunterfahren jedoch nicht.
    Hat es bei dir beim Herunterfahren funktioniert?

    Und hat vielleicht jemand ein Beispiel für den EventFilter?
    Ich habe ziemlich komische Reaktionen des Programmes, klicke ich zum Beispiel auf einen Eintrag in der "menubar" schließt sich mein Programm?!

    Gruß Dominik



  • Mein Beispiel funktioniert auch beim Herunterfahren. Ich habe dir das kleine Testprojekt mal hier hochgeladen:

    https://www.partsoft.de/jdownloads/shutdown/shutdown.zip

    Theoretisch kannst du das Programm "shutdown.exe" direkt ausführen, vorausgesetzt du legst vorher das Verzeichnis "C:/Users/shold/Documents" an und stellst sicher das es Schreibrechte hat.

    Ansonsten findest du den Quellcode im Ordner "src" und kannst das Projekt mit angepasstem Pfad selbst erstellen. Dann musst du die Datei zusammen mit allen benötigten DLLs in einen neuen Ordner kopieren und von dort ausführen (also nicht aus Qt Creator heraus starten). Die DLLs die ich in das Archiv gepackt habe sind für Qt 5.5.1

    Bitte teste das zeitnah und melde dich dann wieder hier, damit ich den Download dann wieder entferne (ist meine Firmenseite und dort hat er eigentlich nichts verloren).



  • Danke vielmals! 👍 😃

    Funktioniert bei mir auch. Dummer Fehler von mir: habe immer noch "QApplication" statt der überschriebenen Methode von "Applicatione" verwendet.^^



  • Schön das es jetzt geklappt hat 😉

    Den Download habe ich wieder entfernt.


Anmelden zum Antworten