b.0x449
@0x44449

현재 로그인된 윈도우 계정 정보가 필요한 경우가 종종 있습니다.

WindowsIdentity.GetCurrent 함수를 사용할 수 있으나 해당 함수는 윈도우의 계정이 아닌 현재 프로세스의 계정을 반환합니다.
이는 UAC가 켜져있고 사용자 계정에 관리자 권한이 없는 경우 문제가 발생할 수 있습니다.
UAC가 동작하면서 관리자 계정으로 프로세스가 수행되기 때문입니다.

WMI를 이용하여 현재 계정을 가져오는 방법이 있으나 원격으로 연결되어 있는 환경에서는 WMI로 현재 계정을 가져오면 빈값이 반환됩니다.
Explorer.exe 프로세스의 계정을 사용하는 방법도 있으나 원격으로 여러 클라이언트가 연결되어 있으면 Explorer.exe 프로세스가 연결된 세션만큼 존재합니다.

레지스트리를 확인해보면 LOCAL_MACHINE 아래 현재 윈도우 세션에 대한 정보가 저장되어 있습니다.
현재 활성화된 세션 ID만 알면 세션의 계정 정보 또한 확인이 가능합니다.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData

WTSQuerySessionInformation 함수를 이용하여 현재 세션의 ID를 가져올 수 있습니다.

하지만 문제가 하나 있는데, 현재 어플리케이션이 윈도우 서비스 프로그램이라면 현재 세션은 0을 반환합니다.
서비스 프로그램은 0번 세션에 격리되어 구동되기 때문입니다.

이런 경우에는 현재 윈도우의 세션을 순회하면서 활성화되어 있는 세션을 찾아야 합니다.
WTSQuerySessionInformation 함수를 사용하지 않고 세션 순회로만 찾아도 큰 문제는 없을 것 같습니다.

WinAPI는 다음 내용을 사용합니다.

[DllImport("Wtsapi32.dll")]
public static extern bool WTSQuerySessionInformation(IntPtr hServer,
                                                     int sessionId,
                                                     WTSInfoClass wtsInfoClass,
                                                     out IntPtr ppBuffer,
                                                     out uint pBytesReturned);

public enum WTSInfoClass
{
    WTSInitialProgram,
    WTSApplicationName,
    WTSWorkingDirectory,
    WTSOEMId,
    WTSSessionId,
    WTSUserName,
    WTSWinStationName,
    WTSDomainName,
    WTSConnectState,
    WTSClientBuildNumber,
    WTSClientName,
    WTSClientDirectory,
    WTSClientProductId,
    WTSClientHardwareId,
    WTSClientAddress,
    WTSClientDisplay,
    WTSClientProtocolType,
    WTSIdleTime,
    WTSLogonTime,
    WTSIncomingBytes,
    WTSOutgoingBytes,
    WTSIncomingFrames,
    WTSOutgoingFrames,
    WTSClientInfo,
    WTSSessionInfo
}

[DllImport("wtsapi32.dll", ExactSpelling = true, SetLastError = false)]
public static extern void WTSFreeMemory(IntPtr memory);

public const int WTS_CURRENT_SESSION = -1;

[DllImport("wtsapi32.dll", SetLastError = true)]
public static extern Int32 WTSEnumerateSessions(
IntPtr hServer,
[MarshalAs(UnmanagedType.U4)] Int32 Reserved,
[MarshalAs(UnmanagedType.U4)] Int32 Version,
ref IntPtr ppSessionInfo,
[MarshalAs(UnmanagedType.U4)] ref Int32 pCount);

[StructLayout(LayoutKind.Sequential)]
private struct WTS_SESSION_INFO
{
    public Int32 SessionID;

    [MarshalAs(UnmanagedType.LPStr)]
    public String pWinStationName;

    public WTS_CONNECTSTATE_CLASS State;
}

public enum WTS_CONNECTSTATE_CLASS
{
    WTSActive,
    WTSConnected,
    WTSConnectQuery,
    WTSShadow,
    WTSDisconnected,
    WTSIdle,
    WTSListen,
    WTSReset,
    WTSDown,
    WTSInit
}

계정 정보를 찾는 것은 다음과 같이 찾아옵니다.

public static string GetCurrentUserName()
{
    IntPtr buffer = IntPtr.Zero;
    uint bytesReturned;
    int sessionID = 0;

    try
    {
        bool sessionInfo = WTSQuerySessionInformation(IntPtr.Zero, WTS_CURRENT_SESSION, WTSInfoClass.WTSSessionId, out buffer, out bytesReturned);
        sessionID = Marshal.ReadInt32(buffer);
    }
    catch
    {
    }
    finally
    {
        WTSFreeMemory(buffer);
        buffer = IntPtr.Zero;
    }

    if (sessionID == 0)
    {
        sessionID = GetActiveSessionID();
    }

    string key = @"Software\Microsoft\Windows\CurrentVersion\Authentication\LogonUI\SessionData\";
    key += sessionID.ToString();
    string samUserValueName = "LoggedOnSAMUser";

    RegistryKey regKey = null;
    if (Environment.Is64BitOperatingSystem)
    {
        regKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
    }
    else
    {
        regKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
    }
    using (var kk = regKey.OpenSubKey(key))
    {
        var samUser = kk.GetValue(samUserValueName).ToString();
        return samUser;
    }
}

private static int GetActiveSessionID()
{
    int sessionId = 0;

    try
    {
        List<String> ret = new List<string>();

        IntPtr ppSessionInfo = IntPtr.Zero;

        Int32 count = 0;
        Int32 retval = WTSEnumerateSessions(IntPtr.Zero, 0, 1, ref ppSessionInfo, ref count);
        Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));

        Int64 current = (int)ppSessionInfo;

        if (retval != 0)
        {
            for (int i = 0; i < count; i++)
            {
                WTS_SESSION_INFO si = (WTS_SESSION_INFO)Marshal.PtrToStructure((System.IntPtr)current, typeof(WTS_SESSION_INFO));
                current += dataSize;

                if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive)
                {
                    sessionId = si.SessionID;
                    break;
                }
            }

            WTSFreeMemory(ppSessionInfo);
        }
    }
    finally
    {
    }

    return sessionId;
}