硬盘卷路径转换为完整文件路径

5

我有一个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

我已经在网上查找了相关资源,但并没有找到清晰明确的可编程实现方法。如何才能完成此操作呢?

1个回答

5
你应该寻找一个Windows API函数来进行卷管理: 卷管理函数 这里有一个MSDN的例子: 显示卷路径
#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 可以做到这一点,但我需要具体知道哪个方法可以解决问题,并且最好我正在寻找一个编程解决方案。 - Alexandru
2
这个来自 MSDN 的示例似乎正好符合您的需求:http://msdn.microsoft.com/en-us/library/cc542456%28v=vs.85%29.aspx 它显示了系统上每个驱动器的名称和路径,只需选择您要查找的驱动器即可。 - kyflare
这实际上能工作吗?文档中说它需要卷GUID路径,而不是\Device\HarddiskVolume路径。 - Harry Johnston
澄清一下:FindFirst/NextVolume会给你卷名(GUID ?\等),QueryDosDevice会从GUID中给你设备名(\Device\HarddiskVolume),而GetVolumePathNamesForVolumeName会从GUID中给你驱动器号(C:)。 - kyflare
是的,示例代码有效,因为它获取了一个卷GUID路径并将其传递给GetVolumePathNamesForVolumeName。但OP是从设备名称开始的。我不确定GetVolumePathNamesForVolumeName是否适用于设备名称,或者您是否必须枚举卷以查找与其设备名称匹配的卷。 - Harry Johnston

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接