2012-12-11 5 views
8

Chcę skopiować plik do nowej nazwy pliku. Czasami plik źródłowy może być symboliczne (plik) odnośnik, stworzony zFile.Copy() and Symbolic Links

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

używam tego kodu:

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

jednak otrzymuję IOException

Nazwa pliku nie może zostać rozwiązany przez system.

kiedy plik from jest rzeczywiście dowiązaniem symbolicznym.

Jak mogę kodować przypadki, w których plik źródłowy może być plikiem rzeczywistym lub dowiązaniem symbolicznym do pliku?

+0

Hmm, to błąd systemu plików. Coś jak cel linku na innym dysku, którego już nie ma. –

+0

@HansPassant: Wszystko jest lokalne, na jednym dysku, a właśnie utworzyłem łącze po południu. –

+0

@HansPassant: Może to być związane z podobnym pytaniem, które właśnie napisałem: http://stackoverflow.com/q/13831759/141172 –

Odpowiedz

12

Rozszerzając ten blog post, stworzyłem metody rozszerzające, które pobierają DirectoryInfo lub FileInfo, które mogą odnosić się do oryginalnego lub dowiązania symbolicznego i zwracają ciąg wskazujący pełną ścieżkę do oryginalnego pliku.

Code App

Kod aplikacji wprowadza się następujące zmiany:

// Works whether or not file is a symbolic link 
string from = 
    new FileInfo(@"C:\MyPath\ThisIsASymbolicLink.xml").GetSymbolicLinkTarget(); 

Extension kod metody

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(); 
     } 
    } 

Jednostka Testy

[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 Tworzenie Test jednostki danych

należy uruchamiać jako administrator ... wymóg 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

Dzięki za odpowiedź, to naprawdę mi pomogło! Znalazłem problem - SafeFileHandle jest jednorazowe i zapomniałeś użyć albo używając() albo .dispose(). W moim przypadku usunąłem dowiązanie symboliczne po sprawdzeniu rozmiaru pliku docelowego, więc plik nie został natychmiast usunięty, ponieważ wciąż trzymałem uchwyt pliku. – BigScary

+0

@BigScary: Dobry połów. Zaktualizowałem kod. MSDN zaczęło nazywać się niepodawaniem odpowiednich wzorów kodowania w swoich przykładach, które niestety przenieśli niektórzy blogerzy. –