【问题标题】:Problem with ReadDirectoryChangesW filename when saving in Visual Studio 2019在 Visual Studio 2019 中保存时 ReadDirectoryChangesW 文件名出现问题
【发布时间】:2020-05-20 18:42:26
【问题描述】:

我正在使用 ReadDirectoryChangesW 来检测目录中的 .hlsl 文件何时更改。使用 Notepad/Notepad++/VSCode 保存文件时效果很好,但是当我尝试在 Visual Studio 2019 中保存文件时,我得到一个奇怪的输出。文件夹名称被保留,但文件名和扩展名是乱码。

这就是我的代码的样子。它在应用程序启动时调用的单独线程上运行。

void ShaderReloadWatcher::BeginWatching()
{
    m_thread = std::thread(&ShaderReloadWatcher::WatchShaderDirectory, this);
}

线程主循环:

void ShaderReloadWatcher::WatchShaderDirectory()
{
    LPCTSTR shaderDir = "../Source/Shaders";
    HANDLE shaderFolderHandle = CreateFile(
        shaderDir,
        GENERIC_READ,
        FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
        OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
        NULL);

    char notifyBuffer[1024];

    OVERLAPPED ovl = { 0 };
    ovl.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    DWORD error = ReadDirectoryChangesW(
        shaderFolderHandle,
        &notifyBuffer,
        sizeof(notifyBuffer),
        TRUE,
        FILE_NOTIFY_CHANGE_LAST_WRITE,
        NULL, &ovl, NULL);


    while (true)
    {
        DWORD result = WaitForSingleObject(ovl.hEvent, 0);

        switch (result)
        {
        case WAIT_TIMEOUT:
        {
            break;
        }

        case WAIT_OBJECT_0:
        {
            DWORD bytesTransferred;
            GetOverlappedResult(shaderFolderHandle, &ovl, &bytesTransferred, FALSE);

            OnDirectoryFileChange(notifyBuffer);

            ResetEvent(ovl.hEvent);

            DWORD error = ReadDirectoryChangesW(
                shaderFolderHandle,
                &notifyBuffer,
                sizeof(notifyBuffer),
                TRUE,
                FILE_NOTIFY_CHANGE_LAST_WRITE,
                NULL, &ovl, NULL);

            break;
        }

        }
    }

    CloseHandle(shaderFolderHandle);
}

OnDirectoryFileChange:

void ShaderReloadWatcher::OnDirectoryFileChange(char* buffer)
{
    DWORD offset = 0;
    FILE_NOTIFY_INFORMATION* fileNotifyInfo = nullptr;
    char fileName[1024];

    do 
    {
        memset(fileName, NULL, sizeof(fileName));
        fileNotifyInfo = reinterpret_cast<FILE_NOTIFY_INFORMATION*>(&buffer[offset]);

        WideCharToMultiByte(CP_ACP, NULL, fileNotifyInfo->FileName, fileNotifyInfo->FileNameLength / sizeof(WCHAR), fileName, sizeof(fileName), NULL, NULL);

        printf("%s\n", fileName);

        offset += fileNotifyInfo->NextEntryOffset;
    } while (fileNotifyInfo->NextEntryOffset != 0);
}

这是我使用 Notepad++ 保存(一次)时得到的输出:

D3D11\light_pixel.hlsl
D3D11\light_pixel.hlsl

我了解到,由于文本编辑器写入文件的方式,它被多次打印,所以这是意料之中的。

但是,当使用 Visual Studio 保存时,我得到了这个。

D3D11\sfpwwek3.vs4~
D3D11
D3D11
D3D11
D3D11

文件夹名称(D3D11)看起来不错,但文件名全是乱码。每次都是不同的乱码:

D3D11\5uuwurye.nel~
D3D11
D3D11
D3D11
D3D11

编码设置为西欧 (CP 1252),我尝试使用不同的编码,如 Unicode (CP 65001),但仍然得到相同的结果。

我查看了 fileNotifyInfo-&gt;FileName 值,它似乎从一开始就是胡言乱语,因此导致此问题的 WideCharToMultiByte 转换似乎没有任何问题。

非常感谢您对理解此问题的任何帮助,谢谢!

【问题讨论】:

  • 会不会是临时文件名?它们通常由随机字符生成。如果在关闭 VS 的同时让程序运行,它会变成可识别的东西吗?或许您也应该打开嗅探所有目录更改,以便更清楚地了解发生了什么。
  • 循环是错误的,为什么你完全以异步模式打开文件夹而不是同步,文件名 - 不是 0 终止 - 所以使用 %s 是错误的。你不检查错误
  • @TedLyngmo 我刚刚通过添加FILE_NOTIFY_CHANGE_FILE_NAME 标志进行了尝试,看来您是正确的!它现在输出更多的东西,包括正确的文件名。谢谢!
  • @soomi 太好了。 :-)
  • @RbMm 我再看看循环,谢谢。文件名是 0 终止,因为我在复制值之前将 memset 设置为 NULL。

标签: c++ windows visual-studio visual-studio-2019


【解决方案1】:

问题已解决 - 感谢@Ted Lyngmo。

我只是在听写更改,而 Visual Studio 似乎首先写入一个临时文件名,然后将其重命名为正确的文件名。我添加了FILE_NOTIFY_CHANGE_FILE_NAME,现在输出如下所示:

D3D11\cn42zizu.cht~
D3D11\cn42zizu.cht~
D3D11
D3D11\light_pixel.hlsl~RF62be0e8e.TMP
D3D11\light_pixel.hlsl~RF62be0e8e.TMP
D3D11\light_pixel.hlsl
D3D11\light_pixel.hlsl~RF62be0e8e.TMP
D3D11
D3D11\cn42zizu.cht~
D3D11\light_pixel.hlsl
D3D11
D3D11\light_pixel.hlsl~RF62be0e8e.TMP
D3D11

很多消息,但正确的名字在那里!

【讨论】:

  • 在您的打印输出中,您可能希望在输出中包含FILE_NOTIFY_INFORMATION::Action 字段的值,这样您就可以看到哪个事件是写入,哪个事件是重命名,例如:printf("%u: %s\n", fileNotifyInfo-&gt;Action, fileName);
  • 而且仅供参考,如果您使用 %S%ls%ws 而不是 %s,则不需要 WideCharToMultiByte()printf() 可以打印 Unicode 字符串,例如: printf("%u: %.*S\n", fileNotifyInfo-&gt;Action, fileNotifyInfo-&gt;FileNameLength / sizeof(WCHAR), fileNotifyInfo-&gt;FileName); 或者你可以使用 wprintf() 代替,例如:wprintf(L"%u: %.*s\n", fileNotifyInfo-&gt;Action, fileNotifyInfo-&gt;FileNameLength / sizeof(WCHAR), fileNotifyInfo-&gt;FileName);
  • @Remy Lebeau 谢谢。我需要字符串转换以供以后使用,但知道printf 适用于 unicode 字符串非常有用,我会记住它以备后用!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多