How to get the My Documents folder paths from all users of a machine

3

I am looking for a way to get the paths of the My Documents folders from all users (each user) of a local machine. I found several articles, but they show how to do this for the current user.

I tested the code below, using SHGetKnownFolderPath, but it works only for the logged user. In the class ctor that receives a WindowsIdentity object, I create it with tokens of other users, but the paths returned were of the logged-in user.

Does anyone know how I could get the folders paths?

Thanks.

using Syroot.Windows.IO;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace ConsoleApplication2
{
class Program
{
    private static Dictionary<KnownFolderType, KnownFolder> _knownFolderInstances;
    static void Main(string[] args)
    {
        KnownFolderType type = KnownFolderType.Documents;
        KnownFolder knownFolder = new KnownFolder(type);

    //Ctor with WindowsIdentity parameter
    //public KnownFolder(KnownFolderType type, WindowsIdentity identity)
    //{
    //    Type = type;
    //    Identity = identity;
    //}

    // Write down the current and default path.
    Console.WriteLine(knownFolder.Type.ToString());
        try
        {
            Console.Write("Current Path: ");
            Console.WriteLine(knownFolder.Path);
            Console.Write("Default Path: ");
            Console.WriteLine(knownFolder.DefaultPath);
        }
        catch (ExternalException ex)
        {
            // While uncommon with personal folders, other KnownFolders don't exist on every system, and trying
            // to query those returns an error which we catch here.
            Console.WriteLine("<Exception> " + ex.ErrorCode);
        }

        Console.ReadLine();
    }

    private static KnownFolder GetInstance(KnownFolderType type)
    {
        // Check if the caching directory exists yet.
        if (_knownFolderInstances == null)
        {
            _knownFolderInstances = new Dictionary<KnownFolderType, KnownFolder>();
        }

        // Get a KnownFolder instance out of the cache dictionary or create it when not cached yet.
        KnownFolder knownFolder;
        if (!_knownFolderInstances.TryGetValue(type, out knownFolder))
        {
            knownFolder = new KnownFolder(type);
            _knownFolderInstances.Add(type, knownFolder);
        }
        return knownFolder;
    }

    /// <summary>
    /// The per-user Documents folder.
    /// Defaults to &quot;%USERPROFILE%\Documents&quot;.
    /// </summary>
    public static KnownFolder Documents
    {
        get { return GetInstance(KnownFolderType.Documents); }
    }
}
}

KnownFolder.cs

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace Syroot.Windows.IO
{
/// <summary>
/// Represents a special Windows directory and provides methods to retrieve information about it.
/// </summary>
public sealed class KnownFolder
{
    // ---- CONSTRUCTORS & DESTRUCTOR ------------------------------------------------------------------------------

    /// <summary>
    /// Initializes a new instance of the <see cref="KnownFolder"/> class for the folder of the given type. It
    /// provides the values for the current user.
    /// </summary>
    /// <param name="type">The <see cref="KnownFolderType"/> of the known folder to represent.</param>
    public KnownFolder(KnownFolderType type)
        : this(type, WindowsIdentity.GetCurrent())
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="KnownFolder"/> class for the folder of the given type. It
    /// provides the values for the given impersonated user.
    /// </summary>
    /// <param name="type">The <see cref="KnownFolderType"/> of the known folder to represent.</param>
    /// <param name="identity">The <see cref="WindowsIdentity"/> of the impersonated user which values will be
    /// provided.</param>
    public KnownFolder(KnownFolderType type, WindowsIdentity identity)
    {
        Type = type;
        Identity = identity;
    }

    // ---- PROPERTIES ---------------------------------------------------------------------------------------------

    /// <summary>
    /// Gets the type of the known folder which is represented.
    /// </summary>
    public KnownFolderType Type
    {
        get;
        private set;
    }

    /// <summary>
    /// Gets the <see cref="WindowsIdentity"/> of the user whose folder values are provided.
    /// </summary>
    public WindowsIdentity Identity
    {
        get;
        private set;
    }

    /// <summary>
    /// Gets or sets the default path of the folder.
    /// This does not require the folder to be existent.
    /// </summary>
    /// <exception cref="ExternalException">The known folder could not be retrieved.</exception>
    public string DefaultPath
    {
        get
        {
            return GetPath(KnownFolderFlags.DontVerify | KnownFolderFlags.DefaultPath);
        }
        set
        {

        }
    }

    /// <summary>
    /// Gets or sets the path as currently configured.
    /// This does not require the folder to be existent.
    /// </summary>
    /// <exception cref="ExternalException">The known folder could not be retrieved.</exception>
    public string Path
    {
        get
        {
            return GetPath(KnownFolderFlags.DontVerify);
        }
        set
        {
            SetPath(KnownFolderFlags.None, value);
        }
    }

    /// <summary>
    /// Gets or sets the path as currently configured, with all environment variables expanded.
    /// This does not require the folder to be existent.
    /// </summary>
    /// <exception cref="ExternalException">The known folder could not be retrieved.</exception>
    public string ExpandedPath
    {
        get
        {
            return GetPath(KnownFolderFlags.DontVerify | KnownFolderFlags.NoAlias);
        }
        set
        {
            SetPath(KnownFolderFlags.DontUnexpand, value);
        }
    }

    // ---- METHODS (PUBLIC) ---------------------------------------------------------------------------------------

    /// <summary>
    /// Creates the folder using its Desktop.ini settings.
    /// </summary>
    /// <exception cref="ExternalException">The known folder could not be retrieved.</exception>
    public void Create()
    {
        GetPath(KnownFolderFlags.Init | KnownFolderFlags.Create);
    }

    // ---- METHODS (PRIVATE) --------------------------------------------------------------------------------------

    private string GetPath(KnownFolderFlags flags)
    {
        IntPtr outPath;
        int result = SHGetKnownFolderPath(Type.GetGuid(), (uint)flags, Identity.Token, out outPath);
        if (result >= 0)
        {
            return Marshal.PtrToStringUni(outPath);
        }
        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);
        }
    }

    /// <summary>
    /// Retrieves the full path of a known folder identified by the folder's known folder ID.
    /// </summary>
    /// <param name="rfid">A known folder ID that identifies the folder.</param>
    /// <param name="dwFlags">Flags that specify special retrieval options. This value can be 0; otherwise, one or
    /// more of the <see cref="KnownFolderFlags"/> values.</param>
    /// <param name="hToken">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.</param>
    /// <param name="ppszPath">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.</param>
    /// <returns>Returns S_OK if successful, or an error value otherwise.</returns>
    /// <msdn-id>bb762188</msdn-id>
    [DllImport("Shell32.dll")]
    private static extern int SHGetKnownFolderPath([MarshalAs(UnmanagedType.LPStruct)]Guid rfid, uint dwFlags,
        IntPtr hToken, out IntPtr ppszPath);

    /// <summary>
    /// Redirects a known folder to a new location.
    /// </summary>
    /// <param name="rfid">A <see cref="Guid"/> that identifies the known folder.</param>
    /// <param name="dwFlags">Either 0 or <see cref="KnownFolderFlags.DontUnexpand"/>.</param>
    /// <param name="hToken"></param>
    /// <param name="pszPath"></param>
    /// <returns></returns>
    /// <msdn-id>bb762249</msdn-id>
    [DllImport("Shell32.dll")]
    private static extern int SHSetKnownFolderPath([MarshalAs(UnmanagedType.LPStruct)]Guid rfid, uint dwFlags,
        IntPtr hToken, [MarshalAs(UnmanagedType.LPWStr)]string pszPath);

    // ---- ENUMERATIONS -------------------------------------------------------------------------------------------

    /// <summary>
    /// Represents the retrieval options for known folders.
    /// </summary>
    /// <msdn-id>dd378447</msdn-id>
    [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
    }
}
}
c#
.net
asked on Stack Overflow Nov 9, 2017 by Alvimar • edited Nov 10, 2017 by Alvimar

1 Answer

1

I figured it out. I got all SIDs from the system and then searched the Windows registry for each SID by the "Personal" key in the following format: "HKEY_USERS" + "SID" + "\ Software \ Microsoft \ Windows \ CurrentVersion \ Explorer \ Shell Folders \ Personal ". The "Personal" key retains the current path of each user's "My Documents" folder.

Get SIDs:

    public static List<string> GetMachineSids()
    {
        ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_UserProfile");
        var regs = searcher.Get();
        string sid;

        List<string> sids = new List<string>();

        foreach (ManagementObject os in regs)
        {
            if (os["SID"] != null)
            {
                sid = os["SID"].ToString();
                sids.Add(sid);
            }
        }

        searcher.Dispose();
        return sids.Count > 0 ? sids : null;
    }

Get MyDocuments Path:

    public static List<string> GetMyDocumentsPathAllUsers()
    {
        const string parcialSubkey = @"\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders";
        string subkey = string.Empty;
        const string keyName = "Personal";

        //get sids
        List<string> sids = GetMachineSids();
        List<string> myDocumentsPaths = new List<string>();

        if (sids != null)
        {
            foreach (var sid in sids)
            {
                //get paths                  
                subkey = sid + parcialSubkey;

                using (RegistryKey key = Registry.Users.OpenSubKey(subkey))
                {
                    if (key != null)
                    {
                        Object o = key.GetValue(keyName);
                        if (o != null)
                        {
                            myDocumentsPaths.Add(o.ToString());
                        }
                    }
                }
            }
        }

        return myDocumentsPaths.Count > 0 ? myDocumentsPaths : null;
    }
answered on Stack Overflow Nov 13, 2017 by Alvimar

User contributions licensed under CC BY-SA 3.0