Mở rộng trên blog post này, tôi đã tạo các phương thức mở rộng có một DirectoryInfo hoặc FileInfo có thể tham chiếu đến liên kết gốc hoặc liên kết tượng trưng và trả về một chuỗi cho biết tên đường dẫn đầy đủ của tệp gốc.
App Mã
Các mã ứng dụng được sửa đổi như sau:
// Works whether or not file is a symbolic link
string from =
new FileInfo(@"C:\MyPath\ThisIsASymbolicLink.xml").GetSymbolicLinkTarget();
Extension Method Mã
private const int FILE_SHARE_READ = 1;
private const int FILE_SHARE_WRITE = 2;
private const int CREATION_DISPOSITION_OPEN_EXISTING = 3;
private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
// http://msdn.microsoft.com/en-us/library/aa364962%28VS.85%29.aspx
[DllImport("kernel32.dll", EntryPoint = "GetFinalPathNameByHandleW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int GetFinalPathNameByHandle(IntPtr handle, [In, Out] StringBuilder path, int bufLen, int flags);
// http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeFileHandle CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode,
IntPtr SecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
public static string GetSymbolicLinkTarget(this FileSystemInfo symlink)
{
using (SafeFileHandle fileHandle = CreateFile(symlink.FullName, 0, 2, System.IntPtr.Zero, CREATION_DISPOSITION_OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, System.IntPtr.Zero))
{
if (fileHandle.IsInvalid)
throw new Win32Exception(Marshal.GetLastWin32Error());
StringBuilder path = new StringBuilder(512);
int size = GetFinalPathNameByHandle(fileHandle.DangerousGetHandle(), path, path.Capacity, 0);
if (size < 0)
throw new Win32Exception(Marshal.GetLastWin32Error());
// The remarks section of GetFinalPathNameByHandle mentions the return being prefixed with "\\?\"
// More information about "\\?\" here -> http://msdn.microsoft.com/en-us/library/aa365247(v=VS.85).aspx
if (path[0] == '\\' && path[1] == '\\' && path[2] == '?' && path[3] == '\\')
return path.ToString().Substring(4);
else
return path.ToString();
}
}
Unit Tests
[TestClass]
public class SymlinkTest
{
[TestInitialize]
public void SetupFiles()
{
if (!File.Exists(@"C:\Temp\SymlinkUnitTest\Original.txt")) throw new Exception("Run Symlinksetup.bat as Admin to create test data.");
}
[TestMethod]
public void OrdinaryFile()
{
string file = @"C:\Temp\SymlinkUnitTest\Original.txt";
string actual = new FileInfo(file).GetSymbolicLinkTarget();
Assert.IsTrue(actual.EndsWith(@"SymlinkUnitTest\Original.txt"));
}
[TestMethod]
public void FileSymlink()
{
string file = @"C:\Temp\SymlinkUnitTest\Symlink.txt";
string actual = new FileInfo(file).GetSymbolicLinkTarget();
Assert.IsTrue(actual.EndsWith(@"SymlinkUnitTest\Original.txt"));
}
[TestMethod]
public void OrdinaryDirectory()
{
string dir = @"C:\Temp\SymlinkUnitTest";
string actual = new DirectoryInfo(dir).GetSymbolicLinkTarget();
Assert.IsTrue(actual.EndsWith(@"SymlinkUnitTest"));
}
[TestMethod]
public void DirectorySymlink()
{
string dir = @"C:\Temp\SymlinkUnitTest";
string actual = new DirectoryInfo(dir).GetSymbolicLinkTarget();
Assert.IsTrue(actual.EndsWith(@"SymlinkUnitTest"));
}
}
Batch File để tạo Unit Test dữ liệu
Phải Run as administrator ... một yêu cầu của mklink.
@Echo Off
Echo Must be run as Administrator (due to mklink)
mkdir C:\Temp
mkdir C:\Temp\SymlinkUnitTest
c:
cd C:\Temp\SymlinkUnitTest
echo Original File>Original.txt
mklink Symlink.txt Original.txt
mklink /D C:\Temp\SymlinkUnitTest\SymDir C:\Temp\SymlinkUnitTest
Hmm, đây là sự cố hệ thống tệp. Một cái gì đó giống như mục tiêu liên kết trên một ổ đĩa khác không còn tồn tại nữa. –
@ HansPassant: Đó là tất cả địa phương, trên một ổ đĩa duy nhất và tôi vừa tạo liên kết vào chiều nay. –
@HansPassant: Điều này có thể liên quan đến một câu hỏi tương tự mà tôi vừa đăng: http://stackoverflow.com/q/13831759/141172 –