【问题标题】:Hard disk volume path to full file path硬盘卷路径到完整文件路径
【发布时间】:2014-12-21 19:51:06
【问题描述】:

我有一个 MSVC++ 应用程序,它以以下字符串格式吐出其他应用程序打开的文件句柄的硬盘卷路径:

\Device\HarddiskVolume4\Users\User\Documents\Visual Studio 2013\Projects\FileLocker\FileLocker\bin\Debug\Test.txt

我想将这些路径转换为 ​​Windows 中这些文件的完整路径。例如,我想将上面的硬盘卷路径转换为完整的 Windows 文件路径及其对应的驱动器号:

C:\Users\User\Documents\Visual Studio 2013\Projects\FileLocker\FileLocker\bin\Debug\Test.txt

我在网上查看过,但我没有找到任何明确的资源来以编程方式执行此操作。怎么办?

【问题讨论】:

    标签: c++ .net windows visual-c++ filepath


    【解决方案1】:

    您应该寻找用于卷管理的 Windows API 函数之一:

    Volume Management Functions

    这是一个来自 MSDN 的示例:

    Displaying Volume Paths

    #include <windows.h>
    #include <stdio.h>
    
    void DisplayVolumePaths(
            __in PWCHAR VolumeName
            )
    {
        DWORD  CharCount = MAX_PATH + 1;
        PWCHAR Names     = NULL;
        PWCHAR NameIdx   = NULL;
        BOOL   Success   = FALSE;
    
        for (;;) 
        {
            //
            //  Allocate a buffer to hold the paths.
            Names = (PWCHAR) new BYTE [CharCount * sizeof(WCHAR)];
    
            if ( !Names ) 
            {
                //
                //  If memory can't be allocated, return.
                return;
            }
    
            //
            //  Obtain all of the paths
            //  for this volume.
            Success = GetVolumePathNamesForVolumeNameW(
                VolumeName, Names, CharCount, &CharCount
                );
    
            if ( Success ) 
            {
                break;
            }
    
            if ( GetLastError() != ERROR_MORE_DATA ) 
            {
                break;
            }
    
            //
            //  Try again with the
            //  new suggested size.
            delete [] Names;
            Names = NULL;
        }
    
        if ( Success )
        {
            //
            //  Display the various paths.
            for ( NameIdx = Names; 
                  NameIdx[0] != L'\0'; 
                  NameIdx += wcslen(NameIdx) + 1 ) 
            {
                wprintf(L"  %s", NameIdx);
            }
            wprintf(L"\n");
        }
    
        if ( Names != NULL ) 
        {
            delete [] Names;
            Names = NULL;
        }
    
        return;
    }
    
    void __cdecl wmain(void)
    {
        DWORD  CharCount            = 0;
        WCHAR  DeviceName[MAX_PATH] = L"";
        DWORD  Error                = ERROR_SUCCESS;
        HANDLE FindHandle           = INVALID_HANDLE_VALUE;
        BOOL   Found                = FALSE;
        size_t Index                = 0;
        BOOL   Success              = FALSE;
        WCHAR  VolumeName[MAX_PATH] = L"";
    
        //
        //  Enumerate all volumes in the system.
        FindHandle = FindFirstVolumeW(VolumeName, ARRAYSIZE(VolumeName));
    
        if (FindHandle == INVALID_HANDLE_VALUE)
        {
            Error = GetLastError();
            wprintf(L"FindFirstVolumeW failed with error code %d\n", Error);
            return;
        }
    
        for (;;)
        {
            //
            //  Skip the \\?\ prefix and remove the trailing backslash.
            Index = wcslen(VolumeName) - 1;
    
            if (VolumeName[0]     != L'\\' ||
                VolumeName[1]     != L'\\' ||
                VolumeName[2]     != L'?'  ||
                VolumeName[3]     != L'\\' ||
                VolumeName[Index] != L'\\') 
            {
                Error = ERROR_BAD_PATHNAME;
                wprintf(L"FindFirstVolumeW/FindNextVolumeW returned a bad path: %s\n", VolumeName);
                break;
            }
    
            //
            //  QueryDosDeviceW does not allow a trailing backslash,
            //  so temporarily remove it.
            VolumeName[Index] = L'\0';
    
            CharCount = QueryDosDeviceW(&VolumeName[4], DeviceName, ARRAYSIZE(DeviceName)); 
    
            VolumeName[Index] = L'\\';
    
            if ( CharCount == 0 ) 
            {
                Error = GetLastError();
                wprintf(L"QueryDosDeviceW failed with error code %d\n", Error);
                break;
            }
    
            wprintf(L"\nFound a device:\n %s", DeviceName);
            wprintf(L"\nVolume name: %s", VolumeName);
            wprintf(L"\nPaths:");
            DisplayVolumePaths(VolumeName);
    
            //
            //  Move on to the next volume.
            Success = FindNextVolumeW(FindHandle, VolumeName, ARRAYSIZE(VolumeName));
    
            if ( !Success ) 
            {
                Error = GetLastError();
    
                if (Error != ERROR_NO_MORE_FILES) 
                {
                    wprintf(L"FindNextVolumeW failed with error code %d\n", Error);
                    break;
                }
    
                //
                //  Finished iterating
                //  through all the volumes.
                Error = ERROR_SUCCESS;
                break;
            }
        }
    
        FindVolumeClose(FindHandle);
        FindHandle = INVALID_HANDLE_VALUE;
    
        return;
    }
    

    澄清一下:

    卷名(或 GUID)类似于 \\?\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}

    设备名称类似于\Device\HarddiskVolume1

    驱动器号类似于C:

    FindFirst/NextVolume 为您提供卷名列表。

    QueryDosDevice 为您提供来自卷名称的设备名称。

    GetVolumePathNamesForVolumeName 为您提供卷名中的驱动器号。

    【讨论】:

    • 我知道一定有一些 API 可以做到这一点,但我需要具体知道哪种方法可以解决问题,理想情况下我正在寻找一种编程解决方案。
    • 这个来自 MSDN 的示例似乎完全符合您的要求:msdn.microsoft.com/en-us/library/cc542456%28v=vs.85%29.aspx 它显示系统上每个驱动器的名称和路径,只需选择您要查找的驱动器即可。
    • 这真的有效吗?文档说它需要一个卷 GUID 路径,而不是 \Device\HarddiskVolume 路径。
    • 澄清一下:FindFirst/NextVolume 为您提供卷名(GUID \?\\etc),QueryDosDevice 为您提供来自 GUID 的设备名称(\Device\HarddiskVolume),GetVolumePathNamesForVolumeName 提供来自 GUID 的驱动器号 (C:)。
    • 是的,所以示例代码有效,因为它获取卷 GUID 路径并将其传递给 GetVolumePathNamesForVolumeName。但是 OP 以设备名称开头。我不确定 GetVolumePathNamesForVolumeName 是否适用于设备名称,或者您是否必须枚举卷以查找设备名称匹配的卷。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-16
    • 2016-08-19
    • 2011-06-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多