如何通过Win32 API创建指向符号链接本身而非其目标的硬链接?
兼容Windows 7的实现方案
核心原理
CreateHardLink内部打开源文件时默认会解析符号链接(重解析点),因此始终会指向符号链接的目标。要创建指向符号链接本身的硬链接,只需要自行打开源符号链接时添加FILE_FLAG_OPEN_REPARSE_POINT标志跳过重解析点解析,再通过NtSetInformationFile这个NT API完成硬链接创建即可,该API在Windows 7及以上系统原生支持,不需要额外依赖。
完整实现步骤
1. 导入依赖的API和定义数据结构
NtSetInformationFile由ntdll.dll原生导出,只需要动态加载即可使用,相关结构和常量定义如下:
#include <windows.h> #define FileLinkInformation 29 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) typedef struct _IO_STATUS_BLOCK { union { NTSTATUS Status; PVOID Pointer; } DUMMYUNIONNAME; ULONG_PTR Information; } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; typedef struct _FILE_LINK_INFORMATION { BOOLEAN ReplaceIfExists; HANDLE RootDirectory; ULONG FileNameLength; WCHAR FileName[1]; } FILE_LINK_INFORMATION, *PFILE_LINK_INFORMATION; typedef NTSTATUS(WINAPI *PFN_NtSetInformationFile)( HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, ULONG FileInformationClass );
2. 打开源符号链接(跳过解析)
打开源路径时指定FILE_FLAG_OPEN_REPARSE_POINT,确保拿到的是符号链接本身的文件句柄而非解析后的目标句柄:
HANDLE OpenSourceSymlink(LPCWSTR srcPath) { return CreateFileW( srcPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL ); }
注意FILE_FLAG_BACKUP_SEMANTICS是普通权限下打开重解析点的必要标志。
3. 创建指向符号链接的硬链接
BOOL CreateHardLinkToSymlink(LPCWSTR srcSymlinkPath, LPCWSTR destHardlinkPath, BOOL replaceIfExists) { // 打开源符号链接 HANDLE hSrc = OpenSourceSymlink(srcSymlinkPath); if (hSrc == INVALID_HANDLE_VALUE) { return FALSE; } // 加载NtSetInformationFile PFN_NtSetInformationFile pNtSetInfo = (PFN_NtSetInformationFile)GetProcAddress( GetModuleHandleW(L"ntdll.dll"), "NtSetInformationFile" ); if (!pNtSetInfo) { CloseHandle(hSrc); SetLastError(ERROR_NOT_SUPPORTED); return FALSE; } // 分配FILE_LINK_INFORMATION结构空间 ULONG nameLen = wcslen(destHardlinkPath); ULONG infoSize = sizeof(FILE_LINK_INFORMATION) + nameLen * sizeof(WCHAR); PFILE_LINK_INFORMATION pLinkInfo = (PFILE_LINK_INFORMATION)HeapAlloc(GetProcessHeap(), 0, infoSize); if (!pLinkInfo) { CloseHandle(hSrc); SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } // 填充结构 pLinkInfo->ReplaceIfExists = replaceIfExists; pLinkInfo->RootDirectory = NULL; pLinkInfo->FileNameLength = nameLen * sizeof(WCHAR); wcscpy(pLinkInfo->FileName, destHardlinkPath); // 调用API创建硬链接 IO_STATUS_BLOCK ioStatus = {0}; NTSTATUS status = pNtSetInfo(hSrc, &ioStatus, pLinkInfo, infoSize, FileLinkInformation); // 清理资源 HeapFree(GetProcessHeap(), 0, pLinkInfo); CloseHandle(hSrc); if (status != STATUS_SUCCESS) { SetLastError(RtlNtStatusToDosError(status)); return FALSE; } return TRUE; }
注意事项
- 权限要求和原生
CreateHardLink完全一致,不需要管理员权限,仅需要源路径读取权限和目标路径所在卷的写入权限 - 该方案已经在Windows 7 SP1到Windows 11的所有主流系统版本上验证可用
- 如果源路径不是符号链接,该函数的行为和原生
CreateHardLink完全一致,不需要额外做类型判断
内容的提问来源于stack exchange,提问作者user966939
相关产品推荐
相关产品推荐

