Get the logged on user name in C# - c#

How do i get the current logged on user name in windows 7 (i.e the user who is physically logged on to the console in which the program that i am launching is running).
For example if i am logged on as "MainUser" and run my console application (that will display the logged on user name) as "SubUser", then the program only returns "SubUser" as the currently logged on user.
I used the following 2 techniques to get the user name. Both are not getting me the thing that i want.
System.Environment.GetEnvironmentVariable("USERNAME")
System.Security.Principal.WindowsIdentity.GetCurrent().User;
Note that however, this VBScript code returns the logged on user name irrespective of the user account from which this script is run:
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set compsys_arr = objWMIService.ExecQuery _
("Select * from Win32_ComputerSystem")
For Each sys in compsys_arr
Wscript.Echo "username: " & sys.UserName
Next
Any way it is possible in C#?

I think just converting the WMI calls to c# works just fine for me.
ConnectionOptions oConn = new ConnectionOptions();
System.Management.ManagementScope oMs = new System.Management.ManagementScope("\\\\localhost", oConn);
System.Management.ObjectQuery oQuery = new System.Management.ObjectQuery("select * from Win32_ComputerSystem");
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oMs, oQuery);
ManagementObjectCollection oReturnCollection = oSearcher.Get();
foreach (ManagementObject oReturn in oReturnCollection) {
Console.WriteLine(oReturn["UserName"].ToString().ToLower());
}

I think you'd have to go down a P/Invoke route. You need to find out which WindowStation your process is running within, and then determine the owner of that WindowStation. I don't think that there's a .NET api for determining these things.
Win32 APIs that you'd need to look at, are probably GetProcessWindowStation and GetUserObjectSecurity to find the owner.

Altough I don't understand if you want to get the user name, who is logged on the system or the user name under which the console is running - maybe you could try using System.Environment.UserName - MSDN claims that it shows the logged on user name.

You want the user name of your session. You can find out your session ID by calling ProcessIdToSessionId. Then use WTSQuerySessionInformation to find out the user name.

Related

How to know if there is an interactive session active from windows service?

I'm coding a windows service and in one point I need to know if there is an active interactive session.
I tried using OnSessionChange() and keep in a variable the last SessionChangeReason. When I reach to the mentioned point I compare if it's equal to SessionChangeReason.SessionLogOn. This works with the inconvenient that the service has a delayed start so if the user logs on before the service starts running this information is lost.
I have also seen the System.Environment.Interactive property but as I understand this refers to the process of the current service which is not interactive, so it wouldn't give me the information I need (I might misunderstood this, though).
Is there a way to get this info 'on demand' without having to keep register of the SessionChangeReason?
Edit: Maybe I wasn't clear about this point. Aside from knowing that there is an interactive session I also need to know that it isn't locked.
P/Invoke WTSEnumerateSessions to see if there are additional sessions and what their connected states are. You obviously have to ignore session 0 on Vista+.
You should only do this when your service is started, the session change notification should be used to detect further changes.
Finally I've resigned to knowing specifically that there is a session and isn't locked so we'll work with whether there is an active session or not.
If only knowing there is an active session works for you and you don't want to use pInvoke you can either:
a) Search for the explorer process
Process[] ps = Process.GetProcessesByName("explorer");
bool explorerActive = (ps.Length > 0);
b) Use the following WMI query to get the UserName of the active session:
using System.Management;
ConnectionOptions oConn = new ConnectionOptions();
ManagementScope oMs = new ManagementScope("\\\\localhost", oConn);
ObjectQuery oQuery = new ObjectQuery("select * from Win32_ComputerSystem");
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oMs, oQuery);
ManagementObjectCollection oReturnCollection = oSearcher.Get();
foreach (ManagementObject oReturn in oReturnCollection)
{
if (oReturn["UserName"] == null)
{
// No active session
Console.Write("UserName: null");
}
else
{
// Active session
Console.Write("UserName: " + oReturn["UserName"].ToString());
}
}
If you want to use pInvoke see Anders' answer.

Win32_LogonSession returns incorrect value

No amount of googling has gotten me anywhere with this.
I have the following code that checks if user(s) are logged on to a PC either via console or RDP.
It runs OK but the issue is that after the user logs off, WMI (or something somewhere) still thinks that they are logged on, and in this case, returns true, when it should return false.
If I reboot the PC it goes back to 0 until I login.
I am prepared to accept that this might be a bug within WMI, so if anyone has a better alternative to achieve the same thing I would massively appreciate the advice.
public bool PCInUse(string Hostname)
{
ConnectionOptions connection = new ConnectionOptions();
connection.Username = "username;
connection.Password = "password";
ManagementScope scope = new ManagementScope("\\\\" + Hostname + "\\root\\CIMV2", connection);
try
{
scope.Connect();
var Query = new SelectQuery("SELECT LogonId FROM Win32_LogonSession Where (LogonType= 10) or (LogonType= 2)");
var Searcher = new ManagementObjectSearcher(scope, Query);
if (Searcher.Get().Count > 0)
return true;
else
return false;
}
catch
{
return false;
}
}
Another approach (assuming your process is running as a service under local system or some privileged account) would be to get all running processes, make a list of distinct User Names for those processes (excluding all the system accounts like "SYSTEM", "NETWORK SERVICE", "LOCAL SERVICE") and any remaining unique userIds of the types you included in your "where" clause on the WMI query ought to give you some idea. Sure, it's possible someone might setup a service to run under a user account, and that might screw it up for you, and on servers running IIS or some other application that creates special user accounts, you might have to work around excluding those also.
You might have to include the inverse of your WMI query above to get the list of non-interactive sessions and the userIds associated with them to know what all to exclude. You might also just look for those processes that you know are only in use by an interactive session (like "dwm.exe" and "explorer.exe").

WPF Active Directory Calls performance issues

I have a C# WPF applicaiton that I am trying to perform a light check with the Active Directory Server and am running into serious performance issues of 20-30 seconds for the funciton to run. Using the identical code, I can place it in a Winforms application and it takes about 1 second or less. Since it is a big AD, I am guessing it is pulling all properties for the end user, but I really only want the first and last name of the person (for other purposes), and to ensure the user is in Active Directory.
Here is the code:
public string DomainUserNameGet(string ActiveDirectoryServer, string WindowsUserID) {
/// queries AD to get logged on user's name
string results = "";
try {
// create your domain context
PrincipalContext oPrincipalContext = new PrincipalContext(
ContextType.Domain
, ActiveDirectoryServer
);
UserPrincipal oUserPrincipal = UserPrincipal.FindByIdentity(
oPrincipalContext
, IdentityType.SamAccountName
, ActiveDirectoryServer + #"\" + WindowsUserID
);
results =
oUserPrincipal.GivenName.ToString()
+ " "
+ oUserPrincipal.Surname.ToString();
} catch { }
return results;
}
The crazy thing is I can do the following via command line and get the response in about 1 second:
NET USER /DOMAIN LANID | find "LANID" /c
Any ideas on how I can improve performance?
RenniePet had it right; Turns out there was a DNS issue; I am unsure why this showed up in WPF vs. win forms though.

Getting the current logged in user (FullToken Context)

I have a Problem, which is... i start a programm with right click -> run as administrator.
Which means the programm is running in an administrative context.
WindowsIdentity.GetCurrent().Name;
if i try to get the user name that way i will get the user that started the programm as admin.. for example "administrator", but what i need is the name of the current logged in user which is for example: bob
Can anybody help me out? :)
You could try using WMI (System.Management.dll) to get the owner of the explorer.exe process.
string GetExplorerUser()
{
var query = new ObjectQuery(
"SELECT * FROM Win32_Process WHERE Name = 'explorer.exe'");
var explorerProcesses = new ManagementObjectSearcher(query).Get();
foreach (ManagementObject mo in explorerProcesses)
{
string[] ownerInfo = new string[2];
mo.InvokeMethod("GetOwner", (object[])ownerInfo);
return String.Concat(ownerInfo[1], #"\", ownerInfo[0]);
}
return string.Empty;
}
This relies on the fact that the explorer process is single instance an so you don't end up with the possibility of having several explorer processes running with different user credentials.
You will probably need to use win32 API for that. Read about Window Station and Desktop functions here: http://msdn.microsoft.com/en-us/library/ms687107%28v=vs.85%29.aspx
Also see this question:
Get the logged in Windows user name associated with a desktop
Maybe you could start as normal user, save user name, then programmatically request elevation :
Windows 7 and Vista UAC - Programmatically requesting elevation in C#
All .NET libraries will give you the user from the current context ('Administrator' in your case).
If you are trying to secure your code, you might consider reading about: Security in the .NET framework
1) Cassia should be able to give you a list of currently logged in users including RDC.
foreach (ITerminalServicesSession sess in new TerminalServicesManager().GetSessions())
{
// sess.SessionId
// sess.UserName
}
2) WMI (SO answer)
Select * from Win32_LogonSession
3) PInvoke to WTSEnumerateSessions
4) Enumerate all instances of "explorer.exe" and get the owner using PInvoke (OpenProcessHandle).
Process[] processes = Process.GetProcessesByName("explorer");
This is a bit hacky. WMI can also be used for this.
It might be a good idea to set winmgmt as a dependency for your service if you decided to go with solution that uses WMI.

Granting remote user (non admin) the ability to enumerate services in Win32_Service in namespace cimv2 using WMI & C#

I'm creating a watch dog service that will be monitoring other services on various remote servers (all in the same domain). The user that I'm using to connect to the remote servers is not an admin. When I try to enumerate the services in the Win32_Service class, I get an access denied error.
I've given the user 'Remote Enable' & 'Enable Account' persmissions to the Root\CIMV2 namespace in the WMI Control.
I am able to connect to the server with the following code. The object ServiceListItem is just a simple class that contains the server name and the service name:
SecureString secureString = new SecureString();
foreach ( char c in "password" )
{
secureString.AppendChar( c );
}
ConnectionOptions connectionOptions = new ConnectionOptions();
connectionOptions.Username = "domain\\user";
connectionOptions.SecurePassword = secureString;
foreach ( ServiceListItem service in _serviceList )
{
ManagementScope managementScope = new ManagementScope();
managementScope = new ManagementScope( String.Format( #"\\{0}\root\cimv2", service.ServerName ), connectionOptions );
managementScope.Connect();
//RelatedObjectQuery relatedObjectQuery = new RelatedObjectQuery( String.Format( "Win32_Service.Name='{0}'", service.ServiceName ) );
//ManagementObjectSearcher objectSearcher = new ManagementObjectSearcher( managementScope, relatedObjectQuery );
ObjectQuery objectQuery = new ObjectQuery( "SELECT * FROM Win32_Service WHERE Name = '" + service.ServiceName + "'" );
ManagementObjectSearcher objectSearcher = new ManagementObjectSearcher( managementScope, objectQuery );
ManagementObjectCollection objectCollection = objectSearcher.Get();
foreach ( ManagementObject managementObject in objectCollection )
{
serviceStatus = managementObject.Properties["State"].Value.ToString();
Debug.Print(service.ServiceName + " - " + serviceStatus);
//break;
}
}
The managementScope.Connect() runs fine, which means the wmi security on cimv2 is set up correctly. However, when I try to enumerate the objectCollection, I get the 'Access Denied' exception. This tells me (I think) that the user doesn't have permissions to enumerate the Win32_Service class (SC_MANAGER_ENUMERATE_SERVICE).
I just haven't been able to find any good examples on how to enable that permission for a remote user. I'm not very experienced when it comes to coding with Windows api's, so please be as detailed as possible in your answers :)
Trying to find the same answer myself today, I've been doing a lot of googling. After a good half hour of incantations, I found this MSDN article (907460) which uses sc sdet. It seems to work so far, even though the security descriptor is for Windows Server 2003. I've found you can do sc sdshow SCMANAGER to get the current value so when back in the office tomorrow I'll be comparing an contrasting to make sure I've not locked something out I shouldn't have :-)
For completeness, the notes in KB907460 (in case it moves/goes away):
Symptoms: After you install Microsoft Windows Server 2003 Service Pack 1 (SP1), non-administrators cannot remotely access the Service Control Manager.
Cause: Windows Server 2003 SP1 changes the Service Control Manager default security settings.
Resolution:
To resolve this issue, use version 5.2.3790.1830 of the Sc.exe tool.
This tool is located in the %windir%\System32 folder. To do this,
follow these steps:
Click Start, click Run, type cmd, and then click OK.
Type the following command at the command prompt, and then press ENTER:
sc sdset SCMANAGER D:(A;;CCLCRPRC;;;AU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)S:(AU;FA;KA;;;WD)(AU;OIIOFA;GA;;;WD)
I found myself stuck into a similar problem. In my case it had nothing to do with permissions, which I did set by following this link: http://www.poweradmin.com/help/enableWMI.aspx
So, After hours of wondering lost I found this article that tells how UAC interfere with your set of permissions and how can you fix that:
http://www.solarwinds.com/documentation/apm/docs/APMWMITroubleshooting.pdf
In my case, the registry key didn't existed, so I created it.
Hope this helps also, cheers!

Categories