列出文件的硬链接(C# 中)

发布于 2024-10-02 21:36:53 字数 732 浏览 13 评论 0原文

我想编写一个程序来显示另一个带有硬链接的驱动器的文件。

我想保持两个硬链接在文件名和其他方面的一致,所以我必须获得一个函数/方法,在其中可以列出文件的所有当前硬链接。

例如:

我有一个文件 C:\file.txt 和另一个指向 D:\file.txt 的硬链接。

然后我将 D:\file.txt 重命名为 D:\file_new.txt

我现在希望也能够重命名 C 驱动器上的硬链接。

因此,我需要一个返回 D:\file_new.txt 的函数,其中存在以下硬链接:

C:\file.txt
D:\file_new.txt

然后我可以重命名 C:\ 上的硬链接也获得 < code>D:\file_new.txt

所以我需要获取物理文件的所有硬链接。 或者:使用硬链接寻址的文件的所有硬链接。

希望有人可以帮忙!

编辑

奥利弗注意到硬链接不能在不同的磁盘上使用。谢谢...所以我将问题延伸为:我需要什么?连接点?符号链接?它还应该适用于文件,而不仅仅是文件夹!

I want to write a program that shows the files of another drive with hard links.

I want to keep both hardlinks consistent in filename and other things, so I have to get a function/method where I can list all current hard links of a file.

For example:

I have a file C:\file.txt and a second hard link to D:\file.txt.

Then I rename D:\file.txt to D:\file_new.txt.

I now want to be able to also rename the hardlink on the C drive as well.

So I need a function which returns for D:\file_new.txt that there are the following hardlinks:

C:\file.txt
D:\file_new.txt

then I can rename the hard link on C:\ also to get D:\file_new.txt

So I need to get all hard links of a physical file.
Or: All hard links of a file addressed with a hard link.

Hope somebody can help!

Edit:

Oliver noticed that hard links can't be used on different disks. thanks... So I extend the question to: What do I need? Junction Points? Symbolic Links? It should also work with files not only with folders!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

同展鸳鸯锦 2024-10-09 21:36:53

以下代码应该可以正常工作(最初由 Peter provost 在 PowerShell Code Repository 上发布):

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;

namespace HardLinkEnumerator
{
   public static class Kernel32Api
   {
       [StructLayout(LayoutKind.Sequential)]
       public struct BY_HANDLE_FILE_INFORMATION
       {
           public uint FileAttributes;
           public FILETIME CreationTime;
           public FILETIME LastAccessTime;
           public FILETIME LastWriteTime;
           public uint VolumeSerialNumber;
           public uint FileSizeHigh;
           public uint FileSizeLow;
           public uint NumberOfLinks;
           public uint FileIndexHigh;
           public uint FileIndexLow;
       }

       [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
       static extern SafeFileHandle CreateFile(
           string lpFileName,
           [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
           [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
           IntPtr lpSecurityAttributes,
           [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
           [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
           IntPtr hTemplateFile);

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

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

       [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
       static extern IntPtr FindFirstFileNameW(
           string lpFileName,
           uint dwFlags,
           ref uint stringLength,
           StringBuilder fileName);

       [DllImport("kernel32.dll", SetLastError = true, CharSet=CharSet.Unicode)]
       static extern bool FindNextFileNameW(
           IntPtr hFindStream,
           ref uint stringLength,
           StringBuilder fileName);

       [DllImport("kernel32.dll", SetLastError = true)]
       static extern bool FindClose(IntPtr fFindHandle);

       [DllImport("kernel32.dll")]
       static extern bool GetVolumePathName(string lpszFileName,
           [Out] StringBuilder lpszVolumePathName, uint cchBufferLength);

       [DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
       static extern bool PathAppend([In, Out] StringBuilder pszPath, string pszMore);

       public static int GetFileLinkCount(string filepath)
       {
           int result = 0;
           SafeFileHandle handle = CreateFile(filepath, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Archive, IntPtr.Zero);
           BY_HANDLE_FILE_INFORMATION fileInfo = new BY_HANDLE_FILE_INFORMATION();
           if (GetFileInformationByHandle(handle, out fileInfo))
               result = (int)fileInfo.NumberOfLinks;
           CloseHandle(handle);
           return result;
       }

       public static string[] GetFileSiblingHardLinks(string filepath)
       {
           List<string> result = new List<string>();
           uint stringLength = 256;
           StringBuilder sb = new StringBuilder(256);
           GetVolumePathName(filepath, sb, stringLength);
           string volume = sb.ToString();
           sb.Length = 0; stringLength = 256;
           IntPtr findHandle = FindFirstFileNameW(filepath, 0, ref stringLength, sb);
           if (findHandle.ToInt32() != -1)
           {
               do
               {
                   StringBuilder pathSb = new StringBuilder(volume, 256);
                   PathAppend(pathSb, sb.ToString());
                   result.Add(pathSb.ToString());
                   sb.Length = 0; stringLength = 256;
               } while (FindNextFileNameW(findHandle, ref stringLength, sb));
               FindClose(findHandle);
               return result.ToArray();
           }
           return null;
       }

   }
}

the following code should work well (originally postet by Peter provost on PowerShell Code Repository):

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;

namespace HardLinkEnumerator
{
   public static class Kernel32Api
   {
       [StructLayout(LayoutKind.Sequential)]
       public struct BY_HANDLE_FILE_INFORMATION
       {
           public uint FileAttributes;
           public FILETIME CreationTime;
           public FILETIME LastAccessTime;
           public FILETIME LastWriteTime;
           public uint VolumeSerialNumber;
           public uint FileSizeHigh;
           public uint FileSizeLow;
           public uint NumberOfLinks;
           public uint FileIndexHigh;
           public uint FileIndexLow;
       }

       [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
       static extern SafeFileHandle CreateFile(
           string lpFileName,
           [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess,
           [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode,
           IntPtr lpSecurityAttributes,
           [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition,
           [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes,
           IntPtr hTemplateFile);

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

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

       [DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
       static extern IntPtr FindFirstFileNameW(
           string lpFileName,
           uint dwFlags,
           ref uint stringLength,
           StringBuilder fileName);

       [DllImport("kernel32.dll", SetLastError = true, CharSet=CharSet.Unicode)]
       static extern bool FindNextFileNameW(
           IntPtr hFindStream,
           ref uint stringLength,
           StringBuilder fileName);

       [DllImport("kernel32.dll", SetLastError = true)]
       static extern bool FindClose(IntPtr fFindHandle);

       [DllImport("kernel32.dll")]
       static extern bool GetVolumePathName(string lpszFileName,
           [Out] StringBuilder lpszVolumePathName, uint cchBufferLength);

       [DllImport("shlwapi.dll", CharSet = CharSet.Auto)]
       static extern bool PathAppend([In, Out] StringBuilder pszPath, string pszMore);

       public static int GetFileLinkCount(string filepath)
       {
           int result = 0;
           SafeFileHandle handle = CreateFile(filepath, FileAccess.Read, FileShare.Read, IntPtr.Zero, FileMode.Open, FileAttributes.Archive, IntPtr.Zero);
           BY_HANDLE_FILE_INFORMATION fileInfo = new BY_HANDLE_FILE_INFORMATION();
           if (GetFileInformationByHandle(handle, out fileInfo))
               result = (int)fileInfo.NumberOfLinks;
           CloseHandle(handle);
           return result;
       }

       public static string[] GetFileSiblingHardLinks(string filepath)
       {
           List<string> result = new List<string>();
           uint stringLength = 256;
           StringBuilder sb = new StringBuilder(256);
           GetVolumePathName(filepath, sb, stringLength);
           string volume = sb.ToString();
           sb.Length = 0; stringLength = 256;
           IntPtr findHandle = FindFirstFileNameW(filepath, 0, ref stringLength, sb);
           if (findHandle.ToInt32() != -1)
           {
               do
               {
                   StringBuilder pathSb = new StringBuilder(volume, 256);
                   PathAppend(pathSb, sb.ToString());
                   result.Add(pathSb.ToString());
                   sb.Length = 0; stringLength = 256;
               } while (FindNextFileNameW(findHandle, ref stringLength, sb));
               FindClose(findHandle);
               return result.ToArray();
           }
           return null;
       }

   }
}
没︽人懂的悲伤 2024-10-09 21:36:53

也许我误解了你的问题,但硬链接不能从一个驱动器转到另一个驱动器。它们只能存在于单个驱动器上。

在 .Net 框架内,不支持获取这些信息。但 Win32 API 可以为您提供这些信息。

请查看这篇文章。它可能对你有帮助。

更新

据我所知,不可能在不同的驱动器之间执行此操作。连接点绝对不是你的朋友,因为它只适用于折叠。但在阅读这篇维基百科文章之后,您似乎可以在 Vista 和 Win7 上使用符号链接来完成此操作。还有一个指向 此 shell 扩展 的链接,它似乎涵盖了您可以执行的所有操作具有这些 NTFS 特殊功能。也许通过这个你可以检查你的目标是否可以实现,然后也许可以检查 MSDN 以获得所需的 Win32 API 函数。

Maybe i misunderstand your questions, but hardlinks can't go from one drive to another. They can only exist on a single drive.

Within the .Net framwork there is no support to get these informations. But the Win32 API can provide you with these informations.

Take a look at this article. It may help you.

Update

As far as i know it is not possible to do it between different drives. Junction Points are definitely not your friend cause it only works on foldes. But after reading this wikipedia article it seems that you can do it on Vista and Win7 with symbolic links. There is also a link to this shell extension which seems to cover everything you can do with these NTFS special features. Maybe with this you can check if your goal is reachable and maybe afterwards check the MSDN for the desired Win32 API function.

故事↓在人 2024-10-09 21:36:53

注意:

  • 硬链接只能是同一卷上的文件,这与问题的要求相矛盾,从而导致了以下问题问题正文中的问题OP本人回答了

  • 但是,鉴于问题的标题,通过谷歌搜索找到这篇文章的用户很可能对问题的解决方案感兴趣如标题中所述给出文件,我如何找到它的所有硬链接(根据定义,它们都在同一卷上)。

  • 下面的解决方案是Marcel Nolte 的有用答案简化和现代化改编。< /p>

它的行为和约束是:

  • 对于给定的输入文件,其硬链接数组作为完整文件路径返回,其中包括输入文件的路径本身。

  • 如果文件只有一个硬链接(本身),或者您指定了一个目录,则仅返回该文件/目录的完整路径。

  • 如果路径引用的卷不支持硬链接,或者路径不存在,则返回null

    • NiKiZe 指出,您无法通过 CIFS/SMB 连接(网络驱动器)查询硬链接。

以下是一个独立的 Windows 控制台应用程序,您应该能够按原样编译和运行它;感兴趣的方法是HardLinkHelper.GetHardLinks():

using System;
using System.Text;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace demo
{
  public static class Program
  {
    public static void Main()
    {
      // Sample file that is known to have (one) hard link.
      var file = Environment.ExpandEnvironmentVariables(@"%SYSTEMROOT%\explorer.exe");
      foreach (var link in HardLinkHelper.GetHardLinks(file) ?? new string[] { "n/a" })
      {
        Console.WriteLine(link);
      }
    }
  }

  public static class HardLinkHelper
  {

    #region WinAPI P/Invoke declarations
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern IntPtr FindFirstFileNameW(string lpFileName, uint dwFlags, ref uint StringLength, StringBuilder LinkName);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern bool FindNextFileNameW(IntPtr hFindStream, ref uint StringLength, StringBuilder LinkName);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool FindClose(IntPtr hFindFile);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern bool GetVolumePathName(string lpszFileName, [Out] StringBuilder lpszVolumePathName, uint cchBufferLength);

    public static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1); // 0xffffffff;
    public const int MAX_PATH = 65535; // Max. NTFS path length.
    #endregion

    /// <summary>
    //// Returns the enumeration of hardlinks for the given *file* as full file paths, which includes
    /// the input path itself.
    /// </summary>
    /// <remarks>
    /// If the file has only one hardlink (itself), or you specify a directory, only that
    /// file's / directory's full path is returned.
    /// If the path refers to a volume that doesn't support hardlinks, or the path
    /// doesn't exist, null is returned.
    /// </remarks>
    public static string[] GetHardLinks(string filepath)
    {
      StringBuilder sbPath = new StringBuilder(MAX_PATH);
      uint charCount = (uint)sbPath.Capacity; // in/out character-count variable for the WinAPI calls.
      // Get the volume (drive) part of the target file's full path (e.g., @"C:\")
      GetVolumePathName(filepath, sbPath, (uint)sbPath.Capacity);
      string volume = sbPath.ToString();
      // Trim the trailing "\" from the volume path, to enable simple concatenation
      // with the volume-relative paths returned by the FindFirstFileNameW() and FindFirstFileNameW() functions,
      // which have a leading "\"
      volume = volume.Substring(0, volume.Length - 1);
      // Loop over and collect all hard links as their full paths.
      IntPtr findHandle;
      if (INVALID_HANDLE_VALUE == (findHandle = FindFirstFileNameW(filepath, 0, ref charCount, sbPath))) return null;
      List<string> links = new List<string>();
      do
      {
        links.Add(volume + sbPath.ToString()); // Add the full path to the result list.
        charCount = (uint)sbPath.Capacity; // Prepare for the next FindNextFileNameW() call.
      } while (FindNextFileNameW(findHandle, ref charCount, sbPath));
      FindClose(findHandle);
      return links.ToArray();
    }

  }
}

Note:

  • Hard links can only be files on the same volume, which contradicts the requirements of the question, which led to a follow-up question in the question body that the OP himself answered.

  • Given the title of the question, however, users who find this post by googling are most likely interest in a solution to the problem as stated in the title: given a file, how can I find all hard links to it (which are by definition all on the same volume).

  • The solution below is a streamlined and modernized adaptation of Marcel Nolte's helpful answer.

Its behavior and constraints are:

  • For a given input file, its array of hard links is returned, as full file paths, which includes the input file's path itself.

  • If the file has only one hard link (itself), or you specify a directory, only that file's / directory's full path is returned.

  • If the path refers to a volume that doesn't support hard links, or the path doesn't exist, null is returned.

    • NiKiZe notes that you cannot query hardlinks via a CIFS/SMB connection (network drive).

The following is a self-contained Windows console application that you should be able to compile and run as-is; the method of interest is HardLinkHelper.GetHardLinks():

using System;
using System.Text;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace demo
{
  public static class Program
  {
    public static void Main()
    {
      // Sample file that is known to have (one) hard link.
      var file = Environment.ExpandEnvironmentVariables(@"%SYSTEMROOT%\explorer.exe");
      foreach (var link in HardLinkHelper.GetHardLinks(file) ?? new string[] { "n/a" })
      {
        Console.WriteLine(link);
      }
    }
  }

  public static class HardLinkHelper
  {

    #region WinAPI P/Invoke declarations
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern IntPtr FindFirstFileNameW(string lpFileName, uint dwFlags, ref uint StringLength, StringBuilder LinkName);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern bool FindNextFileNameW(IntPtr hFindStream, ref uint StringLength, StringBuilder LinkName);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool FindClose(IntPtr hFindFile);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    static extern bool GetVolumePathName(string lpszFileName, [Out] StringBuilder lpszVolumePathName, uint cchBufferLength);

    public static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1); // 0xffffffff;
    public const int MAX_PATH = 65535; // Max. NTFS path length.
    #endregion

    /// <summary>
    //// Returns the enumeration of hardlinks for the given *file* as full file paths, which includes
    /// the input path itself.
    /// </summary>
    /// <remarks>
    /// If the file has only one hardlink (itself), or you specify a directory, only that
    /// file's / directory's full path is returned.
    /// If the path refers to a volume that doesn't support hardlinks, or the path
    /// doesn't exist, null is returned.
    /// </remarks>
    public static string[] GetHardLinks(string filepath)
    {
      StringBuilder sbPath = new StringBuilder(MAX_PATH);
      uint charCount = (uint)sbPath.Capacity; // in/out character-count variable for the WinAPI calls.
      // Get the volume (drive) part of the target file's full path (e.g., @"C:\")
      GetVolumePathName(filepath, sbPath, (uint)sbPath.Capacity);
      string volume = sbPath.ToString();
      // Trim the trailing "\" from the volume path, to enable simple concatenation
      // with the volume-relative paths returned by the FindFirstFileNameW() and FindFirstFileNameW() functions,
      // which have a leading "\"
      volume = volume.Substring(0, volume.Length - 1);
      // Loop over and collect all hard links as their full paths.
      IntPtr findHandle;
      if (INVALID_HANDLE_VALUE == (findHandle = FindFirstFileNameW(filepath, 0, ref charCount, sbPath))) return null;
      List<string> links = new List<string>();
      do
      {
        links.Add(volume + sbPath.ToString()); // Add the full path to the result list.
        charCount = (uint)sbPath.Capacity; // Prepare for the next FindNextFileNameW() call.
      } while (FindNextFileNameW(findHandle, ref charCount, sbPath));
      FindClose(findHandle);
      return links.ToArray();
    }

  }
}
若言繁花未落 2024-10-09 21:36:53

我找到了一个解决方案:

首先,我不必使用硬链接(因为它们不能指向其他磁盘)。我必须改用符号链接。因此,我在原始磁盘上有一个硬链接文件,在其他磁盘上有指向该文件的符号链接。限制是操作系统必须是 Vista 或更高版本。

其次,我必须能够找出符号链接指向的位置。
在这里我找到了一个很好的例子如何找到我需要的信息:
http://www.codeproject.com/KB/vista/ReparsePointID.aspx

我唯一没有做到的是从特定文件(硬链接)中查找所有符号链接。我想没有现成的解决方案,我必须递归所有符号链接并测试目标。但就我而言,这没有问题。

我希望这可以帮助其他人!

I found a solution:

First I don't have to use hard links (since they can't point to an other disk). I have to use symbolic links instead. So I have one hard linked file on the original disk and symbolic links on other disks to this file. The limitation is OS must be Vista or newer.

Second I have to be able to find out where the symbolic link is pointing to.
Here I found a good example how to find out the information I need:
http://www.codeproject.com/KB/vista/ReparsePointID.aspx

The only thing I don't managed is to find all symbolic links from a specific file (hard link). I guess there is no out of the box solution and I have to recurse all symbolic links and test the target. But in my case that's no problem.

I hope that can help others!

回忆追雨的时光 2024-10-09 21:36:53

我在项目中使用了 HardLinkHelper 类,它非常适合在 Windows NTFS 驱动器上查找硬链接。

这是我的 HardLinkHelper 类版本,进行了以下更改:

  1. 不使用 StringBuilder,因为 Microsoft 建议 避免在 pinvoke 上使用 StringBuilder
  2. 它具有私有成员变量(INVALID_HANDLE_VALUE 和 MAX_PATH)。
  3. 永远不会返回 Null,而是为不存在的路径或不受支持的路径返回空列表。这允许在返回值上安全地使用 foreach。
  4. 添加了 ReturnEmptyListIfOnlyOne 输入变量,当设置为 true 时,允许调用函数在 foreach 中使用它,其中仅当文件具有多个共享硬链接时才会进入 foreach 循环。
    用法示例:
    foreach(HardLinkHelper.GetHardLinks(entry.Path,true)中的var链接)
    public static class HardLinkHelper {
        #region WinAPI P/Invoke declarations
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern IntPtr FindFirstFileNameW(string lpFileName, uint dwFlags, ref uint StringLength, char[] LinkName);
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern bool FindNextFileNameW(IntPtr hFindStream, ref uint StringLength, char[] LinkName);
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool FindClose(IntPtr hFindFile);
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern bool GetVolumePathName(string lpszFileName, [Out] char[] lpszVolumePathName, uint cchBufferLength);
        private static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1); // 0xffffffff;
        private const int MAX_PATH = 65535; // Max. NTFS path length.
        #endregion
        /// <summary>
        /// Checks for hard links on a Windows NTFS drive associated with the given path.
        /// </summary>
        /// <param name="filepath">Fully qualified path of the file to check for shared hard links</param>
        /// <param name="ReturnEmptyListIfOnlyOne">Set true, to return populated list only for files having multiple hard links</param>
        /// <returns>
        ///     Empty list is returned for non-existing path or unsupported path.
        ///     Single hard link paths returns empty list if ReturnEmptyListIfOnlyOne is true. If false, returns single item list.
        ///     For multiple shared hard links, returns list of all the shared hard links.
        /// </returns>
        public static List<string> GetHardLinks(string filepath, bool ReturnEmptyListIfOnlyOne = false) {
            List<string> links = new List<string>();
            try {
                Char[] sbPath = new Char[MAX_PATH + 1];
                uint charCount = (uint)MAX_PATH;
                GetVolumePathName(filepath, sbPath, (uint)MAX_PATH); // Must use GetVolumePathName, because Path.GetPathRoot fails on a mounted drive on an empty folder.
                string volume = new string(sbPath).Trim('\0');
                volume = volume.Substring(0, volume.Length - 1);
                Array.Clear(sbPath, 0, MAX_PATH); // Reset the array because these API's can leave garbage at the end of the buffer.
                IntPtr findHandle;
                if (INVALID_HANDLE_VALUE != (findHandle = FindFirstFileNameW(filepath, 0, ref charCount, sbPath))) {
                    do {
                        links.Add((volume + new string(sbPath)).Trim('\0')); // Add the full path to the result list.
                        charCount = (uint)MAX_PATH;
                        Array.Clear(sbPath, 0, MAX_PATH);
                    } while (FindNextFileNameW(findHandle, ref charCount, sbPath));
                    FindClose(findHandle);
                }
            }
            catch (Exception ex) {
                //Logger.Instance.Info($"GetHardLinks: Exception, file: {filepath}, reason: {ex.Message}, stacktrace {ex.StackTrace}");
            }
            if (ReturnEmptyListIfOnlyOne && links.Count < 2)
                links.Clear();
            return links;
        }
    }

I used the HardLinkHelper class in a project, and it works great for finding hard links on Windows NTFS drives.

Here's my version of HardLinkHelper class with the following changes:

  1. Does not use StringBuilder, because Microsoft recommends to avoid using StringBuilder on pinvokes.
  2. It has the member variables (INVALID_HANDLE_VALUE & MAX_PATH) private.
  3. Null is never returned, and instead empty list is returned for non-existing path or unsupported path. This allows safe use of foreach on return value.
  4. Added ReturnEmptyListIfOnlyOne input variable which when set to true, allows calling function to use it in a foreach, where the foreach loop will only be entered if the file has multiple shared hard links.
    Example usage:
    foreach (var link in HardLinkHelper.GetHardLinks(entry.Path, true))
    public static class HardLinkHelper {
        #region WinAPI P/Invoke declarations
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern IntPtr FindFirstFileNameW(string lpFileName, uint dwFlags, ref uint StringLength, char[] LinkName);
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern bool FindNextFileNameW(IntPtr hFindStream, ref uint StringLength, char[] LinkName);
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool FindClose(IntPtr hFindFile);
        [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        static extern bool GetVolumePathName(string lpszFileName, [Out] char[] lpszVolumePathName, uint cchBufferLength);
        private static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1); // 0xffffffff;
        private const int MAX_PATH = 65535; // Max. NTFS path length.
        #endregion
        /// <summary>
        /// Checks for hard links on a Windows NTFS drive associated with the given path.
        /// </summary>
        /// <param name="filepath">Fully qualified path of the file to check for shared hard links</param>
        /// <param name="ReturnEmptyListIfOnlyOne">Set true, to return populated list only for files having multiple hard links</param>
        /// <returns>
        ///     Empty list is returned for non-existing path or unsupported path.
        ///     Single hard link paths returns empty list if ReturnEmptyListIfOnlyOne is true. If false, returns single item list.
        ///     For multiple shared hard links, returns list of all the shared hard links.
        /// </returns>
        public static List<string> GetHardLinks(string filepath, bool ReturnEmptyListIfOnlyOne = false) {
            List<string> links = new List<string>();
            try {
                Char[] sbPath = new Char[MAX_PATH + 1];
                uint charCount = (uint)MAX_PATH;
                GetVolumePathName(filepath, sbPath, (uint)MAX_PATH); // Must use GetVolumePathName, because Path.GetPathRoot fails on a mounted drive on an empty folder.
                string volume = new string(sbPath).Trim('\0');
                volume = volume.Substring(0, volume.Length - 1);
                Array.Clear(sbPath, 0, MAX_PATH); // Reset the array because these API's can leave garbage at the end of the buffer.
                IntPtr findHandle;
                if (INVALID_HANDLE_VALUE != (findHandle = FindFirstFileNameW(filepath, 0, ref charCount, sbPath))) {
                    do {
                        links.Add((volume + new string(sbPath)).Trim('\0')); // Add the full path to the result list.
                        charCount = (uint)MAX_PATH;
                        Array.Clear(sbPath, 0, MAX_PATH);
                    } while (FindNextFileNameW(findHandle, ref charCount, sbPath));
                    FindClose(findHandle);
                }
            }
            catch (Exception ex) {
                //Logger.Instance.Info(
quot;GetHardLinks: Exception, file: {filepath}, reason: {ex.Message}, stacktrace {ex.StackTrace}");
            }
            if (ReturnEmptyListIfOnlyOne && links.Count < 2)
                links.Clear();
            return links;
        }
    }
焚却相思 2024-10-09 21:36:53

尝试:

using System.IO;

string[] filePathsC = Directory.GetFiles(@"c:\");
string[] filePathsD = Directory.GetFiles(@"d:\");

并循环遍历数组,找到文件并更改名称

编辑:
通过阅读评论,我知道我在知道什么是硬链接之前就回答了。我现在意识到这个答案没有帮助。

try:

using System.IO;

string[] filePathsC = Directory.GetFiles(@"c:\");
string[] filePathsD = Directory.GetFiles(@"d:\");

and loop through the arrays, find the files and change the name

EDIT:
By reading the comments I know that I answered before I knew what a hardlink is. I realise now that this answer is not helping.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文