您要求使用CopyFileEx API 复制目录。它是一个符号链接的事实直到检查它是一个目录之后才会得到解决,这意味着你不能使用这个 API 来复制一个目录符号链接。
目录符号链接与文件符号链接的处理方式略有不同 - 创建它们时,您必须将额外参数传递给 CreateSymbolicLink API。
API 文档中有关于这种行为的微妙提示,其中指出:
要删除符号链接,请删除文件(使用 DeleteFile 或类似 API)或删除目录(使用 RemoveDirectory 或类似 API),具体取决于使用的符号链接类型。
这强烈表明适用于文件的 API 不适用于目录符号链接。
现在关于如何复制它,我强烈希望您必须再次创建符号链接,此过程是读取符号链接目标,然后在目标中再次创建链接;类似于(根本没有错误处理,将相对链接转换为绝对链接):
HANDLE h = CreateFile(srcFile, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
TCHAR outbuffer[2048];
DWORD written = GetFinalPathNameByHandle(h, outbuffer, 2048, 0);
CreateSymbolicLink(targetFile, outbuffer, SYMBOLIC_LINK_FLAG_DIRECTORY);
CloseHandle(h);
现在要使用底层重解析点数据创建符号链接,事情会变得有点复杂。如果你没有ntifs.h那么你需要定义微软重解析点数据结构(复制自the MSDN page for REPARSE_DATA_STRUCTURE):
typedef struct _REPARSE_DATA_BUFFER {
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union {
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[1];
} SymbolicLinkReparseBuffer;
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[1];
} MountPointReparseBuffer;
struct {
UCHAR DataBuffer[1];
} GenericReparseBuffer;
};
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
我们改变常规:
HANDLE h = CreateFile(srcFile, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, 0);
if (h != INVALID_HANDLE_VALUE) {
char tmpBuffer[32 * 1024];
REPARSE_DATA_BUFFER *repBuffer = reinterpret_cast<REPARSE_DATA_BUFFER *>(tmpBuffer);
DWORD retBytes;
if (DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, 0, 0, (void *)tmpBuffer,
sizeof tmpBuffer, &retBytes, 0)) {
if (repBuffer->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
wchar_t dest[2048];
memcpy(dest, repBuffer->SymbolicLinkReparseBuffer.PathBuffer +
repBuffer->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(wchar_t),
repBuffer->SymbolicLinkReparseBuffer.PrintNameLength);
dest[repBuffer->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(wchar_t)] = 0;
if (!CreateSymbolicLink(targetFile, dest, SYMBOLIC_LINK_FLAG_DIRECTORY)) {
// Error Handling
}
}
}
CloseHandle(h);
} else {
// Open Error Handling
}
它要复杂得多,但它保留了其底层形式的符号链接,即如果它是一个相对链接,那么创建的链接也将是一个相对链接。此外,如果目标不存在,仍然会创建链接。
这仍然没有消除成为 (a) 管理员或 (b) 将组策略编辑为 permit non-admin users to create symbolic links 的需要。