2011-05-19 110 views
2

我需要获取唯一标识当前Windows用户的登录会话的值。这是一个winforms应用程序,而不是ASP.NET。我将从多个进程中检索它,以便在同一个登录会话中检索时需要返回相同的值。在所有用户会话期间它只需要在当前机器上唯一。直到机器下次重新启动。我觉得Windows Logon Id是正确的,但似乎有点痛苦检索。还有什么或更简单的方法来获得这个吗?如何在Windows中为当前用户的登录会话获取唯一标识 - c#

我将使用ID包含在命名管道服务的地址中,以在计算机上运行的两个进程之间进行通信。我想包含登录ID以避免在有多个用户登录时发生冲突,包括可能是同一用户的多个会话。

+0

如果用户注销并重新登录,是否可以接受相同的ID? – Gabe 2011-05-19 16:56:31

+0

不,这应该是另一个ID,因为它是另一个会话。 – Rory 2011-05-19 21:13:09

回答

3

据我了解,你需要的是这样的:

SID:S-1- 5-5-XY 名称:登录会话 描述:登录会话。这些SID的X和Y值对于每个会话都不相同。

在Windows操作系统中的已知安全标识符 http://support.microsoft.com/kb/243330

有人问类似的东西在这里:

How to get the logon SID in C#

他们有一个很好的答案,但我想添加自己的

这是我的解决方案:

using System; 
using System.Windows.Forms; 
using System.Runtime.InteropServices; 


namespace TestLogonSid 
{ 
    public partial class Form1 : Form 
    { 

     private delegate bool EnumDesktopProc(string lpszDesktop, IntPtr lParam); 

     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void button1_Click_1(object sender, EventArgs e) 
     { 

      this.textBox1.Text = GetLogonSid.getLogonSid(); 
     } 


    } 

    public class GetLogonSid 
    { 
     //The SID structure that identifies the user that is currently associated with the specified object. 
     //If no user is associated with the object, the value returned in the buffer pointed to by lpnLengthNeeded is zero. 
     //Note that SID is a variable length structure. 
     //You will usually make a call to GetUserObjectInformation to determine the length of the SID before retrieving its value. 
     private const int UOI_USER_SID = 4; 

     //GetUserObjectInformation function 
     //Retrieves information about the specified window station or desktop object. 
     [DllImport("user32.dll")] 
     static extern bool GetUserObjectInformation(IntPtr hObj, int nIndex, [MarshalAs(UnmanagedType.LPArray)] byte[] pvInfo, int nLength, out uint lpnLengthNeeded); 


     //GetThreadDesktop function 
     //Retrieves a handle to the desktop assigned to the specified thread. 
     [DllImport("user32.dll")] 
     private static extern IntPtr GetThreadDesktop(int dwThreadId); 


     //GetCurrentThreadId function 
     //Retrieves the thread identifier of the calling thread. 
     [DllImport("kernel32.dll")] 
     public static extern int GetCurrentThreadId(); 

     //ConvertSidToStringSid function 
     //The ConvertSidToStringSid function converts a security identifier (SID) to a string format suitable for display, storage, or transmission. 
     //To convert the string-format SID back to a valid, functional SID, call the ConvertStringSidToSid function. 

     [DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)] 
     static extern bool ConvertSidToStringSid(
      [MarshalAs(UnmanagedType.LPArray)] byte[] pSID, 
      out IntPtr ptrSid); 


     /// <summary> 
     /// The getLogonSid function returns the Logon Session string 
     /// </summary> 
     /// <returns></returns> 
     public static string getLogonSid() 
     { 
      string sidString = ""; 
      IntPtr hdesk = GetThreadDesktop(GetCurrentThreadId()); 
      byte[] buf = new byte[100]; 
      uint lengthNeeded; 
      GetUserObjectInformation(hdesk, UOI_USER_SID, buf, 100, out lengthNeeded); 
      IntPtr ptrSid; 
      if (!ConvertSidToStringSid(buf, out ptrSid)) 
       throw new System.ComponentModel.Win32Exception(); 
      try 
      { 
       sidString = Marshal.PtrToStringAuto(ptrSid); 
      } 
      catch 
      { 
      } 
      return sidString; 
     } 

    } 
} 
+0

谢谢,这看起来不错。 – Rory 2015-03-02 09:25:05

-2

如果我理解正确,你就可以生成一个GUID

+0

对不起,我没有清楚说明从多个进程中检索时需要保持一致。即需要识别登录会话,而不仅仅是唯一的 – Rory 2011-05-19 15:40:36

0

您可以尝试Environment.UserDomainName & Environment.UserName

为了保证每个用户会话一个唯一的ID我想你将不得不使用的方法痛苦你提到 - How to get the logon SID in C#

+0

如果用户登录两次,该怎么办? – Gabe 2011-05-19 15:37:10

+0

@加贝 - 好点。更新。 – YetAnotherUser 2011-05-19 15:44:20

+0

同一个用户可以使用相同的凭证登录到两台机器吗?我一直认为你会被重定向回你现有的会话。需要测试我猜。 :) – 2011-05-19 16:12:44

8

获得会话ID最简单的方法是看Process.SessionId属性:

System.Diagnostics.Process.GetCurrentProcess().SessionId 

值是由GetTokenInformation(...,TokenSessionId,...)返回的相同。

注意:你应该记住的一件事是会话ID不是Logon Id。 例如,在同一个会话中,在同一用户下启动的Win7提升进程将具有不同的LogonId(共享到非提升的一个),但会具有相同的SessionId。 即使运行RunAs显式指定同一用户的凭据runniung非升级过程将创建新的登录Sesison Id。 这种行为有意义,例如,当您映射网络驱动器时。自Vista以来,即使进程在同一会话和同一用户下运行,使用一个令牌LogonId的进程也不会看到使用另一个LogonId映射的网络目录。

下面是你可以在不同的会话/ creds启动示例应用程序看到的区别:

using System; 
using System.Runtime.InteropServices; 

namespace GetCurrentSessionId 
{ 
    class Program 
    { 

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

     enum TokenType 
     { 
      TokenPrimary = 1, 
      TokenImpersonation 
     } 

     enum SecurityImpersonationLevel 
     { 
      SecurityAnonymous, 
      SecurityIdentification, 
      SecurityImpersonation, 
      SecurityDelegation 
     } 

     [StructLayout(LayoutKind.Sequential)] 
     struct TokenStatistics 
     { 
      public Int64 TokenId; 
      public Int64 AuthenticationId; 
      public Int64 ExpirationTime; 
      public TokenType TokenType; 
      public SecurityImpersonationLevel ImpersonationLevel; 
      public Int32 DynamicCharged; 
      public Int32 DynamicAvailable; 
      public Int32 GroupCount; 
      public Int32 PrivilegeCount; 
      public Int64 ModifiedId; 
     } 

     struct TokenOrigin 
     { 
      public Int64 OriginatingLogonSession; 
     } 

     [DllImport("advapi32.dll", EntryPoint = "GetTokenInformation", SetLastError = true)] 
     static extern bool GetTokenInformation(
      IntPtr tokenHandle, 
      TokenInformationClass tokenInformationClass, 
      IntPtr tokenInformation, 
      int tokenInformationLength, 
      out int ReturnLength); 

     public const int ERROR_INSUFFICIENT_BUFFER = 0x7a; 

     static void Main(string[] args) 
     { 
      try 
      { 
       Console.WriteLine("Session Id: {0}", System.Diagnostics.Process.GetCurrentProcess().SessionId); 

       IntPtr tokenInfo; 
       bool result; 
       int infoSize; 

       IntPtr hToken = System.Security.Principal.WindowsIdentity.GetCurrent().Token; 

       result = GetTokenInformation(hToken, TokenInformationClass.TokenStatistics, IntPtr.Zero, 0, out infoSize); 
       if (!result && Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER) 
       { 
        tokenInfo = Marshal.AllocHGlobal(infoSize); 
        result = GetTokenInformation(hToken, TokenInformationClass.TokenStatistics, tokenInfo, infoSize, out infoSize); 
        if (result) 
        { 
         TokenStatistics tokenStats = (TokenStatistics)Marshal.PtrToStructure(tokenInfo, typeof(TokenStatistics)); 
         Console.WriteLine("LogonId: 0x{0:X16}", tokenStats.AuthenticationId); 
        } 
        else 
        { 
         Console.Error.WriteLine("LogonId: FAILED with 0x{0:X08}", Marshal.GetLastWin32Error()); 
        } 
        Marshal.FreeHGlobal(tokenInfo); 
       } 
       else 
       { 
        Console.Error.WriteLine("LogonId: FAILED with 0x{0:X08}", Marshal.GetLastWin32Error()); 
       } 


       tokenInfo = Marshal.AllocHGlobal(sizeof (Int32)); 
       result = GetTokenInformation(hToken, TokenInformationClass.TokenSessionId, tokenInfo, sizeof (Int32), out infoSize); 
       if (result) 
       { 
        int tokenSessionId = Marshal.ReadInt32(tokenInfo); 
        Console.WriteLine("TokenSessionId: {0}", tokenSessionId); 
       } 
       else 
       { 
        Console.Error.WriteLine("TokenSessionId: FAILED with 0x{0:X08}", Marshal.GetLastWin32Error()); 
       } 

       Marshal.FreeHGlobal(tokenInfo); 


       result = GetTokenInformation(hToken, TokenInformationClass.TokenOrigin, IntPtr.Zero, 0, out infoSize); 
       if (!result && Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER) 
       { 
        tokenInfo = Marshal.AllocHGlobal(infoSize); 
        result = GetTokenInformation(hToken, TokenInformationClass.TokenOrigin, tokenInfo, infoSize, out infoSize); 
        if (result) 
        { 
         TokenOrigin tokenOrigin = (TokenOrigin) Marshal.PtrToStructure(tokenInfo, typeof (TokenOrigin)); 
         Console.WriteLine("OriginatingSessionId: 0x{0:X16}", tokenOrigin.OriginatingLogonSession); 
        } 
        else 
        { 
         Console.WriteLine("OriginatingSessionId: FAILED with 0x{0:X08}", Marshal.GetLastWin32Error()); 
        } 
        Marshal.FreeHGlobal(tokenInfo); 
       } 
       else 
       { 
        Console.WriteLine("OriginatingSessionId: FAILED with 0x{0:X08}", Marshal.GetLastWin32Error()); 
       } 

       Console.WriteLine("Press any key..."); 
       Console.ReadKey(); 

      } 
      catch (Exception ex) 
      { 
       Console.Error.WriteLine("Unexpected error: {0}", ex); 
       Console.ReadKey(); 
      } 
     } 
    } 
} 
+1

感谢这个有用的代码。请注意,OriginatingSessionId不是唯一的。当用户注销并再次登录时,此值保持不变。 – Elmue 2014-09-10 19:08:31

+0

Yup会话ID不是唯一的,不知道为什么这是被接受的答案 – paulm 2015-02-17 08:06:58

+0

@paulm,该会话在所有打开的会话的特定时刻是唯一的,并且解决了OP的任务:寻址命名管道。当用户注销时,它的进程被破坏,所以它们不会干扰用户的后续会话。但是,如果同一用户同时登录到不同的会话,则结果是唯一的。 – mistika 2015-04-06 18:00:57

相关问题