如何使用 GetFinalPathNameByHandle 来解析指向本地目录的符号链接?

Posted

技术标签:

【中文标题】如何使用 GetFinalPathNameByHandle 来解析指向本地目录的符号链接?【英文标题】:How does one use GetFinalPathNameByHandle to resolve a symbolic link to a local directory? 【发布时间】:2018-05-04 15:03:13 【问题描述】:

我有以下代码:

std::wstring GetSymbolicLinkTarget(std::wstring const& linkPath)

    TCHAR path[MAX_PATH];
    CAutoFile hFile = CreateFile( linkPath.c_str(),
        FILE_READ_EA,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        0,
        OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_REPARSE_POINT | FILE_FLAG_OPEN_REPARSE_POINT,
        0);
    if (INVALID_HANDLE_VALUE != hFile)
    
        auto rcode = GetFinalPathNameByHandle(hFile, path, MAX_PATH, FILE_NAME_NORMALIZED);
        switch (rcode)
        
        case ERROR_PATH_NOT_FOUND:
            return std::wstring();
        case ERROR_NOT_ENOUGH_MEMORY:
            return std::wstring();
        case ERROR_INVALID_PARAMETER:
            return std::wstring();
        case ERROR_ACCESS_DENIED:
            return std::wstring();
        default:
            break;
        ;

        if (path[0] == '\\' && path[1] == '\\' && path[2] == '?' && path[3] == '\\')
            return std::wstring(path + 4, path + MAX_PATH);
        else
            return std::wstring(path, path + MAX_PATH);
    

    return std::wstring();

我像这样创建一个符号链接:

e:
cd Projects\ProjectA\IDE_Files
mklink /D src ..

然后在一些代码中我调用上面的函数,根据文档说:

e:\Projects\ProjectA\IDE_Files\src

应该解决:

e:\Projects\ProjectA

它只是返回输入路径:

e:\Projects\ProjectA\IDE_Files\src

rcode 结果包含路径中的字符数。不是错误代码。

为什么无法返回我期望的结果?

【问题讨论】:

To do this, you open the symbolic link。所以删除 FILE_FLAG_OPEN_REPARSE_POINT 来解决问题。 谢谢汉斯!把它作为一个答案,我会接受的。 随意使用您发现的内容来完成您开始的 Q+A。 另外,请求 0 访问,而不是 FILE_READ_EACreateFile 实际上至少会请求SYNCHRONIZE | FILE_READ_ATTRIBUTES,但让它为你决定。并且请求读写删除共享只是源代码中毫无意义的文本,因为您没有请求数据访问;使用 0 表示共享模式。最后,不要在属性中包含FILE_ATTRIBUTE_REPARSE_POINT。你不能设置这个属性;它是在创建重解析点时设置的。 【参考方案1】:

这个答案来自Hans Passant:

问题主要是由于使用了FILE_FLAG_OPEN_REPARSE_POINT 标志,它执行以下操作:

OpenFileById 函数将打开文件或重新解析 点,取决于 FILE_FLAG_OPEN_REPARSE_POINT 标志的使用。 [source].

这是最终代码(上面的 cmets 进行了多项修复。):

std::wstring GetSymbolicLinkTarget(std::wstring const& linkPath)

    TCHAR path[MAX_PATH];
    CAutoFile hFile = CreateFile( linkPath.c_str(),
        0,
        0,  
        0,
        OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS,
        0);
    if (INVALID_HANDLE_VALUE != hFile)
    
        auto rcode = GetFinalPathNameByHandle(hFile, path, MAX_PATH, FILE_NAME_NORMALIZED);
        if (rcode)
        
            if (path[0] == '\\' && path[1] == '\\' && path[2] == '?' && path[3] == '\\')
                return std::wstring(path + 4, path + rcode);
            else
                return std::wstring(path, path + rcode);
        
    

    return std::wstring();

【讨论】:

GetFinalPathNameByHandle 返回的不是错误代码,而是字符串的长度.. 如果它返回 0,您需要调用 GetLastError 并且它的值已经与 ERROR_* 进行比较 直接来自documentation:"如果函数成功,返回值是lpszFilePath接收到的字符串的长度,在TCHARs。[...]如果函数因任何其他原因失败,返回值为零。要获取扩展错误信息,请调用 GetLastError。" 此外,您的错误报告(某些错误条件为空字符串,其他错误条件为异常)的混合是不寻常的.它增加了调用代码的复杂性。这与抽象应该做的相反。 是的,我没有意识到返回值不包含错误代码。至于开关...我正在使用它们进行调试以查看可能发生的代码...

以上是关于如何使用 GetFinalPathNameByHandle 来解析指向本地目录的符号链接?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用本机反应创建登录以及如何验证会话

如何在自动布局中使用约束标识符以及如何使用标识符更改约束? [迅速]

如何使用 AngularJS 的 ng-model 创建一个数组以及如何使用 jquery 提交?

如何使用laravel保存所有行数据每个行名或相等

如何使用 Math.Net 连接矩阵。如何使用 Math.Net 调用特定的行或列?

WSARecv 如何使用 lpOverlapped?如何手动发出事件信号?