2012-12-11 60 views
8

Tôi muốn sao chép tệp sang tên tệp mới. Đôi khi các tập tin nguồn có thể là một (file) liên kết tượng trưng, ​​được tạo ra vớiFile.Copy() và Liên kết tượng trưng

mklink C:\MyPath\ThisIsASymbolicLink.xml C:\MyPath\ThisIsTheOriginal.xml 

Tôi đang sử dụng mã này:

string from = @"C:\MyPath\ThisIsASymbolicLink.xml"; 
string to = @"C:\MyPath\WantCopyOfOriginalFileHere.xml"; 
File.Copy(from, to, true); 

Tuy nhiên, tôi nhận được một IOException

Tên của hệ thống không thể được giải quyết bởi hệ thống.

khi tệp from thực sự là một liên kết tượng trưng.

Tôi làm cách nào để mã hóa các trường hợp tệp nguồn có thể là tệp thực hoặc liên kết tượng trưng cho tệp?

+0

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. –

+0

@ 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. –

+0

@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 –

Trả lời

12

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 
+1

Cảm ơn bạn đã trả lời, điều này thực sự đã giúp tôi! Tuy nhiên, tôi đã tìm thấy một vấn đề - SafeFileHandle là dùng một lần và bạn quên sử dụng hoặc sử dụng() hoặc .dispose(). Trong trường hợp của tôi, tôi đã xóa một liên kết tượng trưng sau khi kiểm tra kích thước của tệp đích, và vì vậy tệp đó không thực sự bị xóa ngay lập tức vì tôi vẫn đang giữ một tay cầm cho tệp. – BigScary

+0

@BigScary: Bắt tốt. Tôi đã cập nhật mã. MSDN bắt đầu một thói quen không bao gồm các mẫu mã hóa thích hợp trong các ví dụ của họ mà không may một số blogger đã chuyển sang. –

Các vấn đề liên quan