在Windows QDir :: mkpath和QFile :: rename报告成功,尽管失败

时间:2017-07-23 13:29:35

标签: c++ windows qt

我正在尝试为我正在处理的应用添加自动更新功能。我的解决方案在Linux上运行良好,但我在Windows上遇到了奇怪的问题。

在解压缩更新包之后,我尝试使用以下功能将其移动到目标目录:

inline void recursiveMoveOrCopy(QDir source, QDir dest, bool move)
{
    auto files = source.entryInfoList(QDir::Files);
    auto dirs = source.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot);

    // move / copy files
    bool success = QDir{}.mkpath(dest.path());
    if (!success){
        throw std::runtime_error(qs("Could not crate directory %1")
                                 .arg(dest.path()).toStdString());
    }

    qDebug()<<"created directory"<<dest.path();
    dumpvar(QDir{}.exists(dest.path()));

    for (auto& file: files){
        QString sourcePath = file.filePath();
        QString fileName = file.fileName();
        QString destPath = dest.filePath(fileName);
        QString backupPath = destPath + "_bck";
        bool success;
        bool backup = false;
        if (QFile::exists(destPath))
            backup = QFile::rename(destPath, backupPath);
        ON_EXIT{
            if (backup) {
                QFile::remove(destPath);
                QFile::rename(backupPath, destPath);
            }
        };
        if (move) success = QFile::rename(sourcePath, destPath);
        else success = QFile::copy(sourcePath, destPath);

        qDebug()<<qs("move from %1 to %2 was %3").arg(sourcePath, destPath, success?"successful":"not sucessful");

        if (success && backup){
            QFile::remove(backupPath);
            backup = false;
        }
        if (!success){
            throw std::runtime_error(qs("Failed to %1 file %2 to %3")
                                     .arg(move?"move":"copy")
                                     .arg(sourcePath)
                                     .arg(destPath)
                                     .toStdString());
        }
    }

    // recursively move/copy dirs
    for (auto &dir: dirs) recursiveMoveOrCopy(dir.filePath(), dest.filePath(dir.fileName()), move);
}

我用一个只有2个文件和1个目录制作了一个小包进行测试:

$ tree .
.
├── katalog
│   └── plik
└── plik2

当我尝试安装这个&#34;更新&#34;以下内容由移动函数写入调试输出:

"moving C:/Users/piotrek/AppData/Local/Temp/dres-update-image to C:/Program Files (x86)/DRES"
created directory "C:/Program Files (x86)/DRES"
QDir{}.exists(dest.path()) = true
"move from C:/Users/piotrek/AppData/Local/Temp/dres-update-image/plik2 to C:/Program Files (x86)/DRES/plik2 was successful"
created directory "C:/Program Files (x86)/DRES/katalog"
QDir{}.exists(dest.path()) = true
"move from C:/Users/piotrek/AppData/Local/Temp/dres-update-image/katalog/plik to C:/Program Files (x86)/DRES/katalog/plik was successful"

如:每一次操作都成功。但是当我查看目标目录时,目录katalog不存在(但文件plik2确实存在)。

请注意,即使目录不存在,QDir :: exists也会报告它。

这不是权限问题,在我测试时,我修改了C:/Program Files (x86)/DRES以授予Everyone完全访问权限。

请告诉我,我并不疯狂。这到底是怎么回事?

编辑:感谢Rudolfs&#39;建议使用Process Monitor我发现文件实际上是写入C:\Users\piotrek\AppData\Local\VirtualStore\Program Files (x86)\DRES的。到底发生了什么,我该如何解决这个问题?

1 个答案:

答案 0 :(得分:0)

好的,我明白了。

我必须使用以下代码为整个应用禁用VirtualStore虚拟化:

#ifdef Q_OS_WIN32
#include <windows.h>

QString getWinError()
{
    DWORD dw = GetLastError();
    LPWSTR lpMsgBuf = NULL;
    FormatMessageW(
                FORMAT_MESSAGE_ALLOCATE_BUFFER |
                FORMAT_MESSAGE_FROM_SYSTEM |
                FORMAT_MESSAGE_IGNORE_INSERTS,
                NULL,
                dw,
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                (LPWSTR) &lpMsgBuf,
                0, NULL );
    QString str = QString::fromWCharArray(lpMsgBuf);
    LocalFree(lpMsgBuf);
    return str;
}


bool disableVirtualStore()
{

    HANDLE token;
    DWORD tokenInformation = 0;

    if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)){
        qWarning()<<getWinError();
        return FALSE;
    }

    ON_EXIT{
        CloseHandle(token);
    };

    if(!SetTokenInformation(token, TokenVirtualizationEnabled,
                            &tokenInformation, sizeof(tokenInformation))) {
        qWarning()<<getWinError();
        return FALSE;
    }

    return TRUE;
}

#endif

现在我可以正常写入我有写入权限的所有文件夹,而且由于Windows不再对我有关写入权限,我可以启动一个提升的帮助应用程序将文件移动到我没有写入的文件夹权限。