Search code examples
c#symlink

Check if a file is real or a symbolic link


Is there a way to tell using C# if a file is real or a symbolic link?

I've dug through the MSDN W32 docs, and can't find anything for checking this. I'm using CreateSymbolicLink from here, and it's working fine.


Solution

  • I have some source code for symlinks posted on my blog that will allow you to:

    • create symlinks
    • check whether a path is a symlink
    • retrieve the target of a symlink

    It also contains NUnit test cases, that you may wish to extend.

    The meaty bit is:

    private static SafeFileHandle getFileHandle(string path)
    {
        return CreateFile(path, genericReadAccess, shareModeAll, IntPtr.Zero, openExisting,
            fileFlagsForOpenReparsePointAndBackupSemantics, IntPtr.Zero);
    }
    
    public static string GetTarget(string path)
    {
        SymbolicLinkReparseData reparseDataBuffer;
    
        using (SafeFileHandle fileHandle = getFileHandle(path))
        {
            if (fileHandle.IsInvalid)
            {
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            }
    
            int outBufferSize = Marshal.SizeOf(typeof(SymbolicLinkReparseData));
            IntPtr outBuffer = IntPtr.Zero;
            try
            {
                outBuffer = Marshal.AllocHGlobal(outBufferSize);
                int bytesReturned;
                bool success = DeviceIoControl(
                    fileHandle.DangerousGetHandle(), ioctlCommandGetReparsePoint, IntPtr.Zero, 0,
                    outBuffer, outBufferSize, out bytesReturned, IntPtr.Zero);
    
                fileHandle.Close();
    
                if (!success)
                {
                    if (((uint)Marshal.GetHRForLastWin32Error()) == pathNotAReparsePointError)
                    {
                        return null;
                    }
                    Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
                }
    
                reparseDataBuffer = (SymbolicLinkReparseData)Marshal.PtrToStructure(
                    outBuffer, typeof(SymbolicLinkReparseData));
            }
            finally
            {
                Marshal.FreeHGlobal(outBuffer);
            }
        }
        if (reparseDataBuffer.ReparseTag != symLinkTag)
        {
            return null;
        }
    
        string target = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
            reparseDataBuffer.PrintNameOffset, reparseDataBuffer.PrintNameLength);
    
        return target;
    }
    

    That is: