如何在 Windows - c# 中获取当前用户登录会话的唯一 ID

本文关键字:登录 用户 会话 ID 唯一 获取 Windows | 更新日期: 2023-09-27 17:57:16

我需要获取一个唯一标识当前 Windows 用户登录会话的值。这是针对 winforms 应用程序,而不是 ASP.NET。我将从多个进程中检索它,因此在同一登录会话中检索时需要返回相同的值。它只需要在所有用户会话期间在当前计算机上是唯一的 - 例如,直到计算机下次重新启动。

我认为Windows登录ID是正确的,但检索起来似乎有点痛苦。还有其他方法或更简单的方法吗?

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

如何在 Windows - c# 中获取当前用户登录会话的唯一 ID

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

System.Diagnostics.Process.GetCurrentProcess().SessionId

该值与 GetTokenInformation(...,TokenSessionId,...) 返回的值相同。

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

下面是您可以在不同会话/信誉上启动的示例应用程序,以查看差异:

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

据我了解,您需要的是:

SID: S-1-5-5-X-Y名称:登录会话说明:登录会话。这些 SID 的 X 和 Y 值对于每个会话都是不同的。

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

有人在这里要求类似的东西:

如何在 C# 中获取登录 SID

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

这是我的解决方案:

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;
        }
    }
}
你可以

试试Environment.UserDomainNameEnvironment.UserName

为了确保每个用户会话的唯一ID,我想您必须使用您提到的痛苦方法 - 如何在C#中获取登录SID。

如果我理解正确,您可以生成 GUID