What the C# equivalent of “mklink /J”?

后端 未结 3 1828
梦毁少年i
梦毁少年i 2020-12-24 01:39

I know how to create a symbolic link in windows in a .bat script:

mklink /J  

How to do the sam

3条回答
  •  Happy的楠姐
    2020-12-24 01:58

    Here is a project that has the PInvoke signatures for creating junctions: http://www.codeproject.com/Articles/15633/Manipulating-NTFS-Junction-Points-in-NET. The code is copied below:

    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Text;
    using Microsoft.Win32.SafeHandles;
    
    namespace Util {
        /// 
        /// Provides access to NTFS junction points in .Net.
        /// 
        public static class JunctionPoint {
            /// 
            /// The file or directory is not a reparse point.
            /// 
            private const int ERROR_NOT_A_REPARSE_POINT = 4390;
    
            /// 
            /// The reparse point attribute cannot be set because it conflicts with an existing attribute.
            /// 
            private const int ERROR_REPARSE_ATTRIBUTE_CONFLICT = 4391;
    
            /// 
            /// The data present in the reparse point buffer is invalid.
            /// 
            private const int ERROR_INVALID_REPARSE_DATA = 4392;
    
            /// 
            /// The tag present in the reparse point buffer is invalid.
            /// 
            private const int ERROR_REPARSE_TAG_INVALID = 4393;
    
            /// 
            /// There is a mismatch between the tag specified in the request and the tag present in the reparse point.
            /// 
            private const int ERROR_REPARSE_TAG_MISMATCH = 4394;
    
            /// 
            /// Command to set the reparse point data block.
            /// 
            private const int FSCTL_SET_REPARSE_POINT = 0x000900A4;
    
            /// 
            /// Command to get the reparse point data block.
            /// 
            private const int FSCTL_GET_REPARSE_POINT = 0x000900A8;
    
            /// 
            /// Command to delete the reparse point data base.
            /// 
            private const int FSCTL_DELETE_REPARSE_POINT = 0x000900AC;
    
            /// 
            /// Reparse point tag used to identify mount points and junction points.
            /// 
            private const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003;
    
            /// 
            /// This prefix indicates to NTFS that the path is to be treated as a non-interpreted
            /// path in the virtual file system.
            /// 
            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 {
                /// 
                /// Reparse point tag. Must be a Microsoft reparse point tag.
                /// 
                public uint ReparseTag;
    
                /// 
                /// 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);
                /// 
                public ushort ReparseDataLength;
    
                /// 
                /// Reserved; do not use. 
                /// 
                public ushort Reserved;
    
                /// 
                /// Offset, in bytes, of the substitute name string in the PathBuffer array.
                /// 
                public ushort SubstituteNameOffset;
    
                /// 
                /// Length, in bytes, of the substitute name string. If this string is null-terminated,
                /// SubstituteNameLength does not include space for the null character.
                /// 
                public ushort SubstituteNameLength;
    
                /// 
                /// Offset, in bytes, of the print name string in the PathBuffer array.
                /// 
                public ushort PrintNameOffset;
    
                /// 
                /// Length, in bytes, of the print name string. If this string is null-terminated,
                /// PrintNameLength does not include space for the null character. 
                /// 
                public ushort PrintNameLength;
    
                /// 
                /// A buffer containing the unicode-encoded path string. The path string contains
                /// the substitute name string and print name string.
                /// 
                [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);
    
            /// 
            /// Creates a junction point from the specified directory to the specified target directory.
            /// 
            /// 
            /// Only works on NTFS.
            /// 
            /// The target directory to create
            /// The source directory to alias
            /// If true overwrites an existing reparse point or empty directory
            /// Thrown when the junction point could not be created or when
            /// an existing directory was found and  if false
            public static void Create(string sourceDir, string targetDir, bool overwrite) {
                sourceDir = Path.GetFullPath(sourceDir);
    
                if (!Directory.Exists(sourceDir))
                    throw new IOException($"Source path does not exist or is not a directory.");
    
                if (Directory.Exists(targetDir))
                    throw new IOException($"Directory '{targetDir}' already exists.");
    
                Directory.CreateDirectory(targetDir);
    
                using (SafeFileHandle handle = OpenReparsePoint(targetDir, EFileAccess.GenericWrite)) {
                    byte[] sourceDirBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(sourceDir));
    
                    REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER();
    
                    reparseDataBuffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
                    reparseDataBuffer.ReparseDataLength = (ushort)(sourceDirBytes.Length + 12);
                    reparseDataBuffer.SubstituteNameOffset = 0;
                    reparseDataBuffer.SubstituteNameLength = (ushort)sourceDirBytes.Length;
                    reparseDataBuffer.PrintNameOffset = (ushort)(sourceDirBytes.Length + 2);
                    reparseDataBuffer.PrintNameLength = 0;
                    reparseDataBuffer.PathBuffer = new byte[0x3ff0];
                    Array.Copy(sourceDirBytes, reparseDataBuffer.PathBuffer, sourceDirBytes.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, sourceDirBytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);
    
                        if (!result)
                            ThrowLastWin32Error($"Unable to create junction point '{sourceDir}' -> '{targetDir}'.");
                    } finally {
                        Marshal.FreeHGlobal(inBuffer);
                    }
                }
            }
    
            /// 
            /// Deletes a junction point at the specified source directory along with the directory itself.
            /// Does nothing if the junction point does not exist.
            /// 
            /// 
            /// Only works on NTFS.
            /// 
            /// The junction point path
            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);
                    }
                }
            }
    
            /// 
            /// Determines whether the specified path exists and refers to a junction point.
            /// 
            /// The junction point path
            /// True if the specified path represents a junction point
            /// Thrown if the specified path is invalid
            /// or some other error occurs
            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;
                }
            }
    
            /// 
            /// Gets the target of the specified junction point.
            /// 
            /// 
            /// Only works on NTFS.
            /// 
            /// The junction point path
            /// The target of the junction point
            /// Thrown when the specified path does not
            /// exist, is invalid, is not a junction point, or some other error occurs
            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()));
            }
        }
    }
    

提交回复
热议问题