Window Service程序中如何获取登陆用户的信息


    在window form程序中, 获取当前用户的信息是很容易的一件事情。然而,在window service中, 如何获取当前活动的(登陆的)用户的信息,.NET 并没有给出一种很容易的方法。多数情况下,用户需要调用底层API以达到此目的。如果想在services中启动一个线程并且运行在当前活动的用户UI界面上,获取当前活动的用户,更是必须的操作。

    在windows底层的api中,提供了以下的方法来满足需求:

    WTSGetActiveConsoleSessionId: 获取当前active User的 session ID。

    CreateProcessAsUser:用当前用户创建一个新的进程。

    下面的程序提供了两个方法,一是获取当前活动的用户包括域名和用户名,二是用当前用户创建一个新的进程。

View Code
using System;
using System.Security;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace NS_UserTest
{
     ///   <summary>
    
///  Class that allows running applications with full admin rights. In
    
///  addition the application launched will bypass the Vista UAC prompt.
    
///   </summary>
     public  class ApplicationLoader
    {
         #region Structures

        [StructLayout(LayoutKind.Sequential)]
         public  struct SECURITY_ATTRIBUTES
        {
             public  int Length;
             public IntPtr lpSecurityDescriptor;
             public  bool bInheritHandle;
        }

        [StructLayout(LayoutKind.Sequential)]
         public  struct STARTUPINFO
        {
             public  int cb;
             public String lpReserved;
             public String lpDesktop;
             public String lpTitle;
             public  uint dwX;
             public  uint dwY;
             public  uint dwXSize;
             public  uint dwYSize;
             public  uint dwXCountChars;
             public  uint dwYCountChars;
             public  uint dwFillAttribute;
             public  uint dwFlags;
             public  short wShowWindow;
             public  short cbReserved2;
             public IntPtr lpReserved2;
             public IntPtr hStdInput;
             public IntPtr hStdOutput;
             public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
         public  struct PROCESS_INFORMATION
        {
             public IntPtr hProcess;
             public IntPtr hThread;
             public  uint dwProcessId;
             public  uint dwThreadId;
        }

         #endregion

         #region Enumerations

         enum TOKEN_TYPE :  int
        {
            TokenPrimary =  1,
            TokenImpersonation =  2
        }

         enum SECURITY_IMPERSONATION_LEVEL :  int
        {
            SecurityAnonymous =  0,
            SecurityIdentification =  1,
            SecurityImpersonation =  2,
            SecurityDelegation =  3,
        }

         enum WTSInfoClass
        {
            InitialProgram,
            ApplicationName,
            WorkingDirectory,
            OEMId,
            SessionId,
            UserName,
            WinStationName,
            DomainName,
            ConnectState,
            ClientBuildNumber,
            ClientName,
            ClientDirectory,
            ClientProductId,
            ClientHardwareId,
            ClientAddress,
            ClientDisplay,
            ClientProtocolType
        } 

         #endregion

         #region Constants

         public  const  int TOKEN_DUPLICATE =  0x0002;
         public  const  uint MAXIMUM_ALLOWED =  0x2000000;
         public  const  int CREATE_NEW_CONSOLE =  0x00000010;

         public  const  int IDLE_PRIORITY_CLASS =  0x40;
         public  const  int NORMAL_PRIORITY_CLASS =  0x20;
         public  const  int HIGH_PRIORITY_CLASS =  0x80;
         public  const  int REALTIME_PRIORITY_CLASS =  0x100;

         #endregion

         #region Win32 API Imports

        [DllImport( " kernel32.dll ", SetLastError =  true)]
         private  static  extern  bool CloseHandle(IntPtr hSnapshot);

        [DllImport( " kernel32.dll ")]
         static  extern  uint WTSGetActiveConsoleSessionId();

        [DllImport( " wtsapi32.dll ", CharSet = CharSet.Unicode, SetLastError =  true), SuppressUnmanagedCodeSecurityAttribute]
         static  extern  bool WTSQuerySessionInformation(System.IntPtr hServer,  int sessionId, WTSInfoClass wtsInfoClass,  out System.IntPtr ppBuffer,  out  uint pBytesReturned);

        [DllImport( " advapi32.dll ", EntryPoint =  " CreateProcessAsUser ", SetLastError =  true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
         public  extern  static  bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine,  ref SECURITY_ATTRIBUTES lpProcessAttributes,
             ref SECURITY_ATTRIBUTES lpThreadAttributes,  bool bInheritHandle,  int dwCreationFlags, IntPtr lpEnvironment,
            String lpCurrentDirectory,  ref STARTUPINFO lpStartupInfo,  out PROCESS_INFORMATION lpProcessInformation);

        [DllImport( " kernel32.dll ")]
         static  extern  bool ProcessIdToSessionId( uint dwProcessId,  ref  uint pSessionId);

        [DllImport( " advapi32.dll ", EntryPoint =  " DuplicateTokenEx ")]
         public  extern  static  bool DuplicateTokenEx(IntPtr ExistingTokenHandle,  uint dwDesiredAccess,
             ref SECURITY_ATTRIBUTES lpThreadAttributes,  int TokenType,
             int ImpersonationLevel,  ref IntPtr DuplicateTokenHandle);

        [DllImport( " kernel32.dll ")]
         static  extern IntPtr OpenProcess( uint dwDesiredAccess,  bool bInheritHandle,  uint dwProcessId);

        [DllImport( " advapi32 ", SetLastError =  true), SuppressUnmanagedCodeSecurityAttribute]
         static  extern  bool OpenProcessToken(IntPtr ProcessHandle,  int DesiredAccess,  ref IntPtr TokenHandle);

         #endregion

         public  static  string GetCurrentActiveUser()
        {
            IntPtr hServer = IntPtr.Zero, state = IntPtr.Zero;
             uint bCount =  0;

             //  obtain the currently active session id; every logged on user in the system has a unique session id
             uint dwSessionId = WTSGetActiveConsoleSessionId();
             string domain =  string.Empty, userName =  string.Empty;

             if (WTSQuerySessionInformation(hServer, ( int)dwSessionId, WTSInfoClass.DomainName,  out state,  out bCount))
            {
                domain = Marshal.PtrToStringAuto(state);
            }

             if (WTSQuerySessionInformation(hServer, ( int)dwSessionId, WTSInfoClass.UserName,  out state,  out bCount))
            {
                userName = Marshal.PtrToStringAuto(state);
            }

             return  string.Format( " {0}\\{1} ", domain, userName);
        }

         ///   <summary>
        
///  Launches the given application with full admin rights, and in addition bypasses the Vista UAC prompt
        
///   </summary>
        
///   <param name="applicationName"> The name of the application to launch </param>
        
///   <param name="procInfo"> Process information regarding the launched application that gets returned to the caller </param>
        
///   <returns></returns>
         public  static  bool StartProcessAndBypassUAC(String applicationName, String command,  out PROCESS_INFORMATION procInfo)
        {
             uint winlogonPid =  0;
            IntPtr hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;
            procInfo =  new PROCESS_INFORMATION();

             //  obtain the currently active session id; every logged on user in the system has a unique session id
             uint dwSessionId = WTSGetActiveConsoleSessionId();

             //  obtain the process id of the winlogon process that is running within the currently active session
            Process[] processes = Process.GetProcessesByName( " winlogon ");
             foreach (Process p  in processes)
            {
                 if (( uint)p.SessionId == dwSessionId)
                {
                    winlogonPid = ( uint)p.Id;
                }
            }

             //  obtain a handle to the winlogon process
            hProcess = OpenProcess(MAXIMUM_ALLOWED,  false, winlogonPid);

             //  obtain a handle to the access token of the winlogon process
             if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE,  ref hPToken))
            {
                CloseHandle(hProcess);
                 return  false;
            }

             //  Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
            
//  I would prefer to not have to use a security attribute variable and to just 
            
//  simply pass null and inherit (by default) the security attributes
            
//  of the existing token. However, in C# structures are value types and therefore
            
//  cannot be assigned the null value.
            SECURITY_ATTRIBUTES sa =  new SECURITY_ATTRIBUTES();
            sa.Length = Marshal.SizeOf(sa);

             //  copy the access token of the winlogon process; the newly created token will be a primary token
             if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED,  ref sa, ( int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, ( int)TOKEN_TYPE.TokenPrimary,  ref hUserTokenDup))
            {
                CloseHandle(hProcess);
                CloseHandle(hPToken);
                 return  false;
            }

             //  By default CreateProcessAsUser creates a process on a non-interactive window station, meaning
            
//  the window station has a desktop that is invisible and the process is incapable of receiving
            
//  user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user 
            
//  interaction with the new process.
            STARTUPINFO si =  new STARTUPINFO();
            si.cb = ( int)Marshal.SizeOf(si);
            si.lpDesktop =  @" winsta0\default "//  interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop

            
//  flags that specify the priority and creation method of the process
             int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;

             //  create a new process in the current user's logon session
             bool result = CreateProcessAsUser(hUserTokenDup,         //  client's access token
                                            applicationName,         //  file to execute
                                            command,                 //  command line
                                             ref sa,                  //  pointer to process SECURITY_ATTRIBUTES
                                             ref sa,                  //  pointer to thread SECURITY_ATTRIBUTES
                                             false,                   //  handles are not inheritable
                                            dwCreationFlags,         //  creation flags
                                            IntPtr.Zero,             //  pointer to new environment block 
                                             null,                    //  name of current directory 
                                             ref si,                  //  pointer to STARTUPINFO structure
                                             out procInfo             //  receives information about new process
                                            );

             //  invalidate the handles
            CloseHandle(hProcess);
            CloseHandle(hPToken);
            CloseHandle(hUserTokenDup);

             return result;  //  return the result
        }

    }
}

    下面给出使用此静态方法的事例:

View Code
private  void RunCommand( object state)
{
     try
    {
         var cmdBatch = state  as  string;
         if ( string.IsNullOrEmpty(cmdBatch))
             return;

         string applicationName =  null;
         if (Path.GetExtension(cmdBatch).Equals( " .BAT "))
            applicationName =  " cmd.exe ";

         //  launch the application
        ApplicationLoader.PROCESS_INFORMATION procInfo;
        ApplicationLoader.StartProcessAndBypassUAC(applicationName,  " \" " +  cmdBatch +  " \" "out procInfo);

         //  check the process and get the complete or not.
         var process = Process.GetProcessById(( int)procInfo.dwProcessId);
         if(process !=  null)
            process.WaitForExit();
    }
     catch (Exception ex)
    {
        EventLog.WriteEntry(ex.Message, System.Diagnostics.EventLogEntryType.Error);
    }
}

    通过以上的方法,可以看到,当前启动的进程运行在活动用户的界面。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM