Moving files to Recycle Bin is slow.



  • Hi,

    I tried to move files to recycle bin in windows 7. But Qt does not provide
    platform independent method to do that. I found that I could use the following
    snippet to send files to recycle bin.

    bool MainWindow::moveToTrash( QString fn ){
        QFileInfo fileinfo( fn );
        if( !fileinfo.exists() ){
            QMessageBox::warning(this,tr("Failed") ,tr("No %1 exists").arg(fn));
            return false;
         //   throw OdtCore::Exception( "File doesnt exists, cant move to trash" );
        }
        WCHAR from[ MAX_PATH ];
        memset( from, 0, sizeof( from ));
        int l = fileinfo.absoluteFilePath().toWCharArray( from );
        Q_ASSERT( 0 <= l && l < MAX_PATH );
        from[ l ] = '\0';
    
        SHFILEOPSTRUCT fileop;
        memset( &fileop, 0, sizeof( fileop ) );
        fileop.wFunc = FO_DELETE;
        fileop.pFrom = from;
        fileop.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
    
    
    //SHFileOperationW
       int rv = SHFileOperationW( &fileop ); //Takes 20 ~ 70 ms which is 90% of deleting process. 5 times slower than just deleting.
        if( 0 != rv ){
            //qDebug() << rv << QString::number( rv ).toInt( 0, 8 );
            QMessageBox::warning(this,tr("Failed") ,tr("Could not move %1 to trash.").arg(fn));
           // throw OdtCore::Exception( "move to trash failed" );
            return false;
        }
    
        return true;
    }
    

    It works but very slow. Deleting 2000 files by explorer takes less than 30 seconds. In contrast, using SHFileOperationW() takes 4 or 5 minutes to delete the same 2000 files which is intollerable. Is it a normal performance or can I improve the performance with other methods?


  • Moderators

    @samdol said in Moving files to Recycle Bin is slow.:
    what first comes to my mind is that you are using QFileInfo for each file. QFileInfo is rather expensive and should be avoided in your case if anyhow possible.

    Also you are testing in release mode right?



  • @raven-worx
    Thanks, but when I run benchmark test, QFileInfo did not take much time compare to SHFileOperationW() which takes 90 percent of processing time for deleting files. It took 30~ 70 miliseconds for each file which is quite slow compare to direct delete. And Yes, I tested in release mode.


  • Lifetime Qt Champion

    Hi,

    After a quicklook about SHFileOperationW, it seems you're not the only experimenting this problem. It is however a Windows API specific issue and nothing Qt can do anything about.

    Taking a look at the documentation of SHFILEOPSTRUCT, it seems that the pFrom string can contain a list of files so you may gain time by building the list of all the files you want to delete and make only one call to SHFileOperationW.



  • @SGaist

    Thank you for the advise. As you suggested, I attempt to put multiple deleting files on SHFileOperationW.
    Doc says I need to put 0s between filenames, I created Qstring with 0s between
    filenames. Then converted to WcharArray and finally put 0 at the end.
    But when I run this code, it puts only file1.jpg to recyclebin. And file2.jpg
    did not move.

    bool moveToTrash( QString filenames ){
        WCHAR from[ MAX_PATH ];
        memset( from, 0, sizeof( from ));
        int l = filenames.toWCharArray( from );
        Q_ASSERT( 0 <= l && l < MAX_PATH );
        from[ l ] = '\0';
    
        SHFILEOPSTRUCT fileop;
        memset( &fileop, 0, sizeof( fileop ) );
        fileop.wFunc = FO_DELETE;
        fileop.pFrom = from;
        fileop.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
    
    //SHFileOperationW
       int rv = SHFileOperationW( &fileop ); //Takes 20 ~ 70 ms which is 90% of deleting process. 5 times slower than just deleting.
        if( 0 != rv ){
            return false;
        }
        return true;
    }
    
    
    
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QString filenames=QString("D:/folder/file1.jpg\0D:/folder/file2.jpg");
        moveToTrash(filenames);
        return a.exec();
    }
    

  • Moderators

    @samdol said in Moving files to Recycle Bin is slow.:

    QString filenames=QString("D:/folder/file1.jpg\0D:/folder/file2.jpg");

    don't go the long way with QString. I think QString only creates the string until it finds the \0 character.
    Pass a QStringList to your moveToTrash() method and create the string for the WinAPI call just before calling it.


  • Lifetime Qt Champion

    You can use a QByteArray to create your long list of files as, like @raven-worx, QString represent a zero terminated string.



  • @SGaist

    In the following code, I tried to send QStringList of deleting files to
    moveToTrash(). And used QByteArray to add '\0's between filenames. But when I compile, it says,
    cannot convert 'char*' to 'WCHAR{aka wchar_t*}' in initialization.
    cannot convert 'WCHAR* {aka wchar_t*} to 'char*' for argument '1' to 'char*
    strcpy(char*, const char*)'
    I also tried to do ba.toWCharArray(from), but I could not find such function
    for QByteArray.

    bool moveToTrash( QStringList filename_list ){
        QByteArray ba;
        for(int i=0;i<filename_list.size();i++){
            ba.append(filename_list.at(i));
            ba.append('\0');
        }
        ba.append('\0');
    
        WCHAR* from = new char[ba.size()]; <-- Error
        strcpy(from, ba.data()); <-- Error
    
        SHFILEOPSTRUCT fileop;
        memset( &fileop, 0, sizeof( fileop ) );
       // fileop.hwnd = NULL;
        fileop.wFunc = FO_DELETE;
        fileop.pFrom = from;
        fileop.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
    
       int rv = SHFileOperationW( &fileop );
       if( 0 != rv ){
            return false;
        }
        return true;
    }
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        QStringList filename_list;
        filename_list<<"D:/folder/file1.jpg"<<"D:/folder/file2.jpg";
        moveToTrash(filename_list);
        return a.exec();
    }
    


  • @samdol
    I also tried the following approach. Manually adding '\0's between files. It seems it should work. but it moves a file to trach only when I delete one file. If I try to delete more than one file, it does not do anything.

    bool moveToTrash( QStringList filename_list ){
    
        WCHAR* from= new WCHAR[ 999999 ];
        int l=filename_list.at(0).toWCharArray( from );
        from[l]='\0';
    
        int i;
        for( i=1;i<filename_list.size();i++){
            WCHAR* from_tmp = new WCHAR[ filename_list.at(i).size() ];
            l += filename_list.at(i).toWCharArray( from_tmp );
            wcscat (from, from_tmp);
            from[l+i]='\0';
    
        }
        from[l+i+1]='\0';
    
        SHFILEOPSTRUCT fileop;
        memset( &fileop, 0, sizeof( fileop ) );
        fileop.wFunc = FO_DELETE;
        fileop.pFrom = from;
        fileop.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
    
       int rv = SHFileOperationW( &fileop ); //Takes 20 ~ 70 ms which is 90% of deleting process. 5 times slower than just deleting.
    
        return true;
    }
    

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.