Получение имени пользователя для входа в систему из службы


У меня есть служба, для установки которой мне пришлось войти в систему локального администратора. Pupose этой службы для входа в систему, когда пользователь входит или выходит, чтобы записать свое имя пользователя. Я наконец нашел немного кода WMI, который, как я думал, будет работать, но он все еще возвращается администратором. Почему это не работает?

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);

    userName = String.Concat(ownerInfo[1], @"", ownerInfo[0]);
}
Console.WriteLine(userName);
Console.ReadLine();

Чтобы прояснить свой вопрос, я пытаюсь получить текущего пользователя, но он возвращает мне Adminstrator учетную запись, которую я использовал для установки сервиса.

4 3

4 ответа:

Для этого следует использовать уведомления диспетчера управления службами. Вы можете настроить службу на получение уведомлений о событиях, когда пользователь входит в систему и / или выходит из нее. Это позволяет службе выполнять интерактивное олицетворение пользователя, если это требуется службе,но она должна предоставить вам информацию, необходимую для ведения журнала.

Ознакомьтесь с разделом "использование уведомлений диспетчера управления службами (SCM)" здесь http://technet.microsoft.com/en-us/library/cc721961 (WS.10).aspx

Править

В классе службы переопределите обработчик событий OnSessionChange для проверки событий входа и выхода из системы.

protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
    base.OnSessionChange(changeDescription);

    switch (changeDescription.Reason)
    {
        case SessionChangeReason.SessionLogon:
            // do your logging here
            break;

        case SessionChangeReason.SessionLogoff:
            // do your logging here
            break;
    }
}

Edit2:

class Class1
{
    [DllImport("Advapi32.dll")]
    static extern bool GetUserName(StringBuilder lpBuffer, ref int nSize);    
    [STAThread]
    static void Main(string[] args)
    {
        StringBuilder Buffer = new StringBuilder(64);
        int nSize=64;
        GetUserName(Buffer, ref nSize);
        Console.WriteLine(Buffer.ToString());
    }
}

Edit3:

public class InteractiveUser
{
    [DllImport("wtsapi32.dll", SetLastError = true)]
    static extern bool WTSQueryUserToken(UInt32 sessionId, out IntPtr Token);

    [DllImport("kernel32.dll")]
    private static extern UInt32 WTSGetActiveConsoleSessionId();

    enum TOKEN_INFORMATION_CLASS
    {
        TokenUser = 1,
        TokenGroups,
        TokenPrivileges,
        TokenOwner,
        TokenPrimaryGroup,
        TokenDefaultDacl,
        TokenSource,
        TokenType,
        TokenImpersonationLevel,
        TokenStatistics,
        TokenRestrictedSids,
        TokenSessionId,
        TokenGroupsAndPrivileges,
        TokenSessionReference,
        TokenSandBoxInert,
        TokenAuditPolicy,
        TokenOrigin
    }

    public struct TOKEN_USER 
    { 
        public SID_AND_ATTRIBUTES User; 
    } 

    [StructLayout(LayoutKind.Sequential)]
    public struct SID_AND_ATTRIBUTES 
    { 
        public IntPtr Sid; 
        public int Attributes; 
    } 

    // Using IntPtr for pSID insted of Byte[]
    [DllImport("advapi32", CharSet=CharSet.Auto, SetLastError=true)]
    static extern bool ConvertSidToStringSid(
        IntPtr pSID, 
        out IntPtr ptrSid);

    [DllImport("kernel32.dll")]
    static extern IntPtr LocalFree(IntPtr hMem);

    [DllImport("advapi32.dll", SetLastError=true)]
    static extern bool GetTokenInformation(
        IntPtr TokenHandle,
        TOKEN_INFORMATION_CLASS TokenInformationClass,
        IntPtr TokenInformation,
        int TokenInformationLength,
        out int ReturnLength);

    private static string GetSID(IntPtr token)
    {
        bool Result;

        int TokenInfLength = 0; 
        string sidAsString = String.Empty;

        // first call gets lenght of TokenInformation
        Result = GetTokenInformation( token , TOKEN_INFORMATION_CLASS.TokenUser , IntPtr.Zero , TokenInfLength , out TokenInfLength ); 

        IntPtr TokenInformation = Marshal.AllocHGlobal( TokenInfLength ) ; 
        Result = GetTokenInformation( token  , TOKEN_INFORMATION_CLASS.TokenUser , TokenInformation , TokenInfLength , out TokenInfLength ) ; 

        if ( Result ) 
        {
            TOKEN_USER TokenUser = ( TOKEN_USER )Marshal.PtrToStructure( TokenInformation , typeof( TOKEN_USER ) ) ; 

            IntPtr pstr = IntPtr.Zero; 
            Boolean ok = ConvertSidToStringSid( TokenUser.User.Sid  , out pstr ); 

            sidAsString = Marshal.PtrToStringAuto( pstr ); 
            LocalFree(pstr);
        }

        Marshal.FreeHGlobal( TokenInformation );

        return sidAsString;
    }

    public static string Account()
    {
        IntPtr token = IntPtr.Zero;
        String account = String.Empty;

        if (WTSQueryUserToken(WTSGetActiveConsoleSessionId(), out token))
        {
            String sid = GetSID(token);
            account =
                new SecurityIdentifier(sid).Translate(typeof(NTAccount)).ToString();
        }
        else
        {
            int err = Marshal.GetLastWin32Error();
            switch (err)
            {
                case 5:
                    account = "ERROR_ACCESS_DENIED";
                    break;
                case 87:
                    account = "ERROR_INVALID_PARAMETER";
                    break;
                case 1008:
                    account = "ERROR_NO_TOKEN";
                    break;
                case 1314:
                    account = "ERROR_PRIVILEGE_NOT_HELD";
                    break;
                case 7022:
                    account = "ERROR_CTX_WINSTATION_NOT_FOUND";
                    break;
                default:
                    account = String.Format("ERROR_{0}", err.ToString());
                    break;
            }
        }

        return account;
    }
}

Вот мой код (все они находятся внутри класса; в моем случае класс наследует ServiceBase).

    [DllImport("Wtsapi32.dll")]
    private static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WtsInfoClass wtsInfoClass, out IntPtr ppBuffer, out int pBytesReturned);
    [DllImport("Wtsapi32.dll")]
    private static extern void WTSFreeMemory(IntPtr pointer);

    private enum WtsInfoClass
    {
        WTSUserName = 5, 
        WTSDomainName = 7,
    }

    private static string GetUsername(int sessionId, bool prependDomain = true)
    {
        IntPtr buffer;
        int strLen;
        string username = "SYSTEM";
        if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSUserName, out buffer, out strLen) && strLen > 1)
        {
            username = Marshal.PtrToStringAnsi(buffer);
            WTSFreeMemory(buffer);
            if (prependDomain)
            {
                if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSDomainName, out buffer, out strLen) && strLen > 1)
                {
                    username = Marshal.PtrToStringAnsi(buffer) + "\\" + username;
                    WTSFreeMemory(buffer);
                }
            }
        }
        return username;
    }

С приведенным выше кодом в вашем классе вы можете просто получить имя пользователя в переопределяемом методе следующим образом:

protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
    string username = GetUsername(changeDescription.SessionId);
    //continue with any other thing you wish to do
}

Попробуйте изменить метод Account (), вставив параметр sessionId и передав changeDescription.SessionId к методу WTSQueryUserToken

public static string Account(uint sessionId)
{
     IntPtr token = IntPtr.Zero;
     String account = String.Empty;
     if (WTSQueryUserToken(sessionId, out token))
     { 
     ...
     ...

P. s.: запустите службу с учетной записью LocalSystem

Я знаю, что эта нить старая, но если кому-то нужно знать, как она работает:

Добавить ссылку на System.Management

Поместите using System.Management; в начало файла

Создайте эту частную переменную в своем классе:

private readonly ManagementClass _wmiComputerSystem = new ManagementClass("Win32_ComputerSystem");

Создайте этот метод в своем сервисе:

protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
   base.OnSessionChange(changeDescription);

   switch (changeDescription.Reason)
   {
      case SessionChangeReason.SessionLogon:
      {
         string user = "";

         foreach (ManagementObject currentObject in _wmiComputerSystem.GetInstances())
         {
            user = currentObject.Properties["UserName"].Value.ToString().Trim();
         }
      } break;
   }
}

Теперь у вас есть имя пользователя в user. Если компьютер находится в домене, он выглядит следующим образом domain\username