QT deployed executable crashes on some computers
-
Finally! I found where the problem is. I received source code of the one of the libraries I use. And there is following code in the library:
programXRootDirectory = getenv("ProgramXROOT");
And yes, if the environmental variable doesn't exist, executable crashes. Strangely, there is no sign about the crash caused by
getenv
.I will keep this thread, maybe someone else has the same problem.
I would like to thank you everyone who tried to help me so far!
Update:
getenv
is deprecated and_dupenv_s
recommended instead. More information about _dupenv_s@Lati said in QT deployed executable crashes on some computers:
Strangely, there is no sign about the crash caused by getenv.
Well, it looks like the culprit isn't getenv() but the way the return value from that function is used within the DLL later on. From man getvenv:
The getenv() function returns a pointer to the value in the environment, or NULL if there is no match.
so
programXRootDirectory
becomes NULL when the variable is not set, and the world ends... -
Ok. I decided to install the Qt on one of the computer where executable crashes. I copied the whole development folder and build the application. I can build the application in Debug and Release without any problem. However, when I try to start the debug with F5, I get following error:
Please note that Qt is installed on D drive. I tried few solutions offered by Google, like copying the Qt dll's in the debug folder which didn't help. I feel very desperate.
@hskoglund
Pc's are a bit far from each other :) I use remote desktop most of the time.@fcarney
I checked that, it is v 5.12.1. I don't know how to be sure which version it should be.@Lati
That file error screenshot is inQt5Cored.dll
. Note thed
at the end of the name. That is the version compiled for debug (plus the message). Why are you deploying debug DLLs?It would be interesting to see whether the problem across different PCs even occurs any more if you compiled & released for Release instead?
getenv
does not crash when an environment variable does not exist, it returnsNULL
. If the code fails to deal with that (e.g. dereferences), that's a different matter, and is the only way it could "crash". So you have verified that the environment variable namedProgramXROOT
does not/does exist on the machines which crash/don't crash respectively? -
@Lati said in QT deployed executable crashes on some computers:
Strangely, there is no sign about the crash caused by getenv.
Well, it looks like the culprit isn't getenv() but the way the return value from that function is used within the DLL later on. From man getvenv:
The getenv() function returns a pointer to the value in the environment, or NULL if there is no match.
so
programXRootDirectory
becomes NULL when the variable is not set, and the world ends...@Pablo-J-Rogina
Exactly! No-one would guess the crash is due togetenv
since all indications were showing that it was either false libraries or graphic card issue.@JonB
I am not deploying anything on that screen. The screenshot is after I clicked on F5 to debug the code and the path of theQt5Cored.dll
is from Qt's installation directory (Qt is installed on D: drive, as I mentioned).getenv
crashes on my computer, as well as the deployed executable crashes on the computers if the environmental variable doesn't exist. It is very easy to test by writing a small application. -
@Pablo-J-Rogina
Exactly! No-one would guess the crash is due togetenv
since all indications were showing that it was either false libraries or graphic card issue.@JonB
I am not deploying anything on that screen. The screenshot is after I clicked on F5 to debug the code and the path of theQt5Cored.dll
is from Qt's installation directory (Qt is installed on D: drive, as I mentioned).getenv
crashes on my computer, as well as the deployed executable crashes on the computers if the environmental variable doesn't exist. It is very easy to test by writing a small application.@Lati said in QT deployed executable crashes on some computers:
It is very easy to test by writing a small application.
char *p = getenv("ProgramXROOT"); // or char *p = getenv("AnythingElseWhichDoesntExist");
won't crash. It will set
p
toNULL
/nullptr
.If the program continues and does not check for that, assuming that
p
will not be null, it may crash. That is all @Pablo-J-Rogina and I are saying. -
@Lati said in QT deployed executable crashes on some computers:
programXRootDirectory
It would be interesting to know what the data type is for this variable. I could not get QString to crash or misbehave.
{ char* null = nullptr; QString test = "hallo"; qInfo() << "before init null"; test = QString(null); qInfo() << test; qInfo() << "after init null"; QString test2 = "hallo again"; qInfo() << "before assign null"; test2 = null; qInfo() << test2; qInfo() << "after assign null"; }
-
@Lati said in QT deployed executable crashes on some computers:
programXRootDirectory
It would be interesting to know what the data type is for this variable. I could not get QString to crash or misbehave.
{ char* null = nullptr; QString test = "hallo"; qInfo() << "before init null"; test = QString(null); qInfo() << test; qInfo() << "after init null"; QString test2 = "hallo again"; qInfo() << "before assign null"; test2 = null; qInfo() << test2; qInfo() << "after assign null"; }
@fcarney
You can reproduce the issue with the following example (at least it crashes on my computer:)):main.cpp
#include "mainwindow.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
mainWindow.h:
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); // Senex root directory std::string notExistEnvVar; private: Ui::MainWindow *ui; }; #endif // MAINWINDOW_H
mainWindow.cpp
#include "mainwindow.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); notExistEnvVar = getenv("notExistEnvVar"); } MainWindow::~MainWindow() { delete ui; }
Compiled with MSVC2015 64bit.
-
You are getting an exception because you are assigning nullptr to std::string. Assign to
char* ptr = getenv("notExistEnvVar"); if(ptr != nullptr) notExistEnvVar = ptr;
getenv is not crashing
Edit:
or use QStringQString str = getenv("notExistEnvVar");
-
You are getting an exception because you are assigning nullptr to std::string. Assign to
char* ptr = getenv("notExistEnvVar"); if(ptr != nullptr) notExistEnvVar = ptr;
getenv is not crashing
Edit:
or use QStringQString str = getenv("notExistEnvVar");
-
For
std::string s = nullptr
, see https://stackoverflow.com/questions/10771864/assign-a-nullptr-to-a-stdstring-is-safe/10771938. Accepted answer:Requires: s shall not be a null pointer.
Since the standard does not ask the library to throw an exception when this particular requirement is not met, it would appear that passing a null pointer provoked undefined behavior.
-
@Lati said in QT deployed executable crashes on some computers:
It is very easy to test by writing a small application.
char *p = getenv("ProgramXROOT"); // or char *p = getenv("AnythingElseWhichDoesntExist");
won't crash. It will set
p
toNULL
/nullptr
.If the program continues and does not check for that, assuming that
p
will not be null, it may crash. That is all @Pablo-J-Rogina and I are saying.@JonB said in QT deployed executable crashes on some computers:
won't crash. It will set p to NULL/nullptr.
The problem is not the assignment... the problem comes once you have assigned NULL to the pointer and then you try using it
-
You are getting an exception because you are assigning nullptr to std::string. Assign to
char* ptr = getenv("notExistEnvVar"); if(ptr != nullptr) notExistEnvVar = ptr;
getenv is not crashing
Edit:
or use QStringQString str = getenv("notExistEnvVar");
Obviously, the person who wrote the library didn't know this (to be honest, I didn't know it as well, I have never used
getenv
). And this is how bugs happened, right? :)But interesting thing is that there is no sign about the crash and no-one had an idea about it (including me). I would still look at the error somewhere else if I wouldn't test the code on one of the problem computer.
Anyway, thank you for the clarification.
-
Obviously, the person who wrote the library didn't know this (to be honest, I didn't know it as well, I have never used
getenv
). And this is how bugs happened, right? :)But interesting thing is that there is no sign about the crash and no-one had an idea about it (including me). I would still look at the error somewhere else if I wouldn't test the code on one of the problem computer.
Anyway, thank you for the clarification.
@Lati
For the record,getenv()
has been in the C runtime libraries since the 1970s(!) There has to be a way for it to tell you that the selected environment variable does not exist (without crashing!), and that is of course by returning0
. I and millions of other coders have been using that behaviour ever since :)In your case, whoever wrote the code which does not check for that will doubtless have been working in an environment where that
ProgramXROOT
variable did always exist, and hence never witnessed the unanticipated behaviour.OOI, how have you resolved this? Did you actually change that library's code to fix and recompile, or have you just told your end users they must have that environment variable defined?
-
@Lati
For the record,getenv()
has been in the C runtime libraries since the 1970s(!) There has to be a way for it to tell you that the selected environment variable does not exist (without crashing!), and that is of course by returning0
. I and millions of other coders have been using that behaviour ever since :)In your case, whoever wrote the code which does not check for that will doubtless have been working in an environment where that
ProgramXROOT
variable did always exist, and hence never witnessed the unanticipated behaviour.OOI, how have you resolved this? Did you actually change that library's code to fix and recompile, or have you just told your end users they must have that environment variable defined?
-
@Lati
For the record,getenv()
has been in the C runtime libraries since the 1970s(!) There has to be a way for it to tell you that the selected environment variable does not exist (without crashing!), and that is of course by returning0
. I and millions of other coders have been using that behaviour ever since :)In your case, whoever wrote the code which does not check for that will doubtless have been working in an environment where that
ProgramXROOT
variable did always exist, and hence never witnessed the unanticipated behaviour.OOI, how have you resolved this? Did you actually change that library's code to fix and recompile, or have you just told your end users they must have that environment variable defined?
@JonB I am not one of that millions of coders (or even if I used it, I never had problems).
In the code, I replaced the
getenv
part using_dupenv_s
as following (which will be valid with the next release):char* buf = nullptr; size_t sz = 0; if (_dupenv_s(&buf, &sz, "programXROOT") == 0 && buf != nullptr) { programXRootDirectory = buf; free(buf); } else { QMessageBox msgBox; msgBox.setText("Environmental variable is missing!"); msgBox.exec(); }
Actually, after installation of this application, user has to create this environmental variable before starting the application. And seems like all the users have created the variable and there was no issue so far. But if a user would forget about this, it would be nightmare to find out the reason.
-
@JonB I am not one of that millions of coders (or even if I used it, I never had problems).
In the code, I replaced the
getenv
part using_dupenv_s
as following (which will be valid with the next release):char* buf = nullptr; size_t sz = 0; if (_dupenv_s(&buf, &sz, "programXROOT") == 0 && buf != nullptr) { programXRootDirectory = buf; free(buf); } else { QMessageBox msgBox; msgBox.setText("Environmental variable is missing!"); msgBox.exec(); }
Actually, after installation of this application, user has to create this environmental variable before starting the application. And seems like all the users have created the variable and there was no issue so far. But if a user would forget about this, it would be nightmare to find out the reason.
@Lati
Again for the record, the_dupenv_s
(which btw is MSVC-only) you mention has nothing to do with the issue you are talking about. The extra parameters it takes are to do with copying the value, if found, into your own buffer. If it's not found:If the variable is not found, then
buffer
is set toNULL
,numberOfElements
is set to0
, and the return value is0
because this situation is not considered to be an error condition.So the issue will rear its head again if code later tries to dereference the
buf
from the&buf
passed in. Just as with the return result fromgetenv()
.Purely as a by-the-by, I presume you are aware in the code you show (which perhaps is only intended as an example):
programXRootDirectory = buf; free(buf);
This would not be a good idea ---
programXRootDirectory
is left pointing to freed memory! You'll get a different error/crash/behaviour if you then dereference that :)