CopyFileExW fails with 0 for an existing file

0

Context:

I have an application that searches files in a directory using QDirIterator, filters and copies specific files.

Problem:

Using the results from QDirIterator::next(), I ensure the file exists (as a unnecessary safe measure) using QFile::exists(QString) which is valid.

However, when attempting to copy the file using CopyFileExW, it returns 0 meaning the file copy failed. I have absolutely no idea why.

File location:

C:/Users/CybeX/Documents/BLMS/Repo/BLMS-Work-Dev/Meeting 2 - Requirements Document Discussion & Dev/2020-05-19 11.22.30 Someone Person's Zoom Meeting 98661954658/zoom_0.mp4

Sanity Tests

I added some sanity tests to check the file content through the conversion from QString -> LPCWSTR as FileCopyExW requires LPCWSTR and it is suggested to convert QString -> LPCWSTR here.

Regarding the conversion, I have tried this too but it yields the same result. It is also suggested as bad practice to user c-style casts

Sanity tests (in application code below) all pass, but FileCopyExW always fails with:

"Error 0x80070002: The system cannot find the file specified."

Code inside application:

QString m_src = QString("C:/Users/CybeX/Documents/BLMS/Repo/BLMS-Work-Dev/Meeting 2 - Requirements Document Discussion & Dev/2020-05-19 11.22.30 Someone Person's Zoom Meeting 98661954658/zoom_0.mp4");
QString m_dst = QString("C:/Users/CybeX/Documents/BLMS/Repo/BLMS-Work-Dev/Meeting 2 - Requirements Document Discussion & Dev/2020-05-19 11.22.30 Someone Person's Zoom Meeting 98661954658/zoom_0.mp42");
     
// Hard coded test location attempting to match variables' content below
//    QString srcLocation = QString("C:/Users/CybeX/Documents/BLMS/Repo/BLMS-Work-Dev/Meeting 2 - Requirements Document Discussion & Dev/2020-05-19 10.41.17 Someone Person's Zoom Meeting 96047275811/zoom_0.mp4");
//    QString dstLocation = QString("C:/Users/CybeX/Documents/BLMS/Repo/BLMS-Work-Dev/Meeting 2 - Requirements Document Discussion & Dev/2020-05-19 10.41.17 Someone Person's Zoom Meeting 96047275811/zoom_0.mp44");

//    std::wstring srcWString1 = srcLocation.toStdWString();
//    std::wstring dstWString1 = dstLocation.toStdWString();

//    const wchar_t* localC_src1 = srcLocation.toStdWString().c_str();
//    const wchar_t* localC_dst1 = dstLocation.toStdWString().c_str();
//
//    std::wstring srcWString = m_src.toStdWString();
//    std::wstring dstWString = m_dst.toStdWString();


// Used inside copy function
    const wchar_t* localC_src = m_src.toStdWString().c_str();
    const wchar_t* localC_dst = m_dst.toStdWString().c_str();
    
    // Sanity tests
     if (m_src.contains("96047275811/zoom_0.mp4")) {
          if (srcLocation != m_src) {
               qDebug() << "Warning";            // Never gets hit
          }
          if (srcWString != srcWString1) {
               qDebug() << "Warning";            // Never gets hit
          }
          if (*localC_src != *localC_src1) {
               qDebug() << "Warning";            // Never gets hit
          }

          if (!QFile::exists(srcLocation)) {
               qDebug() << "Warning";             // Never gets hit
          }
     }
     auto rc = CopyFileExW(localC_src, localC_dst, &FileManager::copyProgress, this, &bStopBackup, 0);
     if (rc == 0) {
          printWarning(TAG, QString("File Copy Error: %1").arg(getLastErrorMsg()));
          // copy failed
          return FileResult::IOError;               // This file always hits
     }

     // copy success
     return FileResult::Success;

However, hard coding the file location in a custom test application does indeed work correctly.

Testing Application:

Result is successful

static QString toString(HRESULT hr)
{
     _com_error err{hr};
     const TCHAR* lastError = err.ErrorMessage();
     return QStringLiteral("Error 0x%1: %2").arg((quint32)hr, 8, 16, QLatin1Char('0'))
            .arg(lastError);
}

static QString getLastErrorMsg()
{
     QString s = toString(HRESULT_FROM_WIN32(GetLastError()));
     return s;
}

int main(int argc, char* argv[])
{
     QCoreApplication a(argc, argv);

     QString m_src = QString("C:/Users/CybeX/Documents/BLMS/Repo/BLMS-Work-Dev/Meeting 2 - Requirements Document Discussion & Dev/2020-05-19 11.22.30 Someone Person's Zoom Meeting 98661954658/zoom_0.mp4");
     QString m_dst = QString("C:/Users/CybeX/Documents/BLMS/Repo/BLMS-Work-Dev/Meeting 2 - Requirements Document Discussion & Dev/2020-05-19 11.22.30 Someone Person's Zoom Meeting 98661954658/zoom_0.mp42");

     auto rc = CopyFileExW(m_src.toStdWString().c_str(), m_dst.toStdWString().c_str(),
                           NULL, NULL, NULL, 0);
     if (rc == 0) {
          QString s = getLastErrorMsg();
          // copy failed
          qDebug() << "Failed";
     }
     else {
          qDebug() << "Copied";                    // Always gets hit
     }

     // copy success
     return a.exec();
}
c++
windows
qt
winapi
file-copying
asked on Stack Overflow Aug 12, 2020 by CybeX • edited Aug 12, 2020 by Zhu Song

1 Answer

0

I am posting this answer for the record - hopefully someone else will benefit from it.

The result of GetLastError() returned 3, which according to this means a section in the path cannot be found, not the file, but the path, i.e. a directory. This got me thinking to debug the folder path.

After copying the same file to various directories along the path, and each was detected, I narrowed down the problematic path component 2020-05-19 11.22.30 Someone Person's Zoom Meeting 98661954658 it down to the folder name containing the single-quotation.

This was a happy mistake where I was working in the destination folder However, attempting to make a copy of the directory (to remove the single quote), Windows complained the path was too long (this was a folder that was already copied, containing the problematic file).

Summary

Basically, the source file was fine, the destination directory was created (programatically), but the copying the file to the destination resulted in a path too long for Windows, causing the 'system cannot find path' error (trickery!)

TL;DR:

Destination path was too long, resulting in a possibly misleading error code.

answered on Stack Overflow Aug 12, 2020 by CybeX

User contributions licensed under CC BY-SA 3.0