Logged in user for application

928 views Asked by At

I am using the following code to get the Current user and the owner of my application's process.

Current User:

CurrentUser = WindowsIdentity.GetCurrent().Name;

Process Owner:

String strQuery = String.Format("Select * From Win32_Process where Name='{0}'", ProcessName);
ObjectQuery oQuery = new ObjectQuery(strQuery);

ManagementObjectSearcher oManSearcher = new ManagementObjectSearcher(oQuery);

foreach (ManagementObject oManItem in oManSearcher.Get())
{
    String[] s = new String[2];

    oManItem.InvokeMethod("GetOwner", (object[])s);

    ProcessOwner = s[0];

    break;
}

I need to determine grammatically, which user account the instance of my application runs in.

Basically, I allow one instance of my application per logged in user.

The above code works, when a user launches the application normally. The code breaks down, when a user right clicks on a shortcut (or using a similar method) and selects "run as administrator". The process owner in this case will not be the logged in user, but rather that of the administrator.

That wreaks havoc in determining a multiple instance, not to mention the proper database path, etc.

From my perspective, an instance launched normally and an instance launched as an administrator are both instances under the same logged in user, just the owner is different.

How do I determine what logged in user a process belongs to? Checking process owner to the current user is not, as stated, always the best check.

ANSWER:

Here is the completed method, developed from the selected answer.

public static String GetUsernameBySessionId(int sessionId, Boolean prependDomain)
{
    IntPtr buffer;
    int strLen;
    String username = "SYSTEM";
    if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSUserName, out buffer, out strLen) && strLen > 1)
    {
        username = Marshal.PtrToStringAnsi(buffer);
        WTSFreeMemory(buffer);
        if (prependDomain)
        {
            if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSDomainName, out buffer, out strLen) && strLen > 1)
            {
                username = String.Format("{0}\\{1}", Marshal.PtrToStringAnsi(buffer), username);
                WTSFreeMemory(buffer);
            }
        }
    }

    return username;
}

The current process SessionId is:

Process.GetCurrentProcess().SessionId
3

There are 3 answers

1
alu On BEST ANSWER

To get the logged in user of an application I would suggest to read the SessionId of the current process that is independent from the user actually running the application. The SessionId of a process can be read with:

System.Diagnostics.Process.GetCurrentProcess().SessionId

The username associated with a SessionId can be retrieved using the function from this answer: Get Windows user name from SessionID .

The function GetUsernameBySessionId also requires this enumeration:

public enum WtsInfoClass
{
    WTSInitialProgram = 0,
    WTSApplicationName = 1,
    WTSWorkingDirectory = 2,
    WTSOEMId = 3,
    WTSSessionId = 4,
    WTSUserName = 5,
    WTSWinStationName = 6,
    WTSDomainName = 7,
    WTSConnectState = 8,
    WTSClientBuildNumber = 9,
    WTSClientName = 10,
    WTSClientDirectory = 11,
    WTSClientProductId = 12,
    WTSClientHardwareId = 13,
    WTSClientAddress = 14,
    WTSClientDisplay = 15,
    WTSClientProtocolType = 16,
    WTSIdleTime = 17,
    WTSLogonTime = 18,
    WTSIncomingBytes = 19,
    WTSOutgoingBytes = 20,
    WTSIncomingFrames = 21,
    WTSOutgoingFrames = 22,
    WTSClientInfo = 23,
    WTSSessionInfo = 24,
    WTSSessionInfoEx = 25,
    WTSConfigInfo = 26,
    WTSValidationInfo = 27,
    WTSSessionAddressV4 = 28,
    WTSIsRemoteSession = 29
}
1
DSway On

System.Environment.UserName - Gets the user name of the person who is currently logged on to the Windows operating system.

Is that what you are looking for?

Edit: May work if you only need to get the value when the process starts. If they switch users and the process continues, the value could change.

0
Ashigore On

You should provide this functionality using a Mutex:

Adapted from: What is a good pattern for using a Global Mutex in C#?

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
    // Get application GUID
    string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), false).GetValue(0)).Value.ToString();
    WindowsIdentity currentUser = WindowsIdentity.GetCurrent();
    // Get user SID
    string userSid = currentUser.User.ToString();

    // Construct application & user specific mutex ID
    string mutexID = string.Format(@"Global\{{{0}}}-{{{1}}}", appGuid, userSid);

    using (Mutex mutex = new Mutex(false, mutexID))
    {
        MutexAccessRule allowUserRule = new MutexAccessRule(currentUser.User, MutexRights.FullControl, AccessControlType.Allow);
        MutexSecurity securitySettings = new MutexSecurity();
        securitySettings.AddAccessRule(allowUserRule);
        mutex.SetAccessControl(securitySettings);

        bool hasHandle = false;

        try
        {
            try
            {
                hasHandle = mutex.WaitOne(5000, false);
            }
            catch (AbandonedMutexException)
            {
                // Log the fact the mutex was abandoned in another process, it will still get aquired
                hasHandle = true;
            }

            if (hasHandle == false) return;

            // Start application
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
        finally
        {
            if (hasHandle) mutex.ReleaseMutex();
        }
    }
}

Adjust the timeout on mutex.WaitOne to however long you want the application to wait for exclusive access before quitting.