2011-05-19 91 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

相關問題