May 16, 2012

Keozon Keozon
Lab Rat
21 posts

Win API WaitForMultiple Thread Crashing Application

 

So, I’ve been having no end to random inconsistencies that did not make any sense to me using QFileSystemWatcher (see my second-to-last post here [qt-project.org] for details), so I decided I was going to go to the source and just use Win API. Which, at first, worked. Then, at some point it started crashing. I know I must have made some small modifications before it did so, but I really don’t remember changing anything. I even used version control software to find the differences in the code for me, and I couldn’t see anything that could possibly cause the problem. By using excessive debug outputs, I’ve determined that the thread always crashes right when it enters the WaitForMultipleObjects event loop.

I have reversed all my changes in that loop to their original state, when it first worked, and it still crashes. I’ve cleaned all. I’ve restarted my computer. Nothing seems to fix it. I’ve spent several hours tweaking, compiling and rechecking to no avail. I’ve decided I need another set of eyes.

Here is the applicable code:

  1. sharedData a;
  2. /*The File Monitoring thread. This thread waits for a directory change to do anything.*/
  3. FileMon::FileMon(){
  4. }
  5. QStringList FileMon::getFileList(QString arg){
  6.     QDir dir(arg);
  7.     QStringList files = dir.entryList(QDir::Files,QDir::Time);
  8.     return files;
  9. }
  10. void FileMon::run(){
  11.     emit newDebug("Thread started successfully. Working dir: "+a.getDir());
  12.     monitor.addPath(a.getDir());
  13.     QStringList files = getFileList(a.getDir());
  14.     if (!files.isEmpty()){
  15.         const QString fullyQ = a.getDir();
  16.         emit newDebug("Watching most recent file: " + files[0]);
  17.         watchFile(fullyQ);
  18.     }
  19.     else {
  20.         emit newDebug("Directory is empty. Will add new file watcher when file is added.");
  21.     }
  22.     QObject::connect(&monitor,SIGNAL(directoryChanged(QString)),SLOT(change_notify(QString)));
  23.     QObject::connect(&monitor,SIGNAL(fileChanged(QString)),SLOT(change_notify(QString)));
  24.     exec();
  25. }
  26. bool FileMon::setconnect(QPushButton *arg,TorTools *arg2){
  27.     bool result = QObject::connect(arg,SIGNAL(clicked()),this,SLOT(terminate()));
  28.     if (result){result = QObject::connect(this,SIGNAL(newDebug(QString)),arg2,SLOT(onNewDebug(QString)));}
  29.     if (result){result = QObject::connect(this,SIGNAL(sendNotify(QString)),arg2,SLOT(on_directory_change(QString)));}
  30.     return result;
  31. }
  32. void FileMon::change_notify(QString arg){
  33.     emit sendNotify(arg);
  34.     /*This checks to see if a new file was added, or if the currently watched file was changed.
  35.       if a new file was added, it removes the old one (if any) and replaces it with the new one.*/
  36.     if (arg == a.getDir()){
  37.         QStringList newFiles = getFileList(a.getDir());
  38.         emit newDebug("Now watching new file "+newFiles[0]);
  39.         const QString fullyQ = a.getDir();
  40.         watchFile(fullyQ);
  41.     }
  42. }
  43. void FileMon::watchFile(const QString arg){
  44.     WCHAR *fname;
  45.     arg.toWCharArray(fname);
  46.     HANDLE changeNotifications[2];
  47.     DWORD waitStatus;
  48.     bool fileAdded = false;
  49.     //detect changes to watched file size, signifying write
  50.     changeNotifications[0] = FindFirstChangeNotification(fname,false,FILE_NOTIFY_CHANGE_SIZE);
  51.     //detect changes to the directory (file added) to signify new file to watch
  52.     changeNotifications[1] = FindFirstChangeNotification(fname,false,FILE_NOTIFY_CHANGE_FILE_NAME);
  53.     if (changeNotifications[0] == INVALID_HANDLE_VALUE
  54.             || changeNotifications[1] == INVALID_HANDLE_VALUE){
  55.         emit newDebug("Error: Invalid file handle.");
  56.     }
  57.     else if (changeNotifications[0] == NULL
  58.              || changeNotifications[1] == NULL){
  59.         emit newDebug("Error: Unexpected NULL handle.");
  60.     }
  61.     //start waiting in infinite loop. Thread is terminated when logging turned off, will kill loop forcibly.
  62.     //bad form? Perhaps. But, this thread makes no actual changes to files or data inside loop.
  63.     else {
  64.         emit newDebug("Now starting API FS watch.");
  65.         while (!fileAdded){
  66.             waitStatus = WaitForMultipleObjects(2,changeNotifications,false,INFINITE);
  67.             switch (waitStatus)
  68.             {
  69.                 case WAIT_OBJECT_0:
  70.                     //watched file was written to
  71.                     emit newDebug("Watched File Changed!");
  72.                     if (!FindNextChangeNotification(changeNotifications[0])){
  73.                         emit newDebug("Unexpected error when attempting to continue file monitoring.");
  74.                     }
  75.                     break;
  76.                 case WAIT_OBJECT_0 + 1:
  77.                     //File added to directory
  78.                     fileAdded = true;
  79.                     break;
  80.             }
  81.         }
  82.     }
  83. }

And here is the output before it crashes:

  1. "Starting Thread..."
  2. "Connections Made."
  3. "Thread started successfully. Working dir: C:\Users\OMITTED"
  4. "Watching most recent fileNew Text Document.txt"
  5. The program has unexpectedly finished.
  6. C:\Users\OMITTED\TorTools.exe exited with code -1073741819

The error code is not consistent.

 Signature 

I know exactly where my computer is, so I have no idea how fast it’s going.

5 replies

May 16, 2012

Keozon Keozon
Lab Rat
21 posts

Okay, I figured it out. It was (surprise, surprise) a memory management issue. line 44 needed to change to:

  1. WCHAR *fname = new WCHAR[arg.length];

Oddly, I’m 100% sure that worked before without the allocation. Oh well, I’m content that it’s fixed.

 Signature 

I know exactly where my computer is, so I have no idea how fast it’s going.

May 16, 2012

Wilk Wilk
Lab Rat
120 posts

Hello.
Actualy it’s totaly your fault. For more information read documentation for toWCharArray [qt-project.org] method.

May 17, 2012

MuldeR MuldeR
Robot Herder
801 posts

Keozon wrote:
Okay, I figured it out. It was (surprise, surprise) a memory management issue. line 44 needed to change to:
  1. WCHAR *fname = new WCHAR[arg.length];

Oddly, I’m 100% sure that worked before without the allocation. Oh well, I’m content that it’s fixed.

Actually I think you should modify the code like this:

  1. const size_t len = arg.length();
  2. WCHAR *fname = new WCHAR[len + 1];
  3. arg.toWCharArray(fname);
  4. fname[len] = L'\0';

That’s because, according to the docs, toWCharArray() does NOT append the required NULL-Terminator!
Nor does arg.length() include the extra space for the trailing NULL character.

You could also do this, which avoids the requirement to make a temp copy of the string:

  1. FindFirstChangeNotification((const wchar_t*) arg.utf16(), false, FILE_NOTIFY_CHANGE_SIZE);

(The typecast is needed because Qt uses “ushort*” to store UTF-16 chars, while the Win32 API uses “wchar_t*”. But don’t worry, they’re both “16-Bit per character” types with UTF-16 encoding, at least on Win32)

 Signature 

My OpenSource software at: http://muldersoft.com/

Qt v4.8.6 MSVC 2013, static/shared: http://goo.gl/BXqhrS

Go visit the coop: http://youtu.be/Jay-fG9eaYk

May 17, 2012

Keozon Keozon
Lab Rat
21 posts

Thanks for the typecast form! That helped simplify the code quite a bit.
Also, I had read about the NULL terminator issue, but it didn’t seem to cause any issues, so I ignored it.

 Signature 

I know exactly where my computer is, so I have no idea how fast it’s going.

May 17, 2012

MuldeR MuldeR
Robot Herder
801 posts
Keozon wrote:
Also, I had read about the NULL terminator issue, but it didn’t seem to cause any issues, so I ignored it.

If your old code, which did not care about the required NULL terminator, worked, then this was sheer luck…

 Signature 

My OpenSource software at: http://muldersoft.com/

Qt v4.8.6 MSVC 2013, static/shared: http://goo.gl/BXqhrS

Go visit the coop: http://youtu.be/Jay-fG9eaYk

 
  ‹‹ Control QMenu/QAction icon location      Qt and GCC/G++ backwards compatibility ››

You must log in to post a reply. Not a member yet? Register here!