Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to properly call QFile::remove() on Windows?

How to properly call QFile::remove() on Windows?

Scheduled Pinned Locked Moved Solved General and Desktop
16 Posts 6 Posters 456 Views 3 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • R Offline
    R Offline
    Robert Hairgrove
    wrote last edited by
    #1

    I am trying to debug my application in Qt Creator using SQLite3 database files which the user can create, open, and overwrite when a new database is created... At least on Linux, I can always overwrite the old database if it is owned by the current user.

    On Windows, I am getting an error stating that the process cannot access the file because it is in use by another process. If I look at the file in Windows Explorer at the moment that happens, it says that it is in use by my application. So I inserted this code in the hopes that it would solve the error:

        QFile x(absFilePath);
    
    #ifdef Q_OS_WINDOWS
        if (x.isOpen()) {
          x.close();
        }
    #endif
    
        if (!x.remove()) {
          const QString title = tr("Could Not Remove Existing File");
          const QString msg = tr("The file:\n\n%1\n\ncould not be removed.\n\nError message:\n%2");
          QMessageBox::warning(this,
                               title,
                               msg.arg(absFilePath).arg(x.errorString()));
          return;
        }
    

    However, this doesn't let me remove the file. The file path in absFilePath is valid, the file exists, and permissions are adequate to allow deletion of the file. This is all checked in code that runs just before this extract is executed. And any objects such as QFile or QFileInfo which might refer to this file are put in their own scope so that when the above bit runs, they all go out of scope.

    I can call the static version of QFile::remove(absFilePath) which DOES seem to work, but then I won't be able to get any error messages from QFileDevice when it doesn't work. Is there a better way to do this but still get an error message when it doesn't work?

    jsulmJ 1 Reply Last reply
    0
    • R Robert Hairgrove

      I am trying to debug my application in Qt Creator using SQLite3 database files which the user can create, open, and overwrite when a new database is created... At least on Linux, I can always overwrite the old database if it is owned by the current user.

      On Windows, I am getting an error stating that the process cannot access the file because it is in use by another process. If I look at the file in Windows Explorer at the moment that happens, it says that it is in use by my application. So I inserted this code in the hopes that it would solve the error:

          QFile x(absFilePath);
      
      #ifdef Q_OS_WINDOWS
          if (x.isOpen()) {
            x.close();
          }
      #endif
      
          if (!x.remove()) {
            const QString title = tr("Could Not Remove Existing File");
            const QString msg = tr("The file:\n\n%1\n\ncould not be removed.\n\nError message:\n%2");
            QMessageBox::warning(this,
                                 title,
                                 msg.arg(absFilePath).arg(x.errorString()));
            return;
          }
      

      However, this doesn't let me remove the file. The file path in absFilePath is valid, the file exists, and permissions are adequate to allow deletion of the file. This is all checked in code that runs just before this extract is executed. And any objects such as QFile or QFileInfo which might refer to this file are put in their own scope so that when the above bit runs, they all go out of scope.

      I can call the static version of QFile::remove(absFilePath) which DOES seem to work, but then I won't be able to get any error messages from QFileDevice when it doesn't work. Is there a better way to do this but still get an error message when it doesn't work?

      jsulmJ Offline
      jsulmJ Offline
      jsulm
      Lifetime Qt Champion
      wrote last edited by
      #2

      @Robert-Hairgrove said in How to properly call QFile::remove() on Windows?:

      On Windows, I am getting an error stating that the process cannot access the file because it is in use by another process.

      It is not clear when it happens:
      a) When SQLite tries to write to that file
      b) When you're trying to delete it

      If it is b) - is the file still in use by SQLite?

      https://forum.qt.io/topic/113070/qt-code-of-conduct

      R 1 Reply Last reply
      2
      • jsulmJ jsulm

        @Robert-Hairgrove said in How to properly call QFile::remove() on Windows?:

        On Windows, I am getting an error stating that the process cannot access the file because it is in use by another process.

        It is not clear when it happens:
        a) When SQLite tries to write to that file
        b) When you're trying to delete it

        If it is b) - is the file still in use by SQLite?

        R Offline
        R Offline
        Robert Hairgrove
        wrote last edited by
        #3

        @jsulm Nothing is writing to the file, otherwise the static QFile::remove() would also fail (but it doesn't).

        1 Reply Last reply
        0
        • I Offline
          I Offline
          IgKh
          wrote last edited by IgKh
          #4

          The static QFile::remove is literally defined as return QFile(fileName).remove(). And the non-static version closes the file by itself if it is open.

          There is no difference in mechanism, so any difference in behavior you observe may be a coincidence. There is not enough information in the post to determine. I'd check that the SQLite connection is completely closed and discarded before removal of the file; note that even on Linux where it is possible to unlink files with open descriptors doing so to SQLite databases is never safe - the WAL or rollback journal file may lose association which can lead to database corruption,

          As for getting the error from the static version, you can call the Win32 API method GetLastError yourself, if you'd like.

          R 1 Reply Last reply
          5
          • Kent-DorfmanK Offline
            Kent-DorfmanK Offline
            Kent-Dorfman
            wrote last edited by Kent-Dorfman
            #5

            May or may not be relevant but in general terms when you create a DB object handle to a database the DB file becomes (open) for the duration of the DB handle. Make sure you are destroying the DB handle before attempting to remove the database file.

            Yeah, what IgKh said above...

            The dystopian literature that served as a warning in my youth has become an instruction manual in my elder years.

            1 Reply Last reply
            4
            • I IgKh

              The static QFile::remove is literally defined as return QFile(fileName).remove(). And the non-static version closes the file by itself if it is open.

              There is no difference in mechanism, so any difference in behavior you observe may be a coincidence. There is not enough information in the post to determine. I'd check that the SQLite connection is completely closed and discarded before removal of the file; note that even on Linux where it is possible to unlink files with open descriptors doing so to SQLite databases is never safe - the WAL or rollback journal file may lose association which can lead to database corruption,

              As for getting the error from the static version, you can call the Win32 API method GetLastError yourself, if you'd like.

              R Offline
              R Offline
              Robert Hairgrove
              wrote last edited by
              #6

              @IgKh and @Kent-Dorfman : Thank you, this is very helpful information. My application does call QSqlDatabase::removeDatabase() on the connection before it tries to delete the file, but maybe I need to also call QApplication::processEvents()?

              The tip about calling GetLastError() is a good one in situations like this. I will test this again before marking it solved.

              JonBJ 1 Reply Last reply
              0
              • R Robert Hairgrove

                @IgKh and @Kent-Dorfman : Thank you, this is very helpful information. My application does call QSqlDatabase::removeDatabase() on the connection before it tries to delete the file, but maybe I need to also call QApplication::processEvents()?

                The tip about calling GetLastError() is a good one in situations like this. I will test this again before marking it solved.

                JonBJ Online
                JonBJ Online
                JonB
                wrote last edited by JonB
                #7

                @Robert-Hairgrove
                If you say you can literally put QFile::remove(x) where you have x.remove() and it works then I would look at/show what you exactly have done with your QFile.

                Also, assuming you are using QSqlDatabase, have you done what you're not supposed to do and kept a member variable copy of it?

                1 Reply Last reply
                0
                • Andy314A Offline
                  Andy314A Offline
                  Andy314
                  wrote last edited by
                  #8

                  I had the same problem. Simple close() of the database solved it.

                  JonBJ 1 Reply Last reply
                  0
                  • Andy314A Andy314

                    I had the same problem. Simple close() of the database solved it.

                    JonBJ Online
                    JonBJ Online
                    JonB
                    wrote last edited by
                    #9

                    @Andy314
                    Although I am sure this in itself is a useful recommendation, and may well solve the problem for whatever reason, did you really have the same problem where QFile file(...); file.remove(); failed while QFile::remove(...); succeeded? If so, do you have a repro? This is what is strange here. Because it seems like that would only be the case depending on what you had done with your QFile file instance....

                    R 1 Reply Last reply
                    0
                    • JonBJ JonB

                      @Andy314
                      Although I am sure this in itself is a useful recommendation, and may well solve the problem for whatever reason, did you really have the same problem where QFile file(...); file.remove(); failed while QFile::remove(...); succeeded? If so, do you have a repro? This is what is strange here. Because it seems like that would only be the case depending on what you had done with your QFile file instance....

                      R Offline
                      R Offline
                      Robert Hairgrove
                      wrote last edited by
                      #10

                      @JonB and @Andy314 : Thanks for the feedback. I am pretty sure that the database connection is being handled properly. It is possible that it has to do with my environment ... running in Oracle VirtualBox in a Windows 10 guest on a Linux host. I had an issue once where I could not remove a USB stick in the virtual environment for several minutes because Windows thought it was being used, where it certainly wasn't. I don't have any anti-virus software running in the guest, but maybe it was just Windows doing its housekeeping?

                      I need to look at the Qt source code some more ... if all else fails, I can try putting the call to QFile::remove() in a while loop and call std::yield or QApplication::processEvents().

                      JonBJ 1 Reply Last reply
                      0
                      • R Robert Hairgrove

                        @JonB and @Andy314 : Thanks for the feedback. I am pretty sure that the database connection is being handled properly. It is possible that it has to do with my environment ... running in Oracle VirtualBox in a Windows 10 guest on a Linux host. I had an issue once where I could not remove a USB stick in the virtual environment for several minutes because Windows thought it was being used, where it certainly wasn't. I don't have any anti-virus software running in the guest, but maybe it was just Windows doing its housekeeping?

                        I need to look at the Qt source code some more ... if all else fails, I can try putting the call to QFile::remove() in a while loop and call std::yield or QApplication::processEvents().

                        JonBJ Online
                        JonBJ Online
                        JonB
                        wrote last edited by JonB
                        #11

                        @Robert-Hairgrove
                        In your sample code you just show:

                            QFile x(absFilePath);
                        
                        #ifdef Q_OS_WINDOWS
                            if (x.isOpen()) {
                              x.close();
                            }
                        #endif
                        
                            if (!x.remove()) {
                        
                        • Put in a debug, do you actually hit that x.close()?
                        • Please show what you actually do with the QFile x in between creating the variable and the close for the attempted remove.

                        Oh, and to be 100% clear, the absFilePath is the path to the actual database file on disk opened by QSqlDatabase, right?

                        R 1 Reply Last reply
                        0
                        • JonBJ JonB

                          @Robert-Hairgrove
                          In your sample code you just show:

                              QFile x(absFilePath);
                          
                          #ifdef Q_OS_WINDOWS
                              if (x.isOpen()) {
                                x.close();
                              }
                          #endif
                          
                              if (!x.remove()) {
                          
                          • Put in a debug, do you actually hit that x.close()?
                          • Please show what you actually do with the QFile x in between creating the variable and the close for the attempted remove.

                          Oh, and to be 100% clear, the absFilePath is the path to the actual database file on disk opened by QSqlDatabase, right?

                          R Offline
                          R Offline
                          Robert Hairgrove
                          wrote last edited by
                          #12

                          @JonB said in How to properly call QFile::remove() on Windows?:

                          @Robert-Hairgrove
                          to be 100% clear, the absFilePath is the path to the actual database file on disk opened by QSqlDatabase, right?

                          No ... absFilePath is the name of the file chosen by the user which will be the name of the new database. It just so happens that this is an existing file in this part of the code, which happens to be a database. There is no open database connection when this code runs, and there is nothing between the QFile x(...) statement and the #ifdef.... The application ensures that any open database is closed (i.e. the connection is removed) before letting the user create a new one.

                          Like @IgKh said, it is pretty much what is in the Qt source code for QFile::remove() ... it also closes the file before trying to remove it. And if it isn't open, then I assume there is no need to call close(). But maybe I should not trust QFile::isOpen() and just call close() in any case?

                          JonBJ 1 Reply Last reply
                          0
                          • R Offline
                            R Offline
                            Robert Hairgrove
                            wrote last edited by
                            #13

                            Maybe this is overkill for something which should be very simple, but at least it is working.
                            The code right before the following performs some checks on exisence and file permissions. If the user chooses to delete the file, then this part runs:

                              QString errstring;
                            
                              if (delete_existing_file) {
                                std::chrono::microseconds us(0);
                                const std::chrono::microseconds us_begin(0);
                                const std::chrono::microseconds interval(500);
                                const std::chrono::microseconds max_wait(1000000);
                                { // start a new scope...
                                  QFile x(absFilePath);
                                  x.close();
                                  // The following snippet taken from the example for std::this_thread::yield():
                                  // https://en.cppreference.com/w/cpp/thread/yield.html
                                  auto wait_for_remove = [](std::chrono::microseconds s) {
                                    auto begin = std::chrono::high_resolution_clock::now();
                                    auto end = begin + s;
                                    do {
                                      std::this_thread::yield();
                                    } while(std::chrono::high_resolution_clock::now() < end);
                                  };
                            
                                  while (!x.remove()) {
                                    us += interval;
                                    wait_for_remove(interval);
                                    if (us >= max_wait)
                                      break;
                                  }
                                  errstring = x.errorString();
                                } // x goes out of scope here...
                            
                                if (us > us_begin) {
                                  if (us < max_wait) {
                                    // Success, but we waited at least once:
                                    qDebug() << "Waited for QFile::remove() in microseconds: " << us;
                                  } else {
                                    title = tr("Problem Removing Existing File");
                                    msg = tr("The file '%1' could not be removed.\n\nError message:\n%2")
                                            .arg(absFilePath).arg(errstring);
                                    QMessageBox::warning(this,title,msg);
                                    qDebug() << "Failed to remove file: " << absFilePath << "Error: " << errstring;
                                    return;
                                  }
                                }
                              }
                            
                            R 1 Reply Last reply
                            0
                            • R Robert Hairgrove has marked this topic as solved
                            • R Robert Hairgrove

                              @JonB said in How to properly call QFile::remove() on Windows?:

                              @Robert-Hairgrove
                              to be 100% clear, the absFilePath is the path to the actual database file on disk opened by QSqlDatabase, right?

                              No ... absFilePath is the name of the file chosen by the user which will be the name of the new database. It just so happens that this is an existing file in this part of the code, which happens to be a database. There is no open database connection when this code runs, and there is nothing between the QFile x(...) statement and the #ifdef.... The application ensures that any open database is closed (i.e. the connection is removed) before letting the user create a new one.

                              Like @IgKh said, it is pretty much what is in the Qt source code for QFile::remove() ... it also closes the file before trying to remove it. And if it isn't open, then I assume there is no need to call close(). But maybe I should not trust QFile::isOpen() and just call close() in any case?

                              JonBJ Online
                              JonBJ Online
                              JonB
                              wrote last edited by JonB
                              #14

                              @Robert-Hairgrove said in How to properly call QFile::remove() on Windows?:

                              there is nothing between the QFile x(...) statement and the #ifdef....

                               QFile x(absFilePath);
                               x.close();
                              

                              Then I have no idea why you are creating any QFile instance here at all. And if for unknown reason it is causing problem just remove it, why have you put this in? Oh, did you say purely in order to be able to go x.remove() so that you can get QFile::error() information for message? Certainly you should not need the close(), and I asked earlier if your if (x.isOpen()) was ever true, because with this code it should never be?

                              Looks like we shall never know why this fails when the static call succeeds....

                              R 1 Reply Last reply
                              0
                              • JonBJ JonB

                                @Robert-Hairgrove said in How to properly call QFile::remove() on Windows?:

                                there is nothing between the QFile x(...) statement and the #ifdef....

                                 QFile x(absFilePath);
                                 x.close();
                                

                                Then I have no idea why you are creating any QFile instance here at all. And if for unknown reason it is causing problem just remove it, why have you put this in? Oh, did you say purely in order to be able to go x.remove() so that you can get QFile::error() information for message? Certainly you should not need the close(), and I asked earlier if your if (x.isOpen()) was ever true, because with this code it should never be?

                                Looks like we shall never know why this fails when the static call succeeds....

                                R Offline
                                R Offline
                                Robert Hairgrove
                                wrote last edited by Robert Hairgrove
                                #15

                                @JonB Here you can read the Qt6 source code of QFile::remove() ... the first link is to the non-static member function, and the second is to the static member function. My code does pretty much the same thing except for the "while {...}" loop I have written:

                                https://codebrowser.dev/qt6/qtbase/src/corelib/io/qfile.cpp.html#421
                                https://codebrowser.dev/qt6/qtbase/src/corelib/io/qfile.cpp.html#452

                                Does this help?

                                And since there is basically no difference in the actual code, as I said ... I do want to get the error message.

                                1 Reply Last reply
                                0
                                • R Robert Hairgrove

                                  Maybe this is overkill for something which should be very simple, but at least it is working.
                                  The code right before the following performs some checks on exisence and file permissions. If the user chooses to delete the file, then this part runs:

                                    QString errstring;
                                  
                                    if (delete_existing_file) {
                                      std::chrono::microseconds us(0);
                                      const std::chrono::microseconds us_begin(0);
                                      const std::chrono::microseconds interval(500);
                                      const std::chrono::microseconds max_wait(1000000);
                                      { // start a new scope...
                                        QFile x(absFilePath);
                                        x.close();
                                        // The following snippet taken from the example for std::this_thread::yield():
                                        // https://en.cppreference.com/w/cpp/thread/yield.html
                                        auto wait_for_remove = [](std::chrono::microseconds s) {
                                          auto begin = std::chrono::high_resolution_clock::now();
                                          auto end = begin + s;
                                          do {
                                            std::this_thread::yield();
                                          } while(std::chrono::high_resolution_clock::now() < end);
                                        };
                                  
                                        while (!x.remove()) {
                                          us += interval;
                                          wait_for_remove(interval);
                                          if (us >= max_wait)
                                            break;
                                        }
                                        errstring = x.errorString();
                                      } // x goes out of scope here...
                                  
                                      if (us > us_begin) {
                                        if (us < max_wait) {
                                          // Success, but we waited at least once:
                                          qDebug() << "Waited for QFile::remove() in microseconds: " << us;
                                        } else {
                                          title = tr("Problem Removing Existing File");
                                          msg = tr("The file '%1' could not be removed.\n\nError message:\n%2")
                                                  .arg(absFilePath).arg(errstring);
                                          QMessageBox::warning(this,title,msg);
                                          qDebug() << "Failed to remove file: " << absFilePath << "Error: " << errstring;
                                          return;
                                        }
                                      }
                                    }
                                  
                                  R Offline
                                  R Offline
                                  Robert Hairgrove
                                  wrote last edited by
                                  #16

                                  @Robert-Hairgrove said in How to properly call QFile::remove() on Windows?:
                                  Small correction:

                                  if (us < max_wait) {
                                  // Success, but we waited at least once:
                                  qDebug() << "Waited for QFile::remove() in microseconds: " << us.count(); // <== need to use ".count()" here...
                                  } else {
                                  // etc.

                                  1 Reply Last reply
                                  0

                                  • Login

                                  • Login or register to search.
                                  • First post
                                    Last post
                                  0
                                  • Categories
                                  • Recent
                                  • Tags
                                  • Popular
                                  • Users
                                  • Groups
                                  • Search
                                  • Get Qt Extensions
                                  • Unsolved