Skip to content

Instantly share code, notes, and snippets.

@Cyberboss
Created October 20, 2018 17:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Cyberboss/a6e673d4647d27895c77bac860636a40 to your computer and use it in GitHub Desktop.
Save Cyberboss/a6e673d4647d27895c77bac860636a40 to your computer and use it in GitHub Desktop.
Symlink test one off
using System;
using BetterWin32Errors;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Text;
namespace Tgstation.Server.Host
{
/// <summary>
/// Native methods used by the code
/// </summary>
static class NativeMethods
{
/// <summary>
/// See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createsymboliclinka#parameters
/// </summary>
[Flags]
public enum CreateSymbolicLinkFlags : int
{
None = 0,
Directory = 1,
AllowUnprivilegedCreate = 2
}
/// <summary>
/// See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowthreadprocessid
/// </summary>
[DllImport("user32.dll")]
public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
/// <summary>
/// See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-findwindoww
/// </summary>
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
/// <summary>
/// See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-sendmessage
/// </summary>
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
/// <summary>
/// See https://msdn.microsoft.com/en-us/library/ms633493(v=VS.85).aspx
/// </summary>
public delegate bool EnumWindowProc(IntPtr hwnd, IntPtr lParam);
/// <summary>
/// See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-enumchildwindows
/// </summary>
[DllImport("user32.dll")]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr lParam);
/// <summary>
/// See https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowtextw
/// </summary>
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
/// <summary>
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx
/// </summary>
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);
/// <summary>
/// See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createsymboliclinkw
/// </summary>
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, CreateSymbolicLinkFlags dwFlags);
}
}
namespace Tgstation.Server.Host.IO
{
/// <summary>
/// <see cref="ISymlinkFactory"/> for windows systems
/// </summary>
sealed class WindowsSymlinkFactory
{
/// <inheritdoc />
public Task CreateSymbolicLink(string targetPath, string linkPath, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
{
if (targetPath == null)
throw new ArgumentNullException(nameof(targetPath));
if (linkPath == null)
throw new ArgumentNullException(nameof(linkPath));
//check if its not a file
var flags = File.Exists(targetPath) ? NativeMethods.CreateSymbolicLinkFlags.None : NativeMethods.CreateSymbolicLinkFlags.Directory;
flags |= NativeMethods.CreateSymbolicLinkFlags.AllowUnprivilegedCreate;
cancellationToken.ThrowIfCancellationRequested();
if (!NativeMethods.CreateSymbolicLink(linkPath, targetPath, flags))
{
if (Win32Exception.GetLastWin32Error() == Win32Error.ERROR_INVALID_PARAMETER) //SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE isn't supported
{
flags &= ~NativeMethods.CreateSymbolicLinkFlags.AllowUnprivilegedCreate;
if (NativeMethods.CreateSymbolicLink(linkPath, targetPath, flags))
return;
}
throw new Win32Exception();
}
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
}
}
namespace ConsoleApp2
{
class Program
{
static async Task Main(string[] args)
{
try
{
Console.WriteLine("Symlinking \"C:\\TGS4_INSTANCES\\STARTREK13\\Configuration\\GameStaticFiles\\config\" to \"C:\\TGS4_INSTANCES\\STARTREK13\\Game\\53278a00-92e5-48ee-9083-7635367f0a28\\A\\config\"");
await new Tgstation.Server.Host.IO.WindowsSymlinkFactory().CreateSymbolicLink("C:\\TGS4_INSTANCES\\STARTREK13\\Configuration\\GameStaticFiles\\config", "C:\\TGS4_INSTANCES\\STARTREK13\\Game\\53278a00-92e5-48ee-9083-7635367f0a28\\A\\config", default).ConfigureAwait(false);
Console.WriteLine("Done!");
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment