Получение имени пользователя для входа в систему из службы
У меня есть служба, для установки которой мне пришлось войти в систему локального администратора. 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 ответа:
Для этого следует использовать уведомления диспетчера управления службами. Вы можете настроить службу на получение уведомлений о событиях, когда пользователь входит в систему и / или выходит из нее. Это позволяет службе выполнять интерактивное олицетворение пользователя, если это требуется службе,но она должна предоставить вам информацию, необходимую для ведения журнала.
Ознакомьтесь с разделом "использование уведомлений диспетчера управления службами (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