2011-02-03 8 views
10

Powiel możliwe:
Best way to determine if two path reference to same file in C#Jak mogę określić, że formaty kilka ścieżek plików wskazują na tej samej lokalizacji fizycznej

Istnieje kilka sposobów, aby określić lokalizację katalogu.

Na przykład:
\\ nazwa_komputera \ c $ \ ROOTPATH ​​\ subścieżkę
\\ nazwa_komputera \ NAZWA_UDZIAŁU (wskazuje na udział podścieżkę)
C: \ ROOTPATH ​​\ subścieżkę
subPath (ścieżka względna, jeśli już w C: \ rootPath

Muszę stwierdzić, że wszystkie te ścieżki są "równe" sobie nawzajem (są tak naprawdę tą samą fizyczną lokalizacją na dysku twardym).

Czy jest jakiś sposób mogę to zrobić w C#?

+1

do rozwiązywania względnych ścieżek, można użyć 'System.IO.Path.GetFullPath' – Jimmy

Odpowiedz

5

Jak podaje Oded, jest to trudne do zrobienia w .Net. Możesz ewentualnie oszukiwać (w zależności od twoich dokładnych wymagań i uprawnień itp.), Pisząc plik z długimi losowo wygenerowanymi nazwami plików do lokalizacji, a następnie sprawdzając, czy możesz go zobaczyć z innej lokalizacji. Trochę hackowania, ale wydaje mi się, że to całkiem niezły test dźwiękowy, zamiast polegać na rozwiązywaniu mapowanych dysków itp.

Ok wiele przeprosin za VB - to wszystko, co mam na tym małym netbooku ... C# nie „t być zbyt różne ...

wykorzystanie np

If sameLocation("\\machineName\c$\rootPath\subPath","\\machineName\shareName") Then...

Public Function sameLocation(ByVal sPath1 As String, ByVal sPath2 As String) As TriState 
    Dim sFile As String = randomFilename() 
    Dim sFullPath1 As String = sPath1 & "\" & sFile 
    Dim sFullPath2 As String = sPath2 & "\" & sFile 
    Dim bReturn As Boolean = False 
    Try 
     Dim fs As New FileStream(sFullPath1, FileMode.CreateNew) 
     fs.Close() 
    Catch ex As Exception 
     Return TriState.UseDefault 
    End Try 

    Try 
     bReturn = File.Exists(sFullPath2) 
    Catch ex As Exception 
     Return TriState.UseDefault 
    End Try 

    File.Delete(sFullPath1) 
    Return bReturn 
End Function 

Public Function randomFilename() As String 
    Dim r As New Random 
    Randomize(My.Computer.Clock.TickCount) 
    Dim sb As New StringBuilder 
    Dim chars As Int16 = 100 
    While chars > 0 
     chars -= 1 
     sb.Append(Chr(r.Next(26) + 65)) 
    End While 
    Return sb.ToString 
End Function 

można dodać więcej bezpieczeństwa, czyli czytanie datownik itd ...

+1

Prawdopodobnie najlepsza odpowiedź, którą znajdziesz, JEŚLI twój program i uruchomiony użytkownik mają prawa do odczytu/zapisu do tych folderów. – KeithS

+0

Nigdy nie dowiesz się w ten sposób, jeśli sama nazwa pliku jest aliasem. Tylko wtedy, gdy aliasing jest gdzieś na ścieżce. –

+0

Proszę zostawiać komentarze przy opuszczaniu! –

0

Nie ma natywnego sposobu na zrobienie tego w .NET - jest to po prostu zbyt niski poziom.

Możesz skorzystać z interfejsu API systemu Windows, aby to osiągnąć (patrząc na i-węzeł katalogu lub jakiś inny identyfikator), ale nie wiem, co API mogłoby to ujawnić.

+1

AFAIK nie ma żadnych i-węzłów w systemie Windows. –

+0

@Al - Tak naprawdę nie wiem, czy istnieje, czy nie, dlatego powiedziałem "lub jakiś inny identyfikator". – Oded

+0

FAT, dla jednego, nie ma unikalnego identyfikatora pliku z wyjątkiem jego nazwy. I kto wie, jakie dziwne systemy plików mogą być dostępne przez sieć. –

0

AKAIK można nawet zmapować ten sam napęd na wiele liter dysków lub podkatalogów. Również współdzielone katalogi sieciowe mogą być mnożone i nigdy nie wiadomo, czy są takie same, czy nie.

Może mógłbyś dodać informacje, dlaczego musisz to wiedzieć.

4

Musisz użyć GetFileInformationByHandle.
Sprawdź to answer w pomocy StackOverflow i MSDN.

Oto metoda pisałem, że współpracuje z katalogów:

using System; 
using System.Runtime.InteropServices; 

namespace CompareByPath  
{  
    public static class DirectoryHelper 
    { 
    // all user defined types copied from 
    // http://pinvoke.net/default.aspx/kernel32.CreateFile 
    // http://pinvoke.net/default.aspx/kernel32.GetFileInformationByHandle 
    // http://pinvoke.net/default.aspx/kernel32.CloseHandle 

    public const short INVALID_HANDLE_VALUE = -1; 

    struct BY_HANDLE_FILE_INFORMATION 
    { 
     public uint FileAttributes; 
     public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime; 
     public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime; 
     public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime; 
     public uint VolumeSerialNumber; 
     public uint FileSizeHigh; 
     public uint FileSizeLow; 
     public uint NumberOfLinks; 
     public uint FileIndexHigh; 
     public uint FileIndexLow; 
    } 

    [Flags] 
    public enum EFileAccess : uint 
    { 
     GenericRead = 0x80000000, 
     GenericWrite = 0x40000000, 
     GenericExecute = 0x20000000, 
     GenericAll = 0x10000000 
    } 

    [Flags] 
    public enum EFileShare : uint 
    { 
     None = 0x00000000, 
     Read = 0x00000001, 
     Write = 0x00000002, 
     Delete = 0x00000004 
    } 

    [Flags] 
    public enum EFileAttributes : uint 
    { 
     Readonly = 0x00000001, 
     Hidden = 0x00000002, 
     System = 0x00000004, 
     Directory = 0x00000010, 
     Archive = 0x00000020, 
     Device = 0x00000040, 
     Normal = 0x00000080, 
     Temporary = 0x00000100, 
     SparseFile = 0x00000200, 
     ReparsePoint = 0x00000400, 
     Compressed = 0x00000800, 
     Offline = 0x00001000, 
     NotContentIndexed = 0x00002000, 
     Encrypted = 0x00004000, 
     Write_Through = 0x80000000, 
     Overlapped = 0x40000000, 
     NoBuffering = 0x20000000, 
     RandomAccess = 0x10000000, 
     SequentialScan = 0x08000000, 
     DeleteOnClose = 0x04000000, 
     BackupSemantics = 0x02000000, 
     PosixSemantics = 0x01000000, 
     OpenReparsePoint = 0x00200000, 
     OpenNoRecall = 0x00100000, 
     FirstPipeInstance = 0x00080000 
    } 

    public enum ECreationDisposition : uint 
    { 
     New = 1, 
     CreateAlways = 2, 
     OpenExisting = 3, 
     OpenAlways = 4, 
     TruncateExisting = 5 
    } 

    [DllImport("kernel32.dll", SetLastError = true)] 
    static extern bool GetFileInformationByHandle(IntPtr hFile, out  BY_HANDLE_FILE_INFORMATION lpFileInformation); 

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)] 
    static extern IntPtr CreateFile(String lpFileName, UInt32 dwDesiredAccess, UInt32 dwShareMode, IntPtr lpSecurityAttributes, UInt32 dwCreationDisposition, UInt32 dwFlagsAndAttributes, IntPtr hTemplateFile); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    static extern bool CloseHandle(IntPtr hObject); 

    public static bool CompareDirectories(string d1, string d2) 
    { 
     bool result = false; 

     BY_HANDLE_FILE_INFORMATION info1; 
     BY_HANDLE_FILE_INFORMATION info2; 

     IntPtr fileHandle1 = CreateFile(d1, (uint)EFileAccess.GenericRead, (uint)EFileShare.Read, IntPtr.Zero, (uint)ECreationDisposition.OpenExisting, (uint)(EFileAttributes.Directory | EFileAttributes.BackupSemantics), IntPtr.Zero); 
     if (fileHandle1.ToInt32() != INVALID_HANDLE_VALUE) 
     { 
     bool rc = GetFileInformationByHandle(fileHandle1, out info1); 
     if (rc) 
     { 
      IntPtr fileHandle2 = CreateFile(d2, (uint)EFileAccess.GenericRead, (uint)EFileShare.Read, IntPtr.Zero, (uint)ECreationDisposition.OpenExisting, (uint)(EFileAttributes.Directory | EFileAttributes.BackupSemantics), IntPtr.Zero); 
      if (fileHandle2.ToInt32() != INVALID_HANDLE_VALUE) 
      { 
      rc = GetFileInformationByHandle(fileHandle2, out info2); 
      if (rc) 
      { 
       if ((info1.FileIndexHigh == info2.FileIndexHigh) && 
        (info1.FileIndexLow == info2.FileIndexLow) && 
        (info1.VolumeSerialNumber == info2.VolumeSerialNumber)) 
       { 
       result = true; 
       } 
      } 
      } 

      CloseHandle(fileHandle2); 
     } 
     } 

     CloseHandle(fileHandle1); 

     return result; 
    } 
    } 
} 
0

Dobre pytanie, nie może być elegancka odpowiedź.

Najlepsze, co mogę ci wskazać na Twojej drodze, to instrukcja wiersza polecenia z net share. Jeśli można przechwycić i przetrawić tekst wygenerowany przez to polecenie programowo, można odwzorować udziały sieciowe 1: 1 w swoich lokalnych folderach docelowych. Następnie możesz wyszukać wystąpienia tych map udziału w danej ścieżce i zastąpić je odpowiednikami lokalnego folderu przed wykonaniem podstawowego porównania ciągów.

2

w Windows Istnieje wiele systemów aliasing:

  • krótki w porównaniu długie nazwy
  • dowiązania i hardlinki
  • nazw UNC vs. zmapowane dyski
  • kilka dyski mapowane do ścieżki samej sieci
  • d: \ folder \ ścieżki vs. \? \ D \ folder \ ścieżki
  • Punkty mocowania NTFS

I dowolne z nich może pojawić się na dowolnym poziomie w drzewie katalogów. W .NET możesz rozwiązać niektóre z nich, ale nie inne.

Jako hackish sposób, spróbuj zablokować/odblokować. Zablokuj plik jako nazwę 1, spróbuj otworzyć jako nazwę 2, upewnij się, że nie działa. Następnie odblokuj, spróbuj otworzyć ponownie, upewnij się, że się udało. I tak dalej, kilka razy, aby uniknąć fałszywych trafień. W przeciwieństwie do sposobu El Ronnoco, ten wykrywa aliasing na poziomie ścieżki i na poziomie pliku.

W niektórych sieciowych systemach plików blokowanie może nie być w ogóle obsługiwane. Ponadto może to zająć trochę czasu - każda operacja blokady/odblokowania/otwarcia jest połączeniem sieciowym.

Ale to naprawdę zależy od Twoich wymagań. Jeśli krótkie i długie nazwy to wszystko, z czym musisz sobie poradzić, jest to przesada.

EDYCJA: dodatkowe komplikacje, jeśli plik jest tylko do odczytu lub jest już otwarty przez inną osobę.

+0

+1 Podoba mi się odpowiedź, ale nie jestem pewien, co masz na myśli mówiąc o mojej metodzie niszczenia istniejącej zawartości? Moja metoda wymagałaby utworzenia pustego pliku. –

+0

@El Ronnoco: niezrozumiany, przepraszam. Jednak Twoja metoda nie wykrywa aliasingu na poziomie nazw plików. –

+0

@Seva W porządku, nawet nie wiem co to jest! :) –

Powiązane problemy