.Net中的JunctionPoints(交接點)操作
2022-06-16 15:53:54 來源:易采站長站 作者:
Junction Points是NTFS v5+的新特性,功能和我們所熟知的UNIX中的文件夾軟鏈接類似(與Windows中的文件夾快捷方式不同的是,它進行了路徑重定向)。如在Vista中的C:\Documents and Settings 就是C:\Users的一個鏈接。然而,在Windows中并沒有提供相關命令,只能編程通過API來實現。
由于我對WinAPI編程并不熟,這里就不更多介紹了,更多信息請訪問原作者相關文章。這里僅轉一下相關函數用法:
創建Junction>
// Creates a Junction Point at
// C:\Foo\JunctionPoint that points to the directory C:\Bar.
// Fails if there is already a file,
// directory or Junction Point with the specified path.
JunctionPoint.Create(@"C:\Foo\JunctionPoint", @"C:\Bar",
false/*don't overwrite*/)
// Creates a Junction Point at C:\Foo\JunctionPoint that points to
// the directory C:\Bar.
// Replaces an existing Junction Point if found at the specified path.
JunctionPoint.Create(@"C:\Foo\JunctionPoint", @"C:\Bar", true/*overwrite*/)
注:不能對文件創建Junction Points.
刪除Junction>
// Delete a Junction Point at C:\Foo\JunctionPoint if it exists.
// Does nothing if there is no such Junction Point.
// Fails if the specified path refers to an existing file or
// directory rather than a Junction Point.
JunctionPoint.Delete(@"C:\Foo\JunctionPoint")
判斷Junction>
// Returns true if there is a Junction Point at C:\Foo\JunctionPoint.
// Returns false if the specified path refers to an existing file
// or directory rather than a Junction Point
// or if it refers to the vacuum of space.
bool exists = JunctionPoint.Exists(@"C:\Foo\JunctionPoint")
獲取Junction>
// Create a Junction Point for demonstration purposes whose target is C:\Bar.
JunctionPoint.Create(@"C:\Foo\JunctionPoint", @"C:\Bar", false)
// Returns the full path of the target of the Junction Point at
// C:\Foo\JunctionPoint.
// Fails if the specified path does not refer to a Junction Point.
string target = JunctionPoint.GetTarget(@"C:\Foo\JunctionPoint")
// target will be C:\Bar
// Returns true if there is a Junction Point at C:\Foo\JunctionPoint. // Returns false if the specified path refers to an existing file // or directory rather than a Junction Point // or if it refers to the vacuum of space. bool exists = JunctionPoint.Exists(@"C:\Foo\JunctionPoint")
獲取Junction>
// Create a Junction Point for demonstration purposes whose target is C:\Bar.
JunctionPoint.Create(@"C:\Foo\JunctionPoint", @"C:\Bar", false)
// Returns the full path of the target of the Junction Point at
// C:\Foo\JunctionPoint.
// Fails if the specified path does not refer to a Junction Point.
string target = JunctionPoint.GetTarget(@"C:\Foo\JunctionPoint")
// target will be C:\Bar
注:這個函數有問題,對系統的權限要求過高,如果對系統盤的一些文件夾鏈接訪問往往會報異常,我把它改了一下,新的代碼如下:
/// <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, GenericNone = 0x0, } [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.GenericNone)) { 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())); } }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持易采站長站。
如有侵權,請聯系QQ:279390809 電話:15144810328
最新圖文推薦
相關文章
-
[Asp.Net Core] 淺談Blazor Server Side
在2016年, 本人就開始了一個內部項目, 其特點就是用C#構建DOM樹, 然后把DOM同步到瀏覽器中顯示. 并且在一些小工程中使用. 3年下來, 效果很不錯, 但因為是使用C#來構建控件樹, 在沒有特定2020-07-01
-
使用vs2019加.net core 對WeiApi的創建過程詳解
vs2019創建webapi 1.創建新的項目 2.選擇.NET CORE的ASP .NET CORE WEB應用程序 3.定義項目名稱和存放地點 4.選擇API創建項目 5.刪除原本的無用的類 6.添加新的方法類 7.設置路由 using Microsoft.AspNe2020-07-03
-
[Asp.Net Core]用Blazor Server Side實現圖片驗證碼
關于Blazor 由于在國內, Blazor一點都不普及, 在閱讀此文前, 建議讀者先翻看我之前寫的隨筆, 了解Blazor Server Side的特點. 在一段時間內, 我會寫一些解說分析型的 "為什么選擇 Blazor Server2020-07-01
-
.Net Core中使用ExceptionFilter過濾器的方法
.Net Core中有各種Filter,分別是AuthorizationFilter、ResourceFilter、ExceptionFilter、ActionFilter、ResultFilter??梢园阉麄兛醋魇?Net Core自帶的AOP的擴展封裝。 今天來看其中的一種:ExceptionFilter(用于2020-03-03