我想编写一个程序,显示带有硬链接的其他驱动器的文件。 我想保持两个硬链接在文件名和其他内容一致,所以我必须得到一个函数/方法,我可以列出文件的所有当前硬链接。
例如:
我有一个文件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:\上的硬链接以获得D:\ file_new.txt
所以我需要获取物理文件的所有硬链接。或者:使用硬链接寻址的文件的所有硬链接。
希望有人可以帮忙!
编辑:
Oliver注意到硬链接不能用于不同的磁盘。谢谢...所以我把问题扩展到:我需要什么?交汇点?符号链接?它还应该不仅与文件夹一起使用文件!
答案 0 :(得分:5)
以下代码应该运行良好(最初由Peter Provost在PowerShell代码存储库上发布):
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;
}
}
}
答案 1 :(得分:1)
也许我误解了你的问题,但硬链接不能从一个驱动器转到另一个驱动器。它们只能存在于一个驱动器上。
在.Net框架内,没有人支持获取这些信息。但是Win32 API可以为您提供这些信息。
看看this article。它可能对你有帮助。
据我所知,不可能在不同的驱动器之间进行此操作。交汇点绝对不是你的朋友因为它只适用于foldes。但在阅读this wikipedia article之后,您似乎可以使用符号链接在Vista和Win7上执行此操作。还有this shell extension的链接,似乎涵盖了您可以使用这些NTFS特殊功能所做的一切。也许有了这个,你可以检查你的目标是否可以访问,然后可以检查MSDN以获得所需的Win32 API函数。
答案 2 :(得分:1)
注意:
硬链接只能是相同卷 上的文件,这与问题的要求相抵触,导致the OP himself answered的问题正文中的后续问题。
给出问题的标题,但是,通过谷歌搜索找到此帖子的用户最有可能对问题的解决方案感兴趣,如标题中所述 :给出了一个文件,如何找到所有硬链接 (根据定义,这些硬链接都在同一卷上)。
下面的解决方案是对Marcel Nolte's helpful answer 的简化和现代化的改编。
它的行为和约束是:
对于给定的输入文件,其硬链接数组作为完整文件路径返回,其中包括输入文件的路径本身。
如果文件(本身)只有一个硬链接,或者您指定了目录,则仅返回该文件/目录的完整路径。
如果路径引用的卷不支持硬链接,或者路径不存在,则返回null
。
以下是一个自包含的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();
}
}
}
答案 3 :(得分:0)
尝试:
using System.IO;
string[] filePathsC = Directory.GetFiles(@"c:\");
string[] filePathsD = Directory.GetFiles(@"d:\");
并遍历数组,找到文件并更改名称
编辑: 通过阅读评论,我知道在我知道硬链接之前我已经回答了。我现在意识到这个答案没有帮助。
答案 4 :(得分:0)
我找到了解决方案:
首先,我不必使用硬链接(因为它们不能指向其他磁盘)。我必须使用符号链接。所以我在原始磁盘上有一个硬链接文件,在这个文件的其他磁盘上有符号链接。限制是操作系统必须是Vista或更新。
其次,我必须能够找到符号链接指向的位置。 在这里,我找到了一个很好的例子,如何找到我需要的信息: http://www.codeproject.com/KB/vista/ReparsePointID.aspx
我唯一没有管理的是找到特定文件(硬链接)中的所有符号链接。我想没有开箱即用的解决方案,我必须递归所有符号链接并测试目标。但就我而言,没问题。
我希望能帮到别人!