在.NET中,如何在NTFS中创建连接,而不是符号链接?
我正在尝试创建一个NTFS连接。在cmd行中,我可以使用。连接的DIR cmd输出如下所示:在.NET中,如何在NTFS中创建连接,而不是符号链接?,.net,winapi,pinvoke,ntfs,deviceiocontrol,.net,Winapi,Pinvoke,Ntfs,Deviceiocontrol,我正在尝试创建一个NTFS连接。在cmd行中,我可以使用。连接的DIR cmd输出如下所示: Volume in drive C has no label. Volume Serial Number is C8BC-2EBD Directory of c:\users\cheeso\Documents 03/22/2009 09:45 PM <JUNCTION> My Music [\??\c:\users\cheeso\Music] 05/11/2007
Volume in drive C has no label.
Volume Serial Number is C8BC-2EBD
Directory of c:\users\cheeso\Documents
03/22/2009 09:45 PM <JUNCTION> My Music [\??\c:\users\cheeso\Music]
05/11/2007 05:42 PM <DIR> My Received Files
03/22/2009 09:46 PM <JUNCTION> my videos [\??\c:\users\cheeso\Videos]
[Interop.DllImport("kernel32.dll", EntryPoint="CreateSymbolicLinkW", CharSet=Interop.CharSet.Unicode)]
public static extern int CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);
[Interop.DllImport("kernel32.dll", EntryPoint="CreateHardLinkW", CharSet=Interop.CharSet.Unicode)]
public static extern bool CreateHardLink(string lpFileName,
string lpExistingFileName,
IntPtr mustBeNull);
我做错了什么?如何从C#中创建连接?看起来您可以,而且有人在CodeProject上创建了一个库,其中包含许多在C#中构建的用于处理连接点的函数 看起来他实际上是在使用以下DllImport来实现这一点:
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
IntPtr InBuffer, int nInBufferSize,
IntPtr OutBuffer, int nOutBufferSize,
out int pBytesReturned, IntPtr lpOverlapped);
此代码来自,作为无法访问此链接的用户的快捷方式,或者在原始页面关闭时作为备份
不要投票支持这个answser,因为我不是这个代码的作者
感谢原作者
使用系统;
使用System.IO;
使用System.Runtime.InteropServices;
使用系统文本;
使用Microsoft.Win32.SafeHandles;
命名空间Monitor.Core.Utilities
{
///
///提供对.Net中NTFS连接点的访问。
///
公共静态类连接点
{
///
///文件或目录不是重分析点。
///
私有常量int错误\u未\u重新分析\u点=4390;
///
///无法设置重分析点属性,因为它与现有属性冲突。
///
private const int ERROR_repasse_ATTRIBUTE_CONFLICT=4391;
///
///重分析点缓冲区中存在的数据无效。
///
private const int ERROR\u INVALID\u resparse\u DATA=4392;
///
///重分析点缓冲区中存在的标记无效。
///
private const int ERROR_repasse_TAG_INVALID=4393;
///
///请求中指定的标记与重分析点中存在的标记不匹配。
///
private const int ERROR_repasse_TAG_MISMATCH=4394;
///
///用于设置重分析点数据块的命令。
///
私有常量int FSCTL\u SET\u repasse\u POINT=0x000900A4;
///
///获取重分析点数据块的命令。
///
私有常量int FSCTL_GET_repasse_POINT=0x000900A8;
///
///用于删除重分析点数据库的命令。
///
私有常量int FSCTL_DELETE_repasse_POINT=0x000900AC;
///
///重新分析点标签,用于标识装入点和连接点。
///
私人建筑IO重新分析标签安装点=0xA0000003;
///
///此前缀向NTFS指示该路径将被视为非解释路径
///虚拟文件系统中的路径。
///
私有常量字符串非解释性路径前缀=@“\??\”;
[旗帜]
私有枚举EFileAccess:uint
{
GenericRead=0x8000000,
GenericWrite=0x40000000,
GenericExecute=0x20000000,
GenericAll=0x10000000,
}
[旗帜]
私有枚举EFileShare:uint
{
无=0x00000000,
读取=0x00000001,
写入=0x00000002,
删除=0x00000004,
}
私有枚举ECreationDisposition:uint
{
新=1,
CreateAlways=2,
OpenExisting=3,
OpenAlways=4,
截断存在=5,
}
[旗帜]
私有枚举efileAttribute:uint
{
只读=0x00000001,
隐藏=0x00000002,
系统=0x00000004,
目录=0x00000010,
存档=0x00000020,
设备=0x00000040,
正常=0x00000080,
临时=0x00000100,
SparseFile=0x00000200,
ReparsePoint=0x00000400,
压缩=0x00000800,
离线=0x000011000,
NotContentIndexed=0x00002000,
加密=0x00004000,
写入到=0x8000000,
重叠=0x40000000,
无缓冲=0x20000000,
随机访问=0x10000000,
顺序扫描=0x08000000,
DeleteOnClose=0x04000000,
BackupSemantics=0x020000000,
PosixSemantics=0x01000000,
OpenRepassePoint=0x00200000,
OpenNoRecall=0x00100000,
FirstPipeInstance=0x00080000
}
[StructLayout(LayoutKind.Sequential)]
私有结构重分析\u数据\u缓冲区
{
///
///重新分析点标记。必须是Microsoft重新分析点标记。
///
公共uint重新分配标签;
///
///保留成员后的数据大小(字节)。可通过以下公式计算:
///(4*sizeof(ushort))+替换搪瓷长度+打印名称长度+
///(名称?2*sizeof(字符):0);
///
公共ushort重传数据长度;
///
///保留;不要使用。
///
保留公共厕所;
///
///PathBuffer数组中替换名称字符串的偏移量(以字节为单位)。
///
公共ushort替代物补偿;
///
///替换名称字符串的长度(字节)。如果此字符串以null结尾,
///SubstituteNameLength不包含空字符的空格。
///
公共ushort替代漆包线;
///
///PathBuffer数组中打印名称字符串的偏移量(字节)。
///
公共ushort PrintNameOffset;
///
///打印名称字符串的长度(字节)。如果此字符串以null结尾,
///PrintNameLength不包括空字符的空格。
///
公共ushort PrintNameLength;
///
///缓冲区
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
IntPtr InBuffer, int nInBufferSize,
IntPtr OutBuffer, int nOutBufferSize,
out int pBytesReturned, IntPtr lpOverlapped);
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace Monitor.Core.Utilities
{
/// <summary>
/// Provides access to NTFS junction points in .Net.
/// </summary>
public static class JunctionPoint
{
/// <summary>
/// The file or directory is not a reparse point.
/// </summary>
private const int ERROR_NOT_A_REPARSE_POINT = 4390;
/// <summary>
/// The reparse point attribute cannot be set because it conflicts with an existing attribute.
/// </summary>
private const int ERROR_REPARSE_ATTRIBUTE_CONFLICT = 4391;
/// <summary>
/// The data present in the reparse point buffer is invalid.
/// </summary>
private const int ERROR_INVALID_REPARSE_DATA = 4392;
/// <summary>
/// The tag present in the reparse point buffer is invalid.
/// </summary>
private const int ERROR_REPARSE_TAG_INVALID = 4393;
/// <summary>
/// There is a mismatch between the tag specified in the request and the tag present in the reparse point.
/// </summary>
private const int ERROR_REPARSE_TAG_MISMATCH = 4394;
/// <summary>
/// Command to set the reparse point data block.
/// </summary>
private const int FSCTL_SET_REPARSE_POINT = 0x000900A4;
/// <summary>
/// Command to get the reparse point data block.
/// </summary>
private const int FSCTL_GET_REPARSE_POINT = 0x000900A8;
/// <summary>
/// Command to delete the reparse point data base.
/// </summary>
private const int FSCTL_DELETE_REPARSE_POINT = 0x000900AC;
/// <summary>
/// Reparse point tag used to identify mount points and junction points.
/// </summary>
private const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003;
/// <summary>
/// This prefix indicates to NTFS that the path is to be treated as a non-interpreted
/// path in the virtual file system.
/// </summary>
private const string NonInterpretedPathPrefix = @"\??\";
[Flags]
private enum EFileAccess : uint
{
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
}
[Flags]
private enum EFileShare : uint
{
None = 0x00000000,
Read = 0x00000001,
Write = 0x00000002,
Delete = 0x00000004,
}
private enum ECreationDisposition : uint
{
New = 1,
CreateAlways = 2,
OpenExisting = 3,
OpenAlways = 4,
TruncateExisting = 5,
}
[Flags]
private 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
}
[StructLayout(LayoutKind.Sequential)]
private struct REPARSE_DATA_BUFFER
{
/// <summary>
/// Reparse point tag. Must be a Microsoft reparse point tag.
/// </summary>
public uint ReparseTag;
/// <summary>
/// Size, in bytes, of the data after the Reserved member. This can be calculated by:
/// (4 * sizeof(ushort)) + SubstituteNameLength + PrintNameLength +
/// (namesAreNullTerminated ? 2 * sizeof(char) : 0);
/// </summary>
public ushort ReparseDataLength;
/// <summary>
/// Reserved; do not use.
/// </summary>
public ushort Reserved;
/// <summary>
/// Offset, in bytes, of the substitute name string in the PathBuffer array.
/// </summary>
public ushort SubstituteNameOffset;
/// <summary>
/// Length, in bytes, of the substitute name string. If this string is null-terminated,
/// SubstituteNameLength does not include space for the null character.
/// </summary>
public ushort SubstituteNameLength;
/// <summary>
/// Offset, in bytes, of the print name string in the PathBuffer array.
/// </summary>
public ushort PrintNameOffset;
/// <summary>
/// Length, in bytes, of the print name string. If this string is null-terminated,
/// PrintNameLength does not include space for the null character.
/// </summary>
public ushort PrintNameLength;
/// <summary>
/// A buffer containing the unicode-encoded path string. The path string contains
/// the substitute name string and print name string.
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3FF0)]
public byte[] PathBuffer;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
IntPtr InBuffer, int nInBufferSize,
IntPtr OutBuffer, int nOutBufferSize,
out int pBytesReturned, IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateFile(
string lpFileName,
EFileAccess dwDesiredAccess,
EFileShare dwShareMode,
IntPtr lpSecurityAttributes,
ECreationDisposition dwCreationDisposition,
EFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
/// <summary>
/// Creates a junction point from the specified directory to the specified target directory.
/// </summary>
/// <remarks>
/// Only works on NTFS.
/// </remarks>
/// <param name="junctionPoint">The junction point path</param>
/// <param name="targetDir">The target directory</param>
/// <param name="overwrite">If true overwrites an existing reparse point or empty directory</param>
/// <exception cref="IOException">Thrown when the junction point could not be created or when
/// an existing directory was found and <paramref name="overwrite" /> if false</exception>
public static void Create(string junctionPoint, string targetDir, bool overwrite)
{
targetDir = Path.GetFullPath(targetDir);
if (!Directory.Exists(targetDir))
throw new IOException("Target path does not exist or is not a directory.");
if (Directory.Exists(junctionPoint))
{
if (!overwrite)
throw new IOException("Directory already exists and overwrite parameter is false.");
}
else
{
Directory.CreateDirectory(junctionPoint);
}
using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite))
{
byte[] targetDirBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(targetDir));
REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER();
reparseDataBuffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
reparseDataBuffer.ReparseDataLength = (ushort)(targetDirBytes.Length + 12);
reparseDataBuffer.SubstituteNameOffset = 0;
reparseDataBuffer.SubstituteNameLength = (ushort)targetDirBytes.Length;
reparseDataBuffer.PrintNameOffset = (ushort)(targetDirBytes.Length + 2);
reparseDataBuffer.PrintNameLength = 0;
reparseDataBuffer.PathBuffer = new byte[0x3ff0];
Array.Copy(targetDirBytes, reparseDataBuffer.PathBuffer, targetDirBytes.Length);
int inBufferSize = Marshal.SizeOf(reparseDataBuffer);
IntPtr inBuffer = Marshal.AllocHGlobal(inBufferSize);
try
{
Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false);
int bytesReturned;
bool result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT,
inBuffer, targetDirBytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);
if (!result)
ThrowLastWin32Error("Unable to create junction point.");
}
finally
{
Marshal.FreeHGlobal(inBuffer);
}
}
}
/// <summary>
/// Deletes a junction point at the specified source directory along with the directory itself.
/// Does nothing if the junction point does not exist.
/// </summary>
/// <remarks>
/// Only works on NTFS.
/// </remarks>
/// <param name="junctionPoint">The junction point path</param>
public static void Delete(string junctionPoint)
{
if (!Directory.Exists(junctionPoint))
{
if (File.Exists(junctionPoint))
throw new IOException("Path is not a junction point.");
return;
}
using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite))
{
REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER();
reparseDataBuffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
reparseDataBuffer.ReparseDataLength = 0;
reparseDataBuffer.PathBuffer = new byte[0x3ff0];
int inBufferSize = Marshal.SizeOf(reparseDataBuffer);
IntPtr inBuffer = Marshal.AllocHGlobal(inBufferSize);
try
{
Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false);
int bytesReturned;
bool result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_DELETE_REPARSE_POINT,
inBuffer, 8, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);
if (!result)
ThrowLastWin32Error("Unable to delete junction point.");
}
finally
{
Marshal.FreeHGlobal(inBuffer);
}
try
{
Directory.Delete(junctionPoint);
}
catch (IOException ex)
{
throw new IOException("Unable to delete junction point.", ex);
}
}
}
/// <summary>
/// Determines whether the specified path exists and refers to a junction point.
/// </summary>
/// <param name="path">The junction point path</param>
/// <returns>True if the specified path represents a junction point</returns>
/// <exception cref="IOException">Thrown if the specified path is invalid
/// or some other error occurs</exception>
public static bool Exists(string path)
{
if (! Directory.Exists(path))
return false;
using (SafeFileHandle handle = OpenReparsePoint(path, EFileAccess.GenericRead))
{
string target = InternalGetTarget(handle);
return target != null;
}
}
/// <summary>
/// Gets the target of the specified junction point.
/// </summary>
/// <remarks>
/// Only works on NTFS.
/// </remarks>
/// <param name="junctionPoint">The junction point path</param>
/// <returns>The target of the junction point</returns>
/// <exception cref="IOException">Thrown when the specified path does not
/// exist, is invalid, is not a junction point, or some other error occurs</exception>
public static string GetTarget(string junctionPoint)
{
using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericRead))
{
string target = InternalGetTarget(handle);
if (target == null)
throw new IOException("Path is not a junction point.");
return target;
}
}
private static string InternalGetTarget(SafeFileHandle handle)
{
int outBufferSize = Marshal.SizeOf(typeof(REPARSE_DATA_BUFFER));
IntPtr outBuffer = Marshal.AllocHGlobal(outBufferSize);
try
{
int bytesReturned;
bool result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_GET_REPARSE_POINT,
IntPtr.Zero, 0, outBuffer, outBufferSize, out bytesReturned, IntPtr.Zero);
if (!result)
{
int error = Marshal.GetLastWin32Error();
if (error == ERROR_NOT_A_REPARSE_POINT)
return null;
ThrowLastWin32Error("Unable to get information about junction point.");
}
REPARSE_DATA_BUFFER reparseDataBuffer = (REPARSE_DATA_BUFFER)
Marshal.PtrToStructure(outBuffer, typeof(REPARSE_DATA_BUFFER));
if (reparseDataBuffer.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
return null;
string targetDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
reparseDataBuffer.SubstituteNameOffset, reparseDataBuffer.SubstituteNameLength);
if (targetDir.StartsWith(NonInterpretedPathPrefix))
targetDir = targetDir.Substring(NonInterpretedPathPrefix.Length);
return targetDir;
}
finally
{
Marshal.FreeHGlobal(outBuffer);
}
}
private static SafeFileHandle OpenReparsePoint(string reparsePoint, EFileAccess accessMode)
{
SafeFileHandle reparsePointHandle = new SafeFileHandle(CreateFile(reparsePoint, accessMode,
EFileShare.Read | EFileShare.Write | EFileShare.Delete,
IntPtr.Zero, ECreationDisposition.OpenExisting,
EFileAttributes.BackupSemantics | EFileAttributes.OpenReparsePoint, IntPtr.Zero), true);
if (Marshal.GetLastWin32Error() != 0)
ThrowLastWin32Error("Unable to open reparse point.");
return reparsePointHandle;
}
private static void ThrowLastWin32Error(string message)
{
throw new IOException(message, Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));
}
}
}