0

I'm developing a simple Background Worker service to run as a Windows service following this example.

The service will periodically log if certain applications are actively running on the users machines. Collected info includes: Machine Name, User Name, App Name, App Version, Is App Currently Focused?.

When I run the worker service's exe directly from windows, everything works fine; but when I create a service using the same exe, Environment.UserName is returning "System" and the user32.dll's GetForegroundWindow() function is not finding the active window (returning 0).

I learned that this is because the windows service is run under a different user (System instead of the logged-in user)

I was able to solve the UserName problem by invoking a PowerShell cmdlet from within the service which grabbed the correct user name, but I don't seem to be able to do the same for GetForegroundWindow().

Here are the methods I'm using to get the user name and check if the grabbed process's window is the foreground window (inspired by this answer):

private static StringBuilder _outStream = new StringBuilder();
private static StringBuilder _errStream = new StringBuilder();
//Using the CliWrap library to invoke command line commands 
private static Command GetPSCommand()
        {
            _outStream.Clear();
            _errStream.Clear();
            var c = Cli.Wrap("PowerShell.exe")
                .WithStandardOutputPipe(PipeTarget.ToStringBuilder(_outStream))
                .WithStandardErrorPipe(PipeTarget.ToStringBuilder(_errStream))
                .WithValidation(CommandResultValidation.ZeroExitCode);
            return c;
        }

        public static async Task<string> GetUserNameAsync()
        {
            var pscmd = @"(Get-CimInstance -ClassName Win32_ComputerSystem).Username";
    
            var cmd = GetPSCommand().WithArguments(pscmd);

            await cmd.ExecuteAsync();

            if (_errStream.Length > 0)
                Log.Error(_errStream.ToString());

            var username = _outStream.ToString();
            return string.IsNullOrEmpty(username) ? "NotFound" : username.Split('\\').LastOrDefault()!;
        }

        
public static bool IsInFocus(Process proc)
        {
            var activatedHandle = GetForegroundWindow();

            if (activatedHandle == IntPtr.Zero)
            {
                return false;       // No window is currently activated
            }

            var procId = proc.Id;
            GetWindowThreadProcessId(activatedHandle, out var activeProcId);

            return activeProcId == procId;
        }

I am creating the service using this code snippet:

var cmd = Cli.Wrap("sc.exe")
        .WithStandardOutputPipe(PipeTarget.ToDelegate(Log.Debug))
        .WithStandardErrorPipe(PipeTarget.ToDelegate(Log.Error))
        .WithValidation(CommandResultValidation.ZeroExitCode)
    ;

var installCmd = cmd.WithArguments(new[]
                { "create",
                    ServiceName,
                    $"binPath={executablePath}",
                    "start=auto",
                }
);

I also tried to create the service using obj= MyUserName and password=MyPassword to see if that would change anything, but couldn't get it to work (err: User does not exist or wrong password)

Any hints are greatly appreciated!

Samer
  • 11
  • 1
  • 6

0 Answers0