Как я могу определить, что несколько форматов путей к файлам указывают на одно и то же физическое местоположение [дубликат]


Возможный дубликат:
Лучший способ определить, если два пути ссылки на один и тот же файл в C#

Существует несколько способов указать расположение каталога.

Для example:
\machineNamec$rootPathsubPath
\machineNameshareName (доля, указывающая на субпут)
C:rootPathsubPath
subPath (относительный путь, если уже в C:rootPath

Мне нужно определить, что все эти пути "равны" друг другу (на самом деле это одно и то же физическое расположение на жестком диске).

Есть ли способ сделать это в C#?

6 10

6 ответов:

Как утверждает Oded, это сложно сделать в .Net. вы можете обмануть (снизив свои точные требования и разрешения и т. д.), записав файл с длинным случайным именем файла в папку, а затем посмотреть, можете ли вы увидеть его из другой папки. Немного Хака, но это довольно звуковой тест, я думаю, вместо того, чтобы полагаться на разрешение подключенных дисков и т. д.

Хорошо, много извинений за VB - это все, что у меня есть на этом крошечном нетбуке... C# было бы не слишком различный...

Использование, например

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

Вы можете добавить дополнительную безопасность, т. е. чтение метки времени и т. д...

Вам нужно использовать GetFileInformationByHandle.
Проверьте этот Ответ в StackOverflow и MSDN справке.

Вот метод, который я написал, который работает с каталогами:

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

В Windows существует множество схем сглаживания:

  • короткие и длинные имена
  • символьные и жесткие ссылки
  • UNC-имена и сопоставленные диски
  • несколько подключенных дисков к одному сетевому пути
  • d:\folder " пути против "?\d \ папка\ пути
  • точки монтирования NTFS

И любой из них может появиться на любом уровне дерева каталогов. В .NET можно разрешить некоторые из них, но не другие.

В качестве хакерского способа обойти это, попробуйте блокировку / разблокировку. Заблокируйте файл с именем 1, попробуйте открыть его с именем 2, Убедитесь, что он не работает. Затем откройте,попробуйте открыть снова, убедитесь, что это удастся. И так далее, несколько раз, чтобы избежать ложных срабатываний. В отличие от способа El Ronnoco, этот метод обнаруживает алиасирование как на уровне пути, так и на уровне файла.

В некоторых сетевых файловых системах блокировка может вообще не поддерживаться. Кроме того, это может занять некоторое время - каждая операция блокировки/разблокировки/открытия является сетевым циклом.

Но это действительно зависит от ваших требований. Если короткие / длинные имена это все, с чем вам приходится иметь дело, это перебор.

EDIT: дополнительные сложности, если файл доступен только для чтения или уже открыт кем-то другим.

В .NET нет собственного способа сделать это - это просто слишком низкий уровень.

Вы можете использовать API windows для достижения этой цели (глядя на индекс каталога или какой-либо другой идентификатор), но я не знаю, какой API будет предоставлять это.

AKAIK вы даже можете сопоставить один и тот же диск со многими буквами диска или подкаталогами. Кроме того, сетевые общие каталоги могут быть умножены, и вы никогда не знаете, являются ли они одинаковыми или нет.

Может быть, вы могли бы добавить информацию, зачем вам это нужно знать.

Хороший вопрос, на него не может быть элегантного ответа.

Лучшее, что я могу найти, чтобы указать вам на ваш путь,-это оператор командной строки net share. Если вы можете захватить и переварить текст, созданный этой командой программно, вы можете сопоставить сетевые ресурсы 1:1 с их локальными целевыми папками. Затем вы можете найти экземпляры этих общих карт в заданном пути и заменить их эквивалентами локальных папок, прежде чем выполнять базовое сравнение строк.