How do you get the UserName of the owner of a process? - c#

I'm trying to get a list of processes currently owned by the current user (Environment.UserName). Unfortunately, the Process class doesn't have any way of getting the UserName of the user owning a process.
How do you get the UserName of the user which is the owner of a process using the Process class so I can compare it to Environment.UserName?
If your solution requires a pinvoke, please provide a code example.

Thanks, your answers put me on the proper path. For those who needs a code sample:
public class App
{
public static void Main(string[] Args)
{
Management.ManagementObjectSearcher Processes = new Management.ManagementObjectSearcher("SELECT * FROM Win32_Process");
foreach (Management.ManagementObject Process in Processes.Get()) {
if (Process["ExecutablePath"] != null) {
string ExecutablePath = Process["ExecutablePath"].ToString();
string[] OwnerInfo = new string[2];
Process.InvokeMethod("GetOwner", (object[]) OwnerInfo);
Console.WriteLine(string.Format("{0}: {1}", IO.Path.GetFileName(ExecutablePath), OwnerInfo[0]));
}
}
Console.ReadLine();
}
}

The CodeProject article How To Get Process Owner ID and Current User SID by Warlib describes how to do this using both WMI and using the Win32 API via PInvoke.
The WMI code is much simpler but is slower to execute. Your question doesn't indicate which would be more appropriate for your scenario.

You will have a hard time getting the username without being an administrator on the computer.
None of the methods with WMI, through the OpenProcess or using the WTSEnumerateProcesses will give you the username unless you are an administrator. Trying to enable SeDebugPrivilege etc does not work either. I have still to see a code that works without being the admin.
If anyone know how to get this WITHOUT being an admin on the machine it is being run, please write how to do it, as I have not found out how to enable that level of access to a service user.

You might look at using System.Management (WMI). With that you can query the Win32_Process tree.

here is the MS link labelled "GetOwner Method of the Win32_Process Class"

Props to Andrew Moore for his answer, I'm merely formatting it because it didn't compile in C# 3.5.
private string GetUserName(string procName)
{
string query = "SELECT * FROM Win32_Process WHERE Name = \'" + procName + "\'";
var procs = new System.Management.ManagementObjectSearcher(query);
foreach (System.Management.ManagementObject p in procs.Get())
{
var path = p["ExecutablePath"];
if (path != null)
{
string executablePath = path.ToString();
string[] ownerInfo = new string[2];
p.InvokeMethod("GetOwner", (object[])ownerInfo);
return ownerInfo[0];
}
}
return null;
}

You'll need to add a reference to System.Management.dll for this to work.
Here's what I ended up using. It works in .NET 3.5:
using System.Linq;
using System.Management;
class Program
{
/// <summary>
/// Adapted from https://www.codeproject.com/Articles/14828/How-To-Get-Process-Owner-ID-and-Current-User-SID
/// </summary>
public static void GetProcessOwnerByProcessId(int processId, out string user, out string domain)
{
user = "???";
domain = "???";
var sq = new ObjectQuery("Select * from Win32_Process Where ProcessID = '" + processId + "'");
var searcher = new ManagementObjectSearcher(sq);
if (searcher.Get().Count != 1)
{
return;
}
var process = searcher.Get().Cast<ManagementObject>().First();
var ownerInfo = new string[2];
process.InvokeMethod("GetOwner", ownerInfo);
if (user != null)
{
user = ownerInfo[0];
}
if (domain != null)
{
domain = ownerInfo[1];
}
}
public static void Main()
{
var processId = System.Diagnostics.Process.GetCurrentProcess().Id;
string user;
string domain;
GetProcessOwnerByProcessId(processId, out user, out domain);
System.Console.WriteLine(domain + "\\" + user);
}
}

Related

how to print Test page on a Printer?

I am new to C# and I am using windows forms, windows 7 and .Net 4.0.
I have 3 printers connected to my computer I want to print windows test page on a specific printer. All printers names are listed in ComboBox as shown in the following code I want to select a printer from ComboBox and print test page.
I had a look Here, Here and Here but nothing helped.
Does anyone know how to do it?
foreach (string printer in System.Drawing.Printing.PrinterSettings.InstalledPrinters)
{
comboBox_Printers.Items.Add(printer);
}
Now, this method might look verbose, but I think it's important, when calling WMI methods, that the Management Options and Scopes are defined correctly.
This gives means to correct/adapt the code to specific contexts, when necessary.
Also, the helper methods here can be reused to initialize any other WMI query.
For example, a wrong Impersonation option, will result in an exception (0x80070005: (E_ACCESSDENIED)) when connecting to the WMI Scope or when the WMI query is executed.
A description of the PrintTestPage method parameters:
string PrinterName: the name of a specific Printer or null to use the default Printer.
string MachineName: the name of a Machine in the Network or null to use the LocalMachine name.
The method returns 0 if successful and throws an exception if the Printer is not found.
Sample call to Print a test page using the default printer in the local machine:
var result = PrintTestPage(null, null);
using System.Linq;
using System.Management;
public static uint PrintTestPage(string PrinterName, string MachineName)
{
ConnectionOptions connOptions = GetConnectionOptions();
EnumerationOptions mOptions = GetEnumerationOptions(false);
string machineName = string.IsNullOrEmpty(MachineName) ? Environment.MachineName : MachineName;
ManagementScope mScope = new ManagementScope($#"\\{machineName}\root\CIMV2", connOptions);
SelectQuery mQuery = new SelectQuery("SELECT * FROM Win32_Printer");
mQuery.QueryString += string.IsNullOrEmpty(PrinterName)
? " WHERE Default = True"
: $" WHERE Name = '{PrinterName}'";
mScope.Connect();
using (ManagementObjectSearcher moSearcher = new ManagementObjectSearcher(mScope, mQuery, mOptions))
{
ManagementObject moPrinter = moSearcher.Get().OfType<ManagementObject>().FirstOrDefault();
if (moPrinter is null) throw new InvalidOperationException("Printer not found");
InvokeMethodOptions moMethodOpt = new InvokeMethodOptions(null, ManagementOptions.InfiniteTimeout);
using (ManagementBaseObject moParams = moPrinter.GetMethodParameters("PrintTestPage"))
using (ManagementBaseObject moResult = moPrinter.InvokeMethod("PrintTestPage", moParams, moMethodOpt))
return (UInt32)moResult["ReturnValue"];
}
}
Helper methods:
private static EnumerationOptions GetEnumerationOptions(bool DeepScan)
{
EnumerationOptions mOptions = new EnumerationOptions()
{
Rewindable = false, //Forward only query => no caching
ReturnImmediately = true, //Pseudo-async result
DirectRead = true, //Skip superclasses
EnumerateDeep = DeepScan //No recursion
};
return mOptions;
}
private static ConnectionOptions GetConnectionOptions()
{
ConnectionOptions connOptions = new ConnectionOptions()
{
EnablePrivileges = true,
Timeout = ManagementOptions.InfiniteTimeout,
Authentication = AuthenticationLevel.PacketPrivacy,
Impersonation = ImpersonationLevel.Impersonate
};
return connOptions;
}

c# ManagementObjectSearcher used for finding process (and its description) with PID

Im working on a program which should list me processes and when i click on certain process it should give me its description.
My idea(Actually i modified something similar what i found on web) was to use ManagementObjectSearcher with sql statement ("Select * From Win32_Process WHERE ProcessID="+a); where "a" is string variable which contains process ID.For some processes its working(chrome,calculator for example) and for some it doesnt(svchost,tiltweelmouse and so on).
private void Lista_procesa_prikaz_MouseClick(object sender, MouseEventArgs e)
{
string a = Lista_procesa_prikaz.Items[Lista_procesa_prikaz.FocusedItem.Index].SubItems[1].Text;
var searcher = new ManagementObjectSearcher("Select * From Win32_Process WHERE ProcessID="+a);
var proces = searcher.Get();
foreach (var process in proces)
{
var processName = process["Name"];
var processPath = process["ExecutablePath"];
if (processPath != null)
{
var fileVersionInfo = FileVersionInfo.GetVersionInfo(processPath.ToString());
var processDescription = fileVersionInfo.FileDescription;
Description_textbox.Text = processDescription.ToString();
}
}
}
IF someone could spot mistake here i would be really happy,
Any help is appreciated
Thank you
EDIT: Partially solved problem,if anyone even cares,platform need to be set to x64(in my case).Go to Project,yourProjectName properties,build,platform target:x64
I don't know if it must be from ManagementObjectSearcher but may I suggest a managed class that gets the same data.
var procs = System.Diagnostics.Process.GetProcesses()
.Where(x => x.Id == 3116);
foreach(var p in procs)
Console.WriteLine(p.ProcessName + p.Id);
Attempting to read the path to the using both approaches will throw if you don't have elevated privileges. As for getting those privileges, consider using a manifest for that.

How to check in C# if user account is active

How can I check from C# if a local user account (namely the local Administrator account) is active?
What I actually want is a C# replacement for the "Account Active" = "Yes" (or "No") output from the "net user Administrator" command.
I'm afraid this question looks like a duplicate to this one, but I don't know what to pass in for the parameter for the root DirectoryEntry object. Tried different things like "ldap://" + Environment.MachineName, "ldap://127.0.0.1", "WinNT://" + Environment.MachineName, but none of them worked. I get an exception thrown by the searcher.FindAll() call in all three cases.
class Program
{
static void Main(string[] args)
{
// Create the context for the principal object.
PrincipalContext ctx = new PrincipalContext(ContextType.Machine);
UserPrincipal u = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, "Administrator");
Console.WriteLine(String.Format("Administrator is enable: {0}", u.Enabled));
}
}
You can query WMI's Win32_UserAccount
This is boilerplate what MS's wmi code creator spits out as a reference;
using System;
using System.Management;
using System.Windows.Forms;
namespace WMISample
{
public class MyWMIQuery
{
public static void Main()
{
try
{
ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT Disabled FROM Win32_UserAccount WHERE name = 'alexk'");
foreach (ManagementObject queryObj in searcher.Get())
{
Console.WriteLine("-----------------------------------");
Console.WriteLine("Win32_UserAccount instance");
Console.WriteLine("-----------------------------------");
Console.WriteLine("Disabled: {0}", queryObj["Disabled"]);
Console.ReadKey();
}
}
catch (ManagementException e)
{
MessageBox.Show("An error occurred while querying for WMI data: " + e.Message);
}
}
}
}
(I'd link the tool but as usual the msdn links are dead)
Try this.
var server = "YOURMACHINENAME";
var username = "Guest";
var de = new DirectoryEntry {Path = "WinNT://" + server + ",computer"};
var result = de.Children
.Cast<DirectoryEntry>()
.First<DirectoryEntry>(d => d.SchemaClassName == "User" && d.Properties["Name"].Value.ToString() == username);
var flags = (int)result.Properties["UserFlags"].Value;
var disabled = (flags & 2) == 2;
This isn't quite the same but they use DirectoryEntry directoryEntry = new DirectoryEntry(string.Format("WinNT://{0}/{1}", computerName, username)); Would that help?
Considering it's a local user, you need to call the win32 api funcion NetGetUserInfo to get what you need.
The example in pinvoke.net is almost what you need, however you need to change the level parameter to 2 to get the neccesary info

How do I retrieve the username that a Windows service is running under?

Given a service name, I would like to retrieve the username that it runs under (i.e. the username shown in the 'Log On' tab of a service's properties window).
There doesn't appear to be anything in the ServiceController class to retrieve this basic information. Nothing else in System.ServiceProcess looks like it exposes this information either.
Is there a managed solution to this, or am I going to have to drop down into something lower-level?
Using WMI, with the System.Management you can try the following code:
using System;
namespace WindowsServiceTest
{
class Program
{
static void Main(string[] args)
{
System.Management.SelectQuery sQuery = new System.Management.SelectQuery(string.Format("select name, startname from Win32_Service")); // where name = '{0}'", "MCShield.exe"));
using (System.Management.ManagementObjectSearcher mgmtSearcher = new System.Management.ManagementObjectSearcher(sQuery))
{
foreach (System.Management.ManagementObject service in mgmtSearcher.Get())
{
string servicelogondetails =
string.Format("Name: {0} , Logon : {1} ", service["Name"].ToString(), service["startname"]).ToString();
Console.WriteLine(servicelogondetails);
}
}
Console.ReadLine();
}
}
}
You can then later substitute the commented code with your service name, and it should only return the instances of your service process that is running.
WMI is your friend. Look at Win32_Service, specifically the StartName property. You can access WMI from C# via the System.Management.ManagementClass.
If you've not used WMI before, this article seems to be quite a good tutorial.
You can find this using the Windows Registry, reading the following string value, replacing [SERVICE_NAME] with the name of the Windows Service:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\[SERVICE_NAME]\ObjectName
Try this:
System.Security.Principal.WindowsIdentity.GetCurrent();
but the most obvious you will get LOCAL SYSTEM or NETWORK. The reason that you cannot show this user - that service can manage multiple users (shared by desktop, attached to current windows session, using shared resource ...)
System starts service, but any user can use it.
This solution works fine for me:
ManagementObject wmiService = new ManagementObject("Win32_Service.Name='" + this.ServiceName + "'");
wmiService.Get();
string user = wmiService["startname"].ToString();
public String getUsername() {
string username = null;
try {
ManagementScope ms = new ManagementScope("\\\\.\\root\\cimv2");
ms.Connect();
ObjectQuery query = new ObjectQuery
("SELECT * FROM Win32_ComputerSystem");
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(ms, query);
foreach (ManagementObject mo in searcher.Get()) {
username = mo["UserName"].ToString();
}
string[] usernameParts = username.Split('\\');
username = usernameParts[usernameParts.Length - 1];
} catch (Exception) {
username = "SYSTEM";
}
return username;
}

Get User SID From Logon ID (Windows XP and Up)

I have a Windows service that needs to access registry hives under HKEY_USERS when users log on, either locally or via Terminal Server. I'm using a WMI query on win32_logonsession to receive events when users log on, and one of the properties I get from that query is a LogonId. To figure out which registry hive I need to access, now, I need the users's SID, which is used as a registry key name beneath HKEY_USERS.
In most cases, I can get this by doing a RelatedObjectQuery like so (in C#):
RelatedObjectQuery relatedQuery = new RelatedObjectQuery( "associators of {Win32_LogonSession.LogonId='" + logonID + "'} WHERE AssocClass=Win32_LoggedOnUser Role=Dependent" );
where "logonID" is the logon session ID from the session query. Running the RelatedObjectQuery will generally give me a SID property that contains exactly what I need.
There are two issues I have with this. First and most importantly, the RelatedObjectQuery will not return any results for a domain user that logs in with cached credentials, disconnected from the domain. Second, I'm not pleased with the performance of this RelatedObjectQuery --- it can take up to several seconds to execute.
Here's a quick and dirty command line program I threw together to experiment with the queries. Rather than setting up to receive events, this just enumerates the users on the local machine:
using System;
using System.Collections.Generic;
using System.Text;
using System.Management;
namespace EnumUsersTest
{
class Program
{
static void Main( string[] args )
{
ManagementScope scope = new ManagementScope( "\\\\.\\root\\cimv2" );
string queryString = "select * from win32_logonsession"; // for all sessions
//string queryString = "select * from win32_logonsession where logontype = 2"; // for local interactive sessions only
ManagementObjectSearcher sessionQuery = new ManagementObjectSearcher( scope, new SelectQuery( queryString ) );
ManagementObjectCollection logonSessions = sessionQuery.Get();
foreach ( ManagementObject logonSession in logonSessions )
{
string logonID = logonSession["LogonId"].ToString();
Console.WriteLine( "=== {0}, type {1} ===", logonID, logonSession["LogonType"].ToString() );
RelatedObjectQuery relatedQuery = new RelatedObjectQuery( "associators of {Win32_LogonSession.LogonId='" + logonID + "'} WHERE AssocClass=Win32_LoggedOnUser Role=Dependent" );
ManagementObjectSearcher userQuery = new ManagementObjectSearcher( scope, relatedQuery );
ManagementObjectCollection users = userQuery.Get();
foreach ( ManagementObject user in users )
{
PrintProperties( user.Properties );
}
}
Console.WriteLine( "\nDone! Press a key to exit..." );
Console.ReadKey( true );
}
private static void PrintProperty( PropertyData pd )
{
string value = "null";
string valueType = "n/a";
if ( pd.Value != null )
{
value = pd.Value.ToString();
valueType = pd.Value.GetType().ToString();
}
Console.WriteLine( " \"{0}\" = ({1}) \"{2}\"", pd.Name, valueType, value );
}
private static void PrintProperties( PropertyDataCollection properties )
{
foreach ( PropertyData pd in properties )
{
PrintProperty( pd );
}
}
}
}
So... is there way to quickly and reliably obtain the user SID given the information I retrieve from WMI, or should I be looking at using something like SENS instead?
I asked a very similar question a while back and got this answer: how to get a SID from a windows username.
I was planning on using SystemEvents to detect when a user logs on to windows, then looping through the logged on users list at that point to detect all the logged on users. (Here's my question, about all this including references for detecting logons and current users.)
If you decide on an approach please post an update - I'd be interested to hear what you find works well.
Another simple way:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion \ProfileList
Another working answer (code in VB.Net)
Public Function GetSIDfromAccName(ByVal strAccName As String) As String
Debug.WriteLine("***WMI-GetSIDfromAccName***")
Dim strSID As String = ""
Try
Dim wmiClass As System.Management.SelectQuery = New System.Management.SelectQuery(("Select * from Win32_UserAccount where Name='" _
+ (strAccName + "'")))
Dim wmiSearcher As System.Management.ManagementObjectSearcher = New System.Management.ManagementObjectSearcher(wmiClass)
For Each val As System.Management.ManagementBaseObject In wmiSearcher.Get
strSID = val("SID").ToString
Next
Catch e As Exception
Debug.WriteLine(e.ToString)
End Try
Return strSID
End Function
Powershell is easier.
Function GetSIDfromAcctName()
{
$myacct = Get-WmiObject Win32_UserAccount -filter "Name = '$env:USERNAME "
write-host Name: $myacct.name
Write-Host SID : $myacct.sid
}

Categories