PDA

View Full Version : How to show a message to user when app crashes



INeedADollar
10th September 2019, 12:03
Hello! I want to show a QMessageBox when my app crashes, but I don't know how to handle the crash if it happens. I know that the best sollution of this is to make my code very well, avoiding the crash, but I want to show the message even if a crash happens. Thank you!

d_stranz
10th September 2019, 16:45
Hello! I want to show a QMessageBox when my app crashes, but I don't know how to handle the crash if it happens.

The usual methods are:


Use try/catch clauses around areas of the code that could throw an exception, making sure your catch() clauses include the right exceptions
Add a signal handler. There is an example here. (http://www.csl.mtu.edu/cs4411.ck/www/NOTES/signal/install.html)

Generally a crash occurs due to a segmentation fault (SIGSEGV (https://stackoverflow.com/questions/1564372/what-causes-a-sigsegv)) because your code has made an invalid memory access. You usually cannot fix such a crash at runtime, because once memory is corrupted, there isn't much you can do except halt the program.

Whether you will be able to display a QMessageBox or not depends on how broken your program is. If the code has corrupted some critical section needed by Qt's event loop or other functions, trying to display a message might just cause more faults.


I know that the best sollution of this is to make my code very well, avoiding the crash

Yep. Learn how to program defensively:


Check all return values from functions that return some kind of status that indicates whether they succeeded or not
Check any pointers for valid values (eg. non-null) before you use them, especially pointers returned from function calls. Don't assume that when a method returns a pointer you can just use it without checking.
Check array indexes before using them to make sure you don't try to access memory that is out of range of your array
Add assert() statements to your code to check that variables, pointers, etc. have valid values.
Always, always make a debug version of your program and run it in the debugger. Learn to use the debugger output to backtrack where the error occurred and fix it.
If you are writing code that others will use (i.e. a library), document the methods in your classes to help others avoid mistakes. In particular, if methods must be called in a certain order or variables initialized, make sure that is clear in your comments.

INeedADollar
10th September 2019, 19:10
The usual methods are:


Use try/catch clauses around areas of the code that could throw an exception, making sure your catch() clauses include the right exceptions
Add a signal handler. There is an example here. (http://www.csl.mtu.edu/cs4411.ck/www/NOTES/signal/install.html)

Generally a crash occurs due to a segmentation fault (SIGSEGV (https://stackoverflow.com/questions/1564372/what-causes-a-sigsegv)) because your code has made an invalid memory access. You usually cannot fix such a crash at runtime, because once memory is corrupted, there isn't much you can do except halt the program.

Whether you will be able to display a QMessageBox or not depends on how broken your program is. If the code has corrupted some critical section needed by Qt's event loop or other functions, trying to display a message might just cause more faults.



Yep. Learn how to program defensively:


Check all return values from functions that return some kind of status that indicates whether they succeeded or not
Check any pointers for valid values (eg. non-null) before you use them, especially pointers returned from function calls. Don't assume that when a method returns a pointer you can just use it without checking.
Check array indexes before using them to make sure you don't try to access memory that is out of range of your array
Add assert() statements to your code to check that variables, pointers, etc. have valid values.
Always, always make a debug version of your program and run it in the debugger. Learn to use the debugger output to backtrack where the error occurred and fix it.
If you are writing code that others will use (i.e. a library), document the methods in your classes to help others avoid mistakes. In particular, if methods must be called in a certain order or variables initialized, make sure that is clear in your comments.


Thank you very much! I found out how to handle the crash, but I need some help with displaying the QMessageBox after app closes. How can I achieve that?
My code:


#include <QApplication>
#include "mainwindow.h"
#include <QMessageBox>
#include <signal.h>

void signalHandler(int signum){
signal(signum, SIG_DFL);
qDebug() << "App crashed";
QMessageBox::critical(nullptr, "App crashed", "Application has unexpectedly close! Try restarting the application.");
}


int main(int argl,char *argv[])
{
Q_INIT_RESOURCE(resources);
signal(SIGSEGV, signalHandler);
QApplication app(argl,argv);
mainWindow *window = new mainWindow();
window->setWindowTitle("Test");
window->setFixedSize(700, 400);
window->show();

return app.exec();
}


In my code, the QMessageBox appears first, and then the app is closed. I want to close the mainWindow or making it dissapear and the displaying the QMessageBox.

d_stranz
10th September 2019, 20:58
You will probably need to do something like the following:

In main(), call QApplication::setQuitOnLastWindowClosed() with the value false before you show the mainWindow. This is to prevent what happens in the signal handler (see below) from causing the app to quit too soon.

In your signal handler, call QApplication::closeAllWindows() first, using the global qApp pointer.
Next, display your QMessageBox.
When the user closes the message box, call QApplication::quit() and then exit the handler.

You might also need to add a call to QApplication::quit() after app.exec() in main, but probably not since main() exits at that point anyway.

Another option would be to define your mainwindow pointer as a global variable in main.cpp so you can get to it from your signal handler. The all you have to do is call mainwindow->hide() from inside the handler before you display your message box. You should probably also call QApplication::quit() after the message box is closed so Qt can have a chance to clean up instead of just crashing out.

INeedADollar
11th September 2019, 17:27
You will probably need to do something like the following:

In main(), call QApplication::setQuitOnLastWindowClosed() with the value false before you show the mainWindow. This is to prevent what happens in the signal handler (see below) from causing the app to quit too soon.

In your signal handler, call QApplication::closeAllWindows() first, using the global qApp pointer.
Next, display your QMessageBox.
When the user closes the message box, call QApplication::quit() and then exit the handler.

You might also need to add a call to QApplication::quit() after app.exec() in main, but probably not since main() exits at that point anyway.

Another option would be to define your mainwindow pointer as a global variable in main.cpp so you can get to it from your signal handler. The all you have to do is call mainwindow->hide() from inside the handler before you display your message box. You should probably also call QApplication::quit() after the message box is closed so Qt can have a chance to clean up instead of just crashing out.

Thank you very, very much! The second method worked very well. You are the best!

d_stranz
11th September 2019, 17:35
The second method worked very well. You are the best!

It may not work so well if you have a modal dialog window open when a SIGSEGV happens - hiding the main window may not hide the dialog.

You could test this by adding a QDialog-based class to your app and override some event (like showEvent()) for the class. In that event, call "raise( SIGSEGV )" to trigger a segmentation fault signal, which should go into your handler. If hiding the main window also hides the dialog, all is good.

INeedADollar
12th September 2019, 19:01
It may not work so well if you have a modal dialog window open when a SIGSEGV happens - hiding the main window may not hide the dialog.

You could test this by adding a QDialog-based class to your app and override some event (like showEvent()) for the class. In that event, call "raise( SIGSEGV )" to trigger a segmentation fault signal, which should go into your handler. If hiding the main window also hides the dialog, all is good.

I have a problem. I cannot do this because QDialog also has a function named raise() but without any arguments and I cannot call the function raise(SIGSEGV) from signal.h
Do you know how can I call it without any error?

P.S. the error is "too many arguments for function call". I think the compiler confuses the functions.

d_stranz
12th September 2019, 22:06
Do you know how can I call it without any error?

You need to remember your C++ basics. To call a non-member function, eg. one in the global namespace, you use the global scope qualifier:



::raise( SIGSEGV );

INeedADollar
13th September 2019, 13:26
You need to remember your C++ basics. To call a non-member function, eg. one in the global namespace, you use the global scope qualifier:



::raise( SIGSEGV );


Thank you very much! Not all windows closed when signal was sent, but I wrote this part inside signalHandler() function before showing the QMessageBox:


const QWidgetList topLevelWidgets = QApplication::topLevelWidgets();
for (QWidget *widget : topLevelWidgets) {
widget->hide();
}


Now it is working very well. Thank you again!

d_stranz
13th September 2019, 16:39
Great! Now start practicing defensive programming and debug your programs before you release them so you will never have a need for this kind of message.

If you want to make this kind of signal handler really useful instead of just displaying a message like "Oops, something broke and there is nothing you can do to fix it", you would implement something in your QApplication class that could save any open files and basically try to do whatever is possible to clean up what the user was doing and leave it in a recoverable state.

Just displaying a message and then crashing out isn't much different from simply crashing out as far as the user is concerned if their work is trashed and they lose it all.

INeedADollar
13th September 2019, 19:23
Great! Now start practicing defensive programming and debug your programs before you release them so you will never have a need for this kind of message.

If you want to make this kind of signal handler really useful instead of just displaying a message like "Oops, something broke and there is nothing you can do to fix it", you would implement something in your QApplication class that could save any open files and basically try to do whatever is possible to clean up what the user was doing and leave it in a recoverable state.

Just displaying a message and then crashing out isn't much different from simply crashing out as far as the user is concerned if their work is trashed and they lose it all.

What a great idea! You are right, it is much better when the app saves your progress automatically If it crashes. Thank you for reminding me of this!

cyberopsinfosec
17th September 2019, 07:35
Hello,

I can't find correct solution still.

Thanks & Regards

Cyberops Infosec LLP

d_stranz
17th September 2019, 19:28
I can't find correct solution still.

What is your question? If you are trying to implement something in the same way as INeedADollar is doing, then there is more than enough information in the code and advice that has been posted in response to his questions for you to do the same thing.