Different set of properties in EnvironmentVariables when run in WindowService

B

Bjorn Abelli

Hi all,

When I run a normal application I can get who started a process with the
following example:

Process[] myProcesses = Process.GetProcesses();

foreach(Process p in myProcesses)
{
string user = p.StartInfo.EnvironmentVariables["username"];
}

But when I try to run exactly the same code within a WindowService as
System, the property "username" doesn't seem to exist anymore as an option
in the EnvironmentVariables!

Can anyone explain why it's so, and hopefully show how I can get this
information in some other way?
 
D

David Browne

Bjorn Abelli said:
Hi all,

When I run a normal application I can get who started a process with the
following example:

Process[] myProcesses = Process.GetProcesses();

foreach(Process p in myProcesses)
{
string user = p.StartInfo.EnvironmentVariables["username"];
}

But when I try to run exactly the same code within a WindowService as
System, the property "username" doesn't seem to exist anymore as an option
in the EnvironmentVariables!

Can anyone explain why it's so, and hopefully show how I can get this
information in some other way?

This is because you inherit your environment variables from your parent
process. A "normal application" is one launched by Explorer.exe, which adds
a bunch of environment variables.

Services aren't launched by Explorer, and don't have the same environment
variables.

You can try this intead:

System.Security.Principal.WindowsIdentity.GetCurrent().Name

David
 
B

Bjorn Abelli

When I run a normal application I can get who
started a process with the
following example:

Process[] myProcesses = Process.GetProcesses();

foreach(Process p in myProcesses)
{
string user = p.StartInfo.EnvironmentVariables["username"];
}

But when I try to run exactly the same code within a
WindowService as System, the property "username" doesn't
seem to exist anymore as an option
in the EnvironmentVariables!

Can anyone explain why it's so, and hopefully show how I can get this
information in some other way?
This is because you inherit your environment variables
from your parent process. A "normal application" is one
launched by Explorer.exe, which adds
a bunch of environment variables.

Services aren't launched by Explorer, and don't have
the same environment variables.

I should've been more clear in my question.

If you look again at the code example, you'll see that I'm not looking after
the username for the "current" process, but for who have started the other
processes on the machine.

[ System.Diagnostics.Process.GetProcesses() ]

Since *they* are launched by Explorer.exe, shouldn't they still have the
same environment variables, regardless of who's asking?

So my questions remain, though perhaps rephrased:

Why do the EnvironmentVariables for the *same* process differ depending on
if I ask for them from a "normal" application or from a service?

How can I fetch the username for another process from within a service?

// Bjorn A
 
W

Willy Denoyette [MVP]

If you run this code from your interactive logon session shell
(explorer.exe), EnvironmentVariables["username"] will return the value of
the INTERACTIVE "Username", from the session's shell environment block for
all processes ran from this shell (parent process environment block is
inheritted).
If you run this from a service, the environment value 'Username' doesn't
exists, because services do not have an associated interactive logon
session.

Willy.

Bjorn Abelli said:
When I run a normal application I can get who
started a process with the
following example:

Process[] myProcesses = Process.GetProcesses();

foreach(Process p in myProcesses)
{
string user = p.StartInfo.EnvironmentVariables["username"];
}

But when I try to run exactly the same code within a
WindowService as System, the property "username" doesn't
seem to exist anymore as an option
in the EnvironmentVariables!

Can anyone explain why it's so, and hopefully show how I can get this
information in some other way?
This is because you inherit your environment variables
from your parent process. A "normal application" is one
launched by Explorer.exe, which adds
a bunch of environment variables.

Services aren't launched by Explorer, and don't have
the same environment variables.

I should've been more clear in my question.

If you look again at the code example, you'll see that I'm not looking after
the username for the "current" process, but for who have started the other
processes on the machine.

[ System.Diagnostics.Process.GetProcesses() ]

Since *they* are launched by Explorer.exe, shouldn't they still have the
same environment variables, regardless of who's asking?

So my questions remain, though perhaps rephrased:

Why do the EnvironmentVariables for the *same* process differ depending on
if I ask for them from a "normal" application or from a service?

How can I fetch the username for another process from within a service?

// Bjorn A
 
D

David Browne

Bjorn Abelli said:
When I run a normal application I can get who
started a process with the
following example:

Process[] myProcesses = Process.GetProcesses();

foreach(Process p in myProcesses)
{
string user = p.StartInfo.EnvironmentVariables["username"];
}

But when I try to run exactly the same code within a
WindowService as System, the property "username" doesn't
seem to exist anymore as an option
in the EnvironmentVariables!

Can anyone explain why it's so, and hopefully show how I can get this
information in some other way?
This is because you inherit your environment variables
from your parent process. A "normal application" is one
launched by Explorer.exe, which adds
a bunch of environment variables.

Services aren't launched by Explorer, and don't have
the same environment variables.

I should've been more clear in my question.

If you look again at the code example, you'll see that I'm not looking after
the username for the "current" process, but for who have started the other
processes on the machine.

[ System.Diagnostics.Process.GetProcesses() ]

Since *they* are launched by Explorer.exe, shouldn't they still have the
same environment variables, regardless of who's asking?

So my questions remain, though perhaps rephrased:

Why do the EnvironmentVariables for the *same* process differ depending on
if I ask for them from a "normal" application or from a service?

Ok. The BCL is confusing here. Process.StartInfo.EnvironmentVariables
always retrieves the environment for the current process. Period. It's
really intended for starting new processes, not examining existing
processes.

So the reason your service doesn't see that environment variable is that you
are always getting the environment of the current service process, which
doesn't have a username variable.

To read the environment of another process, you must read the VM of the
process. This is possible with Win32's ReadProcessMemory and appropriate
permissions, but the System.Diagnostics.Process type doesn't do it.


David
 
B

Bjorn Abelli

"Willy Denoyette [MVP]" ...
If you run this code from your interactive logon session
shell (explorer.exe), EnvironmentVariables["username"]
will return the value of the INTERACTIVE "Username",
from the session's shell environment block for all
processes ran from this shell (parent process environment
block is inheritted).

Ok, so in fact the environment variables I can get hold on *is* depending on
who is asking? That's to say that I can't check for other environment
variables than I can about my own process, *even* if the process in question
*is* started in an interactive session?

using System.Diagnostics;

...

Process[] myProcesses = Process.GetProcesses();


foreach(Process p in myProcesses)
{
// This always returns null when run from within
// a service, *even* if the process p *is* started
// from an interactive session...

string user = p.StartInfo.EnvironmentVariables["username"];
}
If you run this from a service, the environment value
'Username' doesn't exists, because services do not have
an associated interactive logon session.

Okey, I'll try to buy that even though I find it peculiar since there are
processes started by a user with a username...

But it still leaves my second question unanswered.

- How can I fetch the username for another
process from within a service?

Anyone?


// Bjorn A
 
W

Willy Denoyette [MVP]

- How can I fetch the username for another
process from within a service?

Using PInvoke, but then you will have some security issues to solve.
If you tell me on what OS you tend to run this I can give you some more
details how to solve these.

Willy.
 
B

Bjorn Abelli

"Willy Denoyette [MVP]" ...
Using PInvoke, but then you will have some security
issues to solve. If you tell me on what OS you tend
to run this I can give you some more
details how to solve these.

I'd be very grateful for that. :)

The OS is Windows XP (both Home and Pro).

// Bjorn A
 
W

Willy Denoyette [MVP]

Bjorn Abelli said:
"Willy Denoyette [MVP]" ...

I'd be very grateful for that. :)

The OS is Windows XP (both Home and Pro).

// Bjorn A

Bjorn,

Following two console samples:
- [1] uses the WIN32 API OpenProcessToken, but has problems to retrieve the
process identity for some processes (due to a lack of privileges).
[1]
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Security.Principal;
using System.Diagnostics;
using System.Security;
// Problem: proc.Handle property returns Access denied for 'idle' process
and,
// Cannot OpenProcessToken() for NT AUTHORITY\NETWORK SERVICE and NT
AUTHORITY\LOCAL SYSTEM
// Better use - WTSEnumerateProcesses on XP and higher
//

using HANDLE = System.IntPtr;
class IdentUser {

[DllImport("advapi32", SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern int OpenProcessToken(
HANDLE ProcessHandle, // handle to process
int DesiredAccess, // desired access to process
ref IntPtr TokenHandle // handle to open access token
);

[DllImport("kernel32", SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern bool CloseHandle(HANDLE handle);

public const int TOKEN_QUERY = 0X00000008;


public static void Main() {

Process[] _process = Process.GetProcesses();
foreach(Process proc in _process)
{
try {
Console.WriteLine("Process Name :{0} \tProcess ID : {1} ",
proc.ProcessName, proc.Id);

DumpPrincipalName(proc.Handle);
Console.WriteLine("--------------------------------------------------");
}
catch(Exception ex)
{Console.WriteLine("Exception: {0}", ex.Message);}
}
}

static void DumpPrincipalName(HANDLE processHandle)
{
int access = TOKEN_QUERY;
HANDLE tokenHandle = IntPtr.Zero;
if ( 0 != OpenProcessToken( processHandle, access, ref tokenHandle ) )
{
WindowsIdentity wi = new WindowsIdentity(tokenHandle);
Console.WriteLine(wi.Name);
CloseHandle(tokenHandle); // Close process token
}
else
Console.WriteLine("Error OpenProcessToken:
{0}",Marshal.GetLastWin32Error());
}

}


- [2] uses the WTS API's which has not the same prblems as [1], but requires
XP or higher.

[2]

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Security;
// Using Wtsapi32 (available on Windows.NET and XP)
// Get Process information using Terminal server API's
// Requirements: TCB privilege???
using HANDLE = System.IntPtr;
class ProcIdent {

[DllImport("wtsapi32", CharSet=CharSet.Auto, SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern bool WTSEnumerateProcesses (
HANDLE ProcessHandle, // handle to process (from WTSOpenServer)
int Reserved, // dmust be 0
uint Version, // must be 1
ref IntPtr ppProcessInfo, // pointer to pointer to Processinfo
ref uint pCount // no. of processes
);

[DllImport("wtsapi32", SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern IntPtr WTSOpenServer (
string ServerName // Server name (NETBIOS)
);

[DllImport("wtsapi32", SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern void WTSCloseServer (
HANDLE hServer // Handle obtained by WTSOpenServer
);

[DllImport("wtsapi32", SetLastError=true),
SuppressUnmanagedCodeSecurityAttribute]
static extern void WTSFreeMemory (
IntPtr pMemory );

[StructLayout(LayoutKind.Sequential)]
struct WTSProcessInfo {
internal uint SessionId;
internal uint ProcessId;
[MarshalAs(UnmanagedType.LPTStr)] internal string pProcessName;
internal IntPtr pUserSid;
}
[DllImport("advapi32", CharSet=CharSet.Auto)]
static extern bool ConvertSidToStringSid(
IntPtr pSID,
[In,Out,MarshalAs(UnmanagedType.LPTStr)] ref string pStringSid);

[DllImport("advapi32", CharSet=CharSet.Auto, SetLastError=true)]
static extern bool LookupAccountSid
(
string lpSystemName, // name of local or remote computer
IntPtr pSid, // security identifier
StringBuilder Account, // account name buffer
ref int cbName, // size of account name buffer
StringBuilder DomainName, // domain name
ref int cbDomainName, // size of domain name buffer
ref int peUse // SID type
);


const int ERROR_NO_MORE_ITEMS = 259;

public static void Main() {
HANDLE hServer = IntPtr.Zero;
IntPtr pInfo = IntPtr.Zero;
IntPtr pInfoSave = IntPtr.Zero;
WTSProcessInfo WTSpi; // Reference to ProcessInfo struct
uint count = 0;
int iPtr = 0;
try {
hServer = WTSOpenServer(System.Environment.MachineName);
if(hServer == IntPtr.Zero)
Console.WriteLine(Marshal.GetLastWin32Error());
if (WTSEnumerateProcesses(hServer, 0, 1, ref pInfo, ref count))
{
pInfoSave = pInfo;
Console.WriteLine("Number of processes :{0}", count);
for(int n = 0; n < count; n++) {
WTSpi = (WTSProcessInfo) Marshal.PtrToStructure(pInfo,
typeof(WTSProcessInfo) );
iPtr = (int)(pInfo) + Marshal.SizeOf(WTSpi);
pInfo = (IntPtr)(iPtr);
if(WTSpi.ProcessId != 0)
Console.WriteLine(WTSpi.pProcessName + "\t" + WTSpi.ProcessId + "\t" +
DumpAccountFromSid(WTSpi.pUserSid));
else
Console.WriteLine("Idle pseudo proc");
}
}
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
// Free memory used by WTSEnumerateProcesses
WTSFreeMemory(pInfoSave);
// Close server handle
WTSCloseServer(hServer);
}
}

static string DumpAccountFromSid(IntPtr SID)
{
int cchAccount = 0;
int cchDomain = 0;
int snu = 0 ;
StringBuilder sb = new StringBuilder();

// Caller allocated buffer
StringBuilder Account= null;
StringBuilder Domain = null;
// Lookup account name on local system
// First call to get required buffer sizes
bool ret = LookupAccountSid(null, SID, Account, ref cchAccount, Domain,
ref cchDomain, ref snu);
if ( ret == true )
if ( Marshal.GetLastWin32Error() == ERROR_NO_MORE_ITEMS )
return "Error";
Account = new StringBuilder( cchAccount );
Domain = new StringBuilder( cchDomain );
ret = LookupAccountSid(null, SID, Account, ref cchAccount, Domain, ref
cchDomain, ref snu);
if (ret)
{
sb.Append(Domain);
sb.Append(@"\");
sb.Append(Account);
}
else
sb.Append("Win32 Error: " + Marshal.GetLastWin32Error());
return sb.ToString();
}
}

Hope this helps.

Willy.
 
B

Bjorn Abelli

"Willy Denoyette [MVP]" ...
- [2] uses the WTS API's which has not
the same prblems as [1], but requires
XP or higher.

Excellent!!!

It works like a charm!

Thank you very, very much!

// Bjorn A
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top