Как определить текущего пользователя Windows из службы Windows?

Я пишу службу Windows с использованием С#.NET 2005. Как определить, кем является текущий пользователь (если есть)? Также есть ли способ уведомления при входе пользователя в систему?

Альтернативно, есть ли способ узнать, кто недавно использовал машину?

Мне нужно знать текущего зарегистрированного пользователя, чтобы я мог кэшировать некоторые данные для этого пользователя. Работая в корпоративной среде, есть тысячи потенциальных пользователей, но имеет смысл кэшировать данные для тех, кто использует эту машину.

ОБНОВЛЕНИЕ:

Это решение работает хорошо. Также см. этот пример pinvoke.net, который использует расширенную структуру для получения имени домена.

В сочетании с этим я использую класс SystemEvents для уведомления, когда пользователь входит в систему на машине. См. пример 2 здесь для хорошего примера - обратите внимание, что вам нужно использовать скрытую форму из службы, чтобы иметь возможность использовать SystemEvents из службы.

Ответ 1

Вы можете использовать P/Invoke для вызова NetWkstaUserEnum, в котором перечислены текущие пользователи. Имейте в виду, что может быть несколько пользователей, если есть сеансы сервера терминалов, и что не все пользователи возвращаются, это "настоящий" пользователь. Как указано в документации:

"Этот список включает интерактивные, сервис и пакетные входы".

Вот пример рабочего кода в С# о вызове NetWkstaUserEnum:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace EnumerateUsers
{
    class Program
    {
        static void Main(string[] args)
        {
            var ue = new UserEnumerator();
            foreach(string userName in ue.GetLoggedOnUsers(null))
            {
                Console.WriteLine(userName);
            }
        }

    }

    class UserEnumerator
    {
        public IEnumerable<string> GetLoggedOnUsers(string host)
        {
            int entriesRead, totalEntries, resumeHandle = 0;
            IntPtr pBuffer = IntPtr.Zero;
            try
            {
                int result = NetWkstaUserEnum(host, 0, out pBuffer, MAX_PREFERRED_LENGTH, out entriesRead, out totalEntries, ref resumeHandle);
                if (result != NERR_Success)  
                    throw new ApplicationException(String.Format("Failed to enumerate users, error code {0}", result));

                return GetUsersFromStruct(pBuffer, entriesRead).ToList();
            }
            finally
            {
                if (pBuffer != IntPtr.Zero)
                    NetApiBufferFree(pBuffer);
                }

        }

        private IEnumerable<string> GetUsersFromStruct(IntPtr pBuffer, int count)
        {
            for (int i = 0; i < count; i++)
            {
                var user = (WKSTA_USER_INFO_0)Marshal.PtrToStructure(pBuffer, typeof(WKSTA_USER_INFO_0));
                yield return user.username;
                pBuffer = IntPtr.Add(pBuffer, user.username.Length * 2);                
            }
        }
        [DllImport("netapi32.dll")]
        private static extern int NetWkstaUserEnum(string host, int level, out IntPtr pBuffer, int prefMaxLength, out int entriesRead,
                                     out int totalEntries, ref int resumeHandle);

        [DllImport("netapi32.dll")]
        private static extern int NetApiBufferFree(IntPtr buffer);

        private const int MAX_PREFERRED_LENGTH = -1;

        private const int NERR_Success = 0;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct WKSTA_USER_INFO_0
    { 
        [MarshalAs(UnmanagedType.LPTStr)]
        internal string username;
    }
}

Ответ 2

Как вы знаете, в службе Windows может не быть зарегистрированного пользователя в настоящий момент. Почему бы не добавить небольшую служебную программу для процесса входа в систему на машине, которая будет запускаться всякий раз, когда кто-то войдет в систему, что будет выполнять метод, который вызывает функции кэширования данных в службе. Таким образом, эта утилита будет иметь доступ к вошедший в систему пользователя.