Ampliando questo blog post, ho creato metodi di estensione che prendere un DirectoryInfo o FileInfo che può riferirsi sia un originale o un collegamento simbolico, e restituire una stringa che indica il nome del percorso completo del file originale.
App Codice
Il codice dell'applicazione è modificato come segue:
// Works whether or not file is a symbolic link
string from =
new FileInfo(@"C:\MyPath\ThisIsASymbolicLink.xml").GetSymbolicLinkTarget();
estensione Metodo Codice
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 test
[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 per creare unità di Test Data
deve essere eseguito come amministratore ... un requisito di 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, questo è un contrattempo del file system. Qualcosa come l'obiettivo di collegamento su un'altra unità che non è più presente. –
@HansPassant: è tutto locale, su una singola unità, e ho appena creato il collegamento questo pomeriggio. –
@HansPassant: questo potrebbe essere correlato a una domanda simile che ho appena postato: http://stackoverflow.com/q/13831759/141172 –