-1

My app is a windows desktop exe, and windows service which runs under the System account. My windows service needs to integrate with a 3rd party app that the user will also install which stores some config info in an ini file within one of the windows special-folders at: C:\Users\[UserName]\AppData\Local\[3rd party app name]

How can my Windows Service retrieve the current user's path to that folder to read the ini file in it, when the windows service runs as System account?

Ideally, I would have used something like

Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)

which returns the correct \AppData\Local\ folder when run from an app running as the current user.

But because my windows service is running as SYSTEM (which cannot be changed) that method instead returns: C:\Windows\system32\config\systemprofile\AppData\Local

So how can my windows service get the currently logged in user's LocalApplicationData special folder?

Gary Barrett
  • 1,764
  • 5
  • 21
  • 33
  • step 1: identify the user whose folder you're looking for. step 2: ???, step 3: profit. Before you begin anything, the first question you need to answer is "how will I know what folder to look in?". Do you always know what [UserName] is or might that change? If it's static, that simplifies things, if it changes...well you've got to sort that out before you figure out how to access their app data folder. – gilliduck Nov 29 '22 at 15:17
  • It should be apparent that a user's folder (including subfolders) is an inappropriate storage location in this case. Store your data in a location that is common to all users such as `%ProgramData%`. – Tu deschizi eu inchid Nov 29 '22 at 15:34
  • @gilliduck I do not know the [UserName], my app and windows service may be installed on 100s of machines. – Gary Barrett Nov 29 '22 at 15:34
  • @user09938 It is a 3rd party app that is putting its config info into that C:\\\[UserName]\\AppData\\Local folder, so I have no control over that and cannot change its storage location. But my service needs to read the config info in there. – Gary Barrett Nov 29 '22 at 15:38
  • It's unclear what your program does such that it needs to run as _SYSTEM (which cannot be changed)_. Since more than one user could be logged in at a time, it seems that you are really only left with the option of checking all users' folders for the data and determine which has the desired data (ie: the file with the most recent lastupdated time). – Tu deschizi eu inchid Nov 29 '22 at 15:46
  • 1
    The current user _is_ System. – 500 - Internal Server Error Nov 29 '22 at 15:52
  • @500-InternalServerError Well, yes? But that System account still needs to retrieve the path to the normal user's C:\\[UserName]\\AppData\\Local folder, when I do not know in advance what [UserName] is. So, I am not sure how that helps? – Gary Barrett Nov 29 '22 at 16:00
  • My point is that from the perspective of the service running in the System account there is no such thing as `the normal user` - there could be any number of users logged into the box while the service is running - or none at all. – 500 - Internal Server Error Nov 29 '22 at 16:02
  • @500-InternalServerError OK, then it is the currently logged in user that I am considering here. I am confident that in my case 99% of the time there will only be 1 logged in user. – Gary Barrett Nov 29 '22 at 16:07
  • Okay, answers to [this question](https://stackoverflow.com/questions/7065561/from-windowsservice-how-can-i-find-currently-logged-in-user-from-c) may have some clues for you. – 500 - Internal Server Error Nov 29 '22 at 16:41

1 Answers1

0

The code below shows how to get a list of logged in users. Once you have a list of logged in users, then you can check the last updated time of the file (File.GetLastWriteTime) that you're interested in.

Add Reference: System.Management

Create a class (name: UserInfo.cs)

UserInfo.cs:

public class UserInfo : IComparable<UserInfo>
{
    public string Caption { get; set; }
    public string DesktopName { get; set; }
    public string Domain { get; set; }
    public bool IsLocalAccount { get; set; } = false;
    public bool IsLoggedIn { get; set; } = false;
    public bool IsRoamingConfigured { get; set; } = false;
    public DateTime LastUploadTime { get; set; }
    public DateTime LastUseTime { get; set; }
    public string LocalPath { get; set; }
    public bool IsProfileLoaded { get; set; } = false;
    public string ProfilePath { get; set; }
    public string SID { get; set; }
    public uint SIDType { get; set; }
    public string Status { get; set; }
    public string Username { get; set; }

    public int CompareTo(UserInfo other)
    {
        //sort by name
        if (this.Caption == other.Caption)
            return 0;
        else if (String.Compare(this.Caption, other.Caption) > 1)
            return 1;
        else
            return -1;
    }

    public override string ToString()
    {
        string output = string.Empty;
        output += $"Caption: '{Caption}'{Environment.NewLine}";
        output += $"Domain: '{Domain}'{Environment.NewLine}";
        output += $"Username: '{Username}'{Environment.NewLine}";
        output += $"IsProfileLoaded: '{IsProfileLoaded}'{Environment.NewLine}";
        output += $"IsRoamingConfigured: '{IsRoamingConfigured}'{Environment.NewLine}";
        output += $"LocalPath: '{LocalPath}'{Environment.NewLine}";
        output += $"LastUseTime: '{LastUseTime.ToString("yyyy/MM/dd HH:mm:ss")}'{Environment.NewLine}";
        output += $"SID: '{SID}'{Environment.NewLine}";

        return output;
    }
}

GetLoggedInUserInfo:

public List<UserInfo> GetLoggedInUserInfo()
{
    List<UserInfo> users = new List<UserInfo>();

    //create reference
    System.Globalization.CultureInfo cultureInfo = System.Globalization.CultureInfo.CurrentCulture;

    using (ManagementObjectSearcher searcherUserAccount = new ManagementObjectSearcher("SELECT Caption, Domain, LocalAccount, Name, SID, SIDType, Status FROM Win32_UserAccount WHERE Disabled = false"))
    {
        foreach (ManagementObject objUserAccount in searcherUserAccount.Get())
        {
            if (objUserAccount == null)
                continue;

            //create new instance
            UserInfo userInfo = new UserInfo();

            string caption = objUserAccount["Caption"].ToString();

            //set value
            userInfo.Caption = caption;
            userInfo.Domain = objUserAccount["Domain"].ToString();

            userInfo.IsLocalAccount = (bool)objUserAccount["LocalAccount"];
            userInfo.Username = objUserAccount["Name"].ToString();

            string sid = objUserAccount["SID"].ToString();

            userInfo.SID = sid;
            userInfo.SIDType =  Convert.ToUInt32(objUserAccount["SIDType"]);
            userInfo.Status = objUserAccount["Status"].ToString();

            using (ManagementObjectSearcher searcherUserProfile = new ManagementObjectSearcher($"SELECT LastUseTime, LastUploadTime, Loaded, LocalPath, RoamingConfigured FROM Win32_UserProfile WHERE SID = '{sid}'"))
            {
                foreach (ManagementObject objUserProfile in searcherUserProfile.Get())
                {
                    if (objUserProfile == null)
                        continue;

                    if (objUserProfile["LastUploadTime"] != null)
                    {
                        string lastUploadTimeStr = objUserProfile["LastUploadTime"].ToString();

                        if (lastUploadTimeStr.Contains("."))
                            lastUploadTimeStr = lastUploadTimeStr.Substring(0, lastUploadTimeStr.IndexOf("."));

                        DateTime lastUploadTime = DateTime.MinValue;

                        //convert DateTime
                        if (DateTime.TryParseExact(lastUploadTimeStr, "yyyyMMddHHmmss", cultureInfo, System.Globalization.DateTimeStyles.AssumeUniversal, out lastUploadTime))
                            userInfo.LastUseTime = lastUploadTime;

                        //set value
                        userInfo.LastUploadTime = lastUploadTime;
                    }


                    string lastUseTimeStr = objUserProfile["LastUseTime"].ToString();

                    if (lastUseTimeStr.Contains("."))
                        lastUseTimeStr = lastUseTimeStr.Substring(0, lastUseTimeStr.IndexOf("."));

                    DateTime lastUseTime = DateTime.MinValue;

                    //convert DateTime
                    if (DateTime.TryParseExact(lastUseTimeStr, "yyyyMMddHHmmss", cultureInfo, System.Globalization.DateTimeStyles.AssumeUniversal, out lastUseTime))
                        userInfo.LastUseTime = lastUseTime;

                    //Debug.WriteLine($"LastUseTime: '{objUserProfile["LastUseTime"].ToString()}' After Conversion: '{lastUseTime.ToString("yyyy/MM/dd HH:mm:ss")}'");

                    userInfo.LocalPath = objUserProfile["LocalPath"].ToString();
                    userInfo.IsProfileLoaded = (bool)objUserProfile["Loaded"];
                    userInfo.IsRoamingConfigured = (bool)objUserProfile["RoamingConfigured"];
                }
            }

            if (userInfo.IsProfileLoaded)
            {
                Debug.WriteLine(userInfo.ToString());

                //add
                users.Add(userInfo);
            }
        }
    }

    //sort by LastUseTime
    users.Sort(delegate (UserInfo info1, UserInfo info2) { return info1.LastUseTime.CompareTo(info2.LastUseTime); });

    return users;
}

Usage:

List<UserInfo> users = GetLoggedInUserInfo();

Resources:

Tu deschizi eu inchid
  • 4,117
  • 3
  • 13
  • 24