2011-02-03 4 views
10

可能性の重複指していることを決定することができます
Best way to determine if two path reference to same file in C#は、どのように私は、いくつかのファイルパス形式が物理的に同じ場所に

をディレクトリの場所を指定するには、いくつかの方法があります。例えば


\\ MACHINENAME \ C $ \ ROOTPATH \サブパス
\\ MACHINENAME \共有名(サブパスに共有ポインティング)
C:\ ROOTPATH \サブパス
サブパス(すでにC:\ rootPathにある場合は相対パス

これらのすべてのパスが互いに「等しい」(実際にはハードドライブの物理的な位置が同じである)ことを確認する必要があります。

C#でこれを行う方法はありますか?

+1

相対パスを解決するには、 'System.IO.Path.GetFullPath'を使用します。 – Jimmy

答えて

5

Oded状態では、これは.Netで行うのが難しいです。ランダムに生成された長いファイル名のファイルを場所に書き込んだり、他の場所からファイルを見ることができるかどうかを確認することで、不正行為(正確な要件とアクセス許可などを調整する)を行うことができます。ハックのビットですが、マップされたドライブなどを解決することに頼るのではなく、私が思うにはかなり健全なテストです。

VBのために多くの謝罪しています。 「tはあなたがすなわち、タイムスタンプなどを読んで、より多くのセキュリティを追加することができますあまりにも違う...

使用状況など

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 

こと...

+1

あなたのプログラムと実行中のユーザーがそれらのフォルダを読み書きする権限を持っている場合はおそらく最も良い答えでしょう。 – KeithS

+0

ファイル名自体がエイリアスの場合、この方法は決してわかりません。エイリアシングがパスのどこかにある場合のみ。 –

+0

downvoting時にコメントを残してください! –

0

.NETでこれを行うネイティブな方法はありません - それはあまりにも低いレベルです。

Windows APIを使用してこれを実現することができます(ディレクトリのinodeやその他の識別子を見て)が、どのAPIがこれを公開するのかわかりません。

+1

AFAIK Windowsにはinodeがありません。 –

+0

@A1 - 私は本当にそこにあるかどうかわからないので、私は "または他の識別子"と言ったのです。 – Oded

+0

FATは、その名前以外は一意のファイル識別子を持っていません。そして、ネットワークを介してどのような種類の奇妙なファイルシステムが利用可能であるかも知っています。 –

0

AKAIKでは、同じドライブを多くのドライブ文字またはサブディレクトリにマップすることもできます。また、ネットワークの共有ディレクトリを増やすことができ、同じかどうかは分かりません。

多分あなたはそれを知る必要がある情報を追加することができます。

4

GetFileInformationByHandleを使用する必要があります。
StackOverflowのanswerと、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; 
    } 
    } 
} 
0

いい質問は、エレガントな答えがないかもしれない:ここでは

は、ディレクトリと連携し、私が書いた方法です。

私があなたの方法であなたを指し示すことができる最高のものは、net shareコマンドラインステートメントです。このコマンドで生成されたテキストをプログラムでキャプチャしてダイジェストできる場合は、ネットワーク共有1:1をローカルの宛先フォルダにマップできます。次に、指定されたパスでそれらの共有マップのインスタンスを探し、それらをローカルのフォルダに置き換えて、基本的な文字列比較を実行します。

2

多くのエイリアシングスキームは、Windowsにあります。同じネットワークパスへ

  • 短い対長い名前が
  • シンボリックリンクをして
  • UNC名をハードリンク対マップされたドライブ
  • いくつかのマップされたドライブを
  • d:\ folder \ paths対\?\ d \ folder \ paths
  • NTFSマウントポイント

これらはディレクトリツリーの任意のレベルに表示されます。 .NETでは、これらのいくつかを解決できますが、他のものは解決できません。

ハッキングのために、ロック/ロック解除を試してください。ファイルを名前1でロックし、名前2として開きます。失敗したことを確認します。次に、ロックを解除して、もう一度開いて、成功したことを確認してください。偽陽性を避けるために、数回。 El Ronnocoの方法とは異なり、これはパスレベルとファイルレベルのエイリアシングの両方を検出します。

一部のネットワークファイルシステムでは、ロックがまったくサポートされない場合があります。また、しばらく時間がかかる場合があります。ロック/アンロック/オープンの各操作はネットワークのラウンドトリップです。

しかし、実際には要件によって異なります。短い/長い名前で対処しなければならないことはすべて、それは過剰です。

EDIT:ファイルが読み取り専用であるか、他のユーザーによって既に開かれている場合は、さらに複雑になります。

+0

+1私は答えが好きですが、私の方法が既存の内容を破壊していることをどういう意味か分かりません。私の方法は、空のファイルを作成することです。 –

+0

@El Ronnoco:誤解されました、申し訳ありません。あなたの方法はファイル名レベルのエイリアシングを検出しません。 –

+0

@Sevaそれは大丈夫です、私はそれが何であるかも分かりません! :) –

関連する問題