【问题标题】:CopyFileExW fails with 0 for an existing fileCopyFileExW 失败,现有文件为 0
【发布时间】:2020-12-01 16:48:55
【问题描述】:

上下文:

我有一个应用程序,它使用QDirIterator 搜索目录中的文件,过滤和复制特定文件。

问题:

使用来自QDirIterator::next() 的结果,我使用有效的QFile::exists(QString) 确保文件存在(作为不必要的安全措施)。

但是,当尝试使用CopyFileExW 复制文件时,returns 0 表示文件复制失败。我完全不知道为什么。

文件位置:

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 的转换检查文件内容 -> LPCWSTR 因为FileCopyExW 需要LPCWSTR,建议转换QString -> LPCWSTR here

关于转换,我也尝试过this,但结果相同。这也被建议为用户 c 样式转换的不良做法

健全性测试(在下面的应用程序代码中)全部通过,但 FileCopyExW 总是失败:

“错误0x80070002:系统找不到指定的文件。”

应用程序内的代码:

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;

但是,在自定义测试应用程序中硬编码文件位置确实可以正常工作。

测试应用:

结果成功

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();
}

【问题讨论】:

  • 将本机调试器(例如 windbg 或 cdb)附加到进程,并在 CopyFileExW 上设置断点。当断点命中时,检查传递的参数,尤其是源路径和目标路径。在 x64 中,它们在寄存器 rcx 和 rdx 中传递——例如du @rcx 打印源路径。
  • @ErykSun 感谢您的评论,请参阅我的回答。它根本与转换无关......令人惊讶。

标签: c++ windows qt winapi file-copying


【解决方案1】:

我发布此答案以作记录 - 希望其他人会从中受益。

GetLastError() 的结果返回 3,根据this 表示找不到路径中的某个部分,不是文件,而是路径,即目录。这让我想到调试文件夹路径。

将同一个文件复制到路径上的各个目录并检测到每个目录后,我将有问题的路径组件2020-05-19 11.22.30 Someone Person's Zoom Meeting 98661954658缩小到包含单引号的文件夹名称。

这是我在目标文件夹中工作时的一个快乐错误但是,尝试制作目录的副本(以删除单引号),Windows 抱怨路径太长(这个是一个已经被复制的文件夹,包含有问题的文件)。

总结

基本上,源文件很好,目标目录已创建(以编程方式),但是将文件复制到目标导致 Windows 路径太长,导致“系统找不到路径”错误(诡计!)

TL;DR:

目标路径太长,导致错误代码可能具有误导性。

【讨论】:

  • 我在你的解释中遗漏了一些东西。单引号字符 (') 在 FAT32 和 NTFS 中是有效的文件名字符,并且您在示例中显示的路径都没有接近经典的 MAX_PATH (260) 限制。
  • 顺便说一句,Windows 10 系统可以在系统级别为在清单中声明它们是长路径感知的应用程序启用长路径。此外,以 "\\\\?\\" 为前缀的 vebatim 路径具有大约 32,760 个字符的本机限制,但应首先通过 GetFullPathNameW 规范化,然后再添加前缀以确保文件名正确(无尾随空格和点;无保留DOS 设备名称)并确保路径是完全限定的,带有所有“.”和“..”组件已解析,并且仅使用反斜杠作为路径分隔符。
  • 很高兴您得到了解决方案,感谢您的分享,如果您将它们标记为答案,我将不胜感激,这将对其他社区有益。
  • @ZhuSong 我需要进行一些编辑以便更清楚,但我肯定会这样做:)
猜你喜欢
  • 1970-01-01
  • 2013-04-18
  • 1970-01-01
  • 1970-01-01
  • 2013-07-23
  • 1970-01-01
  • 1970-01-01
  • 2016-08-01
  • 2018-09-25
相关资源
最近更新 更多