Create symbolic links in .NET

 
 
  • Gérald Barré

A symbolic link is a pointer to a file or directory that exists independently of its target. Deleting the link leaves the target unaffected, and deleting the target does not automatically remove the link. This differs from hard links, which always refer to an existing file.

Windows has supported symbolic links since Windows Vista. By default, creating them requires administrative privileges. Enabling Developer Mode removes this requirement. On Linux, no elevated privileges are needed.

To create a symbolic link in .NET, use the CreateSymbolicLink function on Windows and the POSIX function on other systems. To avoid writing the P/Invoke yourself, use the Microsoft.Windows.CsWin32 package, which provides a managed API for Windows functions. For POSIX, use the Mono.Posix.NETStandard package.

Let's create a new console application and add the required packages:

Shell
dotnet new console
dotnet add package Mono.Posix.NETStandard
dotnet add package Microsoft.Windows.CsWin32

Add a file named NativeMethods.txt with the following content:

NativeMethods.txt
CreateSymbolicLink

Finally, add the following code to your Program.cs file:

Program.cs (C#)
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Windows.Win32;
using Windows.Win32.Storage.FileSystem;

CreateSymbolicLink("link", "target_file_or_folder");

static void CreateSymbolicLink(string symlinkPath, string targetPath)
{
    if (OperatingSystem.IsWindows())
    {
        if (!OperatingSystem.IsWindowsVersionAtLeast(6, 0, 6000))
            throw new Win32Exception("Only supported on Windows 6.0.6000");

        CreateSymbolicLinkWindows(symlinkPath, targetPath);
    }
    else
    {
        CreateSymbolicLinkPosix(symlinkPath, targetPath);
    }
}

[SupportedOSPlatform("windows6.0.6000")]
static void CreateSymbolicLinkWindows(string symlinkPath, string targetPath)
{
    var flags = SYMBOLIC_LINK_FLAGS.SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
    if (Directory.Exists(targetPath))
    {
        flags |= SYMBOLIC_LINK_FLAGS.SYMBOLIC_LINK_FLAG_DIRECTORY;
    }

    if (!PInvoke.CreateSymbolicLink(symlinkPath, targetPath, flags))
    {
        var error = Marshal.GetLastWin32Error();
        throw new Win32Exception(error, "Cannot create the symbolic link. You may need to enable Developer Mode or run the tests as admin.");
    }
}

static void CreateSymbolicLinkPosix(string symlinkPath, string targetPath)
{
    var result = Mono.Unix.Native.Syscall.symlink(targetPath, symlinkPath);
    if (result != 0)
    {
        var error = Mono.Unix.Native.Stdlib.GetLastError();
        throw new Win32Exception((int)error, "Cannot create the symbolic link: " + error);
    }
}

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?