using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
#nullable disable
namespace Wabbajack.Common.IO
{
///
/// Represents a special Windows directory and provides methods to retrieve information about it.
///
public sealed class KnownFolder
{
// ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------
///
/// Initializes a new instance of the class for the folder of the given type. It
/// provides the values for the current user.
///
/// The of the known folder to represent.
public KnownFolder(KnownFolderType type) : this(type, WindowsIdentity.GetCurrent()) { }
///
/// Initializes a new instance of the class for the folder of the given type. It
/// provides the values for the given impersonated user.
///
/// The of the known folder to represent.
/// The of the impersonated user which values will be
/// provided.
public KnownFolder(KnownFolderType type, WindowsIdentity identity)
{
Type = type;
Identity = identity;
}
// ---- PROPERTIES ---------------------------------------------------------------------------------------------
///
/// Gets the type of the known folder which is represented.
///
public KnownFolderType Type { get; }
///
/// Gets the of the user whose folder values are provided.
///
public WindowsIdentity Identity { get; }
///
/// Gets the default path of the folder. This does not require the folder to be existent.
///
/// The known folder could not be retrieved.
public string DefaultPath
{
get => GetPath(KnownFolderFlags.DontVerify | KnownFolderFlags.DefaultPath);
}
///
/// Gets or sets the path as currently configured. This does not require the folder to be existent.
///
/// The known folder could not be retrieved.
public string Path
{
get => GetPath(KnownFolderFlags.DontVerify);
set => SetPath(KnownFolderFlags.None, value);
}
///
/// Gets or sets the path as currently configured, with all environment variables expanded.
/// This does not require the folder to be existent.
///
/// The known folder could not be retrieved.
public string ExpandedPath
{
get => GetPath(KnownFolderFlags.DontVerify | KnownFolderFlags.NoAlias);
set => SetPath(KnownFolderFlags.DontUnexpand, value);
}
// ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------
///
/// Creates the folder using its Desktop.ini settings.
///
/// The known folder could not be retrieved.
public void Create()
{
GetPath(KnownFolderFlags.Init | KnownFolderFlags.Create);
}
// ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------
private string GetPath(KnownFolderFlags flags)
{
int result = SHGetKnownFolderPath(Type.GetGuid(), (uint)flags, Identity.Token, out IntPtr outPath);
if (result >= 0)
{
string path = Marshal.PtrToStringUni(outPath);
Marshal.FreeCoTaskMem(outPath);
return path;
}
else
{
throw new ExternalException("Cannot get the known folder path. It may not be available on this system.",
result);
}
}
private void SetPath(KnownFolderFlags flags, string path)
{
int result = SHSetKnownFolderPath(Type.GetGuid(), (uint)flags, Identity.Token, path);
if (result < 0)
{
throw new ExternalException("Cannot set the known folder path. It may not be available on this system.",
result);
}
}
///
/// Retrieves the full path of a known folder identified by the folder's known folder ID.
///
/// A known folder ID that identifies the folder.
/// Flags that specify special retrieval options. This value can be 0; otherwise, one or
/// more of the values.
/// An access token that represents a particular user. If this parameter is NULL, which is
/// the most common usage, the function requests the known folder for the current user. Assigning a value of -1
/// indicates the Default User. The default user profile is duplicated when any new user account is created.
/// Note that access to the Default User folders requires administrator privileges.
/// When this method returns, contains the address of a string that specifies the path of
/// the known folder. The returned path does not include a trailing backslash.
/// Returns S_OK if successful, or an error value otherwise.
/// bb762188
[DllImport("Shell32.dll")]
private static extern int SHGetKnownFolderPath([MarshalAs(UnmanagedType.LPStruct)]Guid rfid, uint dwFlags,
IntPtr hToken, out IntPtr ppszPath);
///
/// Redirects a known folder to a new location.
///
/// A that identifies the known folder.
/// Either 0 or .
///
///
///
/// bb762249
[DllImport("Shell32.dll")]
private static extern int SHSetKnownFolderPath([MarshalAs(UnmanagedType.LPStruct)]Guid rfid, uint dwFlags,
IntPtr hToken, [MarshalAs(UnmanagedType.LPWStr)]string pszPath);
// ---- ENUMERATIONS -------------------------------------------------------------------------------------------
///
/// Represents the retrieval options for known folders.
///
/// dd378447
[Flags]
private enum KnownFolderFlags : uint
{
None = 0x00000000,
SimpleIDList = 0x00000100,
NotParentRelative = 0x00000200,
DefaultPath = 0x00000400,
Init = 0x00000800,
NoAlias = 0x00001000,
DontUnexpand = 0x00002000,
DontVerify = 0x00004000,
Create = 0x00008000,
NoAppcontainerRedirection = 0x00010000,
AliasOnly = 0x80000000
}
}
}