첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
본 블로그는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 블로그 콘텐츠 향상을 위해 쓰여집니다.

728x90
반응형
728x170

TestSolution.zip
0.02MB

[TestLibrary 프로젝트]

▶ ProcessHelper.cs

using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace TestLibrary
{
    /// <summary>
    /// 프로세스 헬퍼
    /// </summary>
    public static class ProcessHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Enumeration
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 윈도우 표시 타입 - ShowWindowType

        /// <summary>
        /// 윈도우 표시 타입
        /// </summary>
        private enum ShowWindowType
        {
            /// <summary>
            /// SW_HIDE
            /// </summary>
            SW_HIDE = 1,

            /// <summary>
            /// SW_SHOWNORMAL
            /// </summary>
            SW_SHOWNORMAL = 1,

            /// <summary>
            /// SW_NORMAL
            /// </summary>
            SW_NORMAL = 1,

            /// <summary>
            /// SW_SHOWMINIMIZED
            /// </summary>
            SW_SHOWMINIMIZED = 2,

            /// <summary>
            /// SW_SHOWMAXIMIZED
            /// </summary>
            SW_SHOWMAXIMIZED = 3,

            /// <summary>
            /// SW_MAXIMIZE
            /// </summary>
            SW_MAXIMIZE = 3,

            /// <summary>
            /// SW_SHOWNOACTIVATE
            /// </summary>
            SW_SHOWNOACTIVATE = 4,

            /// <summary>
            /// SW_SHOW
            /// </summary>
            SW_SHOW = 5,

            /// <summary>
            /// SW_MINIMIZE
            /// </summary>
            SW_MINIMIZE = 6,

            /// <summary>
            /// SW_SHOWMINNOACTIVE
            /// </summary>
            SW_SHOWMINNOACTIVE = 7,

            /// <summary>
            /// SW_SHOWN
            /// </summary>
            SW_SHOWN = 8,

            /// <summary>
            /// SW_RESTORE
            /// </summary>
            SW_RESTORE = 9,

            /// <summary>
            /// SW_SHOWDEFAULT
            /// </summary>
            SW_SHOWDEFAULT = 10,

            /// <summary>
            /// SW_MAX
            /// </summary>
            SW_MAX = 10
        }

        #endregion
        #region WTS 연결 상태 클래스 - WTS_CONNECTSTATE_CLASS

        /// <summary>
        /// WTS 연결 상태 클래스
        /// </summary>
        private enum WTS_CONNECTSTATE_CLASS
        {
            /// <summary>
            /// WTSActive
            /// </summary>
            WTSActive,

            /// <summary>
            /// WTSConnected
            /// </summary>
            WTSConnected,

            /// <summary>
            /// WTSConnectQuery
            /// </summary>
            WTSConnectQuery,

            /// <summary>
            /// WTSShadow
            /// </summary>
            WTSShadow,

            /// <summary>
            /// WTSDisconnected
            /// </summary>
            WTSDisconnected,

            /// <summary>
            /// WTSIdle
            /// </summary>
            WTSIdle,

            /// <summary>
            /// WTSListen
            /// </summary>
            WTSListen,

            /// <summary>
            /// WTSReset
            /// </summary>
            WTSReset,

            /// <summary>
            /// WTSDown
            /// </summary>
            WTSDown,

            /// <summary>
            /// WTSInit
            /// </summary>
            WTSInit
        }

        #endregion
        #region 보안 가장 레벨 - SECURITY_IMPERSONATION_LEVEL

        /// <summary>
        /// 보안 가장 레벨
        /// </summary>
        private enum SECURITY_IMPERSONATION_LEVEL
        {
            /// <summary>
            /// SecurityAnonymous
            /// </summary>
            SecurityAnonymous = 0,

            /// <summary>
            /// SecurityIdentification
            /// </summary>
            SecurityIdentification = 1,

            /// <summary>
            /// SecurityImpersonation
            /// </summary>
            SecurityImpersonation = 2,

            /// <summary>
            /// SecurityDelegation
            /// </summary>
            SecurityDelegation = 3
        }

        #endregion
        #region 토큰 타입 - TOKEN_TYPE

        /// <summary>
        /// 토큰 타입
        /// </summary>
        private enum TOKEN_TYPE
        {
            /// <summary>
            /// TokenPrimary
            /// </summary>
            TokenPrimary = 1,

            /// <summary>
            /// TokenImpersonation
            /// </summary>
            TokenImpersonation = 2
        }

        #endregion
        #region 토큰 정보 클래스 - TOKEN_INFORMATION_CLASS

        /// <summary>
        /// 토큰 정보 클래스
        /// </summary>
        private enum TOKEN_INFORMATION_CLASS
        {
            /// <summary>
            /// TokenUser
            /// </summary>
            TokenUser = 1,

            /// <summary>
            /// TokenGroups
            /// </summary>
            TokenGroups,

            /// <summary>
            /// TokenPrivileges
            /// </summary>
            TokenPrivileges,

            /// <summary>
            /// TokenOwner
            /// </summary>
            TokenOwner,

            /// <summary>
            /// TokenPrimaryGroup
            /// </summary>
            TokenPrimaryGroup,

            /// <summary>
            /// TokenDefaultDACL
            /// </summary>
            TokenDefaultDACL,

            /// <summary>
            /// TokenSource
            /// </summary>
            TokenSource,

            /// <summary>
            /// TokenType
            /// </summary>
            TokenType,

            /// <summary>
            /// TokenImpersonationLevel
            /// </summary>
            TokenImpersonationLevel,

            /// <summary>
            /// TokenStatistics
            /// </summary>
            TokenStatistics,

            /// <summary>
            /// TokenRestrictedSIDs
            /// </summary>
            TokenRestrictedSIDs,

            /// <summary>
            /// TokenSessionID
            /// </summary>
            TokenSessionID,

            /// <summary>
            /// TokenGroupsAndPrivileges
            /// </summary>
            TokenGroupsAndPrivileges,

            /// <summary>
            /// TokenSessionReference
            /// </summary>
            TokenSessionReference,

            /// <summary>
            /// TokenSandBoxInert
            /// </summary>
            TokenSandBoxInert,

            /// <summary>
            /// TokenAuditPolicy
            /// </summary>
            TokenAuditPolicy,

            /// <summary>
            /// TokenOrigin
            /// </summary>
            TokenOrigin,

            /// <summary>
            /// TokenElevationType
            /// </summary>
            TokenElevationType,

            /// <summary>
            /// TokenLinkedToken
            /// </summary>
            TokenLinkedToken,

            /// <summary>
            /// TokenElevation
            /// </summary>
            TokenElevation,

            /// <summary>
            /// TokenHasRestrictions
            /// </summary>
            TokenHasRestrictions,

            /// <summary>
            /// TokenAccessInformation
            /// </summary>
            TokenAccessInformation,

            /// <summary>
            /// TokenVirtualizationAllowed
            /// </summary>
            TokenVirtualizationAllowed,

            /// <summary>
            /// TokenVirtualizationEnabled
            /// </summary>
            TokenVirtualizationEnabled,

            /// <summary>
            /// TokenIntegrityLevel
            /// </summary>
            TokenIntegrityLevel,

            /// <summary>
            /// TokenUIAccess
            /// </summary>
            TokenUIAccess,

            /// <summary>
            /// TokenMandatoryPolicy
            /// </summary>
            TokenMandatoryPolicy,

            /// <summary>
            /// TokenLogonSid
            /// </summary>
            TokenLogonSID,

            /// <summary>
            /// MaxTokenInfoClass
            /// </summary>
            MaxTokenInfoClass
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Structure
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 프로세스 정보 - PROCESS_INFORMATION

        /// <summary>
        /// 프로세스 정보
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            //////////////////////////////////////////////////////////////////////////////////////////////////// Field
            ////////////////////////////////////////////////////////////////////////////////////////// Public

            #region Field

            /// <summary>
            /// 프로세스 핸들
            /// </summary>
            public IntPtr ProcessHandle;

            /// <summary>
            /// 스레드 핸들
            /// </summary>
            public IntPtr ThreadHandle;

            /// <summary>
            /// 프로세스 ID
            /// </summary>
            public uint ProcessID;

            /// <summary>
            /// 스레드 ID
            /// </summary>
            public uint ThreadID;

            #endregion
        }

        #endregion
        #region 시작 정보 - STARTUPINFO

        /// <summary>
        /// 시작 정보
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private struct STARTUPINFO
        {
            //////////////////////////////////////////////////////////////////////////////////////////////////// Field
            ////////////////////////////////////////////////////////////////////////////////////////// Public

            #region Field

            /// <summary>
            /// 바이트 카운트
            /// </summary>
            public int ByteCount;

            /// <summary>
            /// 예약 문자열
            /// </summary>
            public string ReservedString;

            /// <summary>
            /// 데스크톱
            /// </summary>
            public string Desktop;

            /// <summary>
            /// 제목
            /// </summary>
            public string Title;

            /// <summary>
            /// X
            /// </summary>
            public uint X;

            /// <summary>
            /// ㅛ
            /// </summary>
            public uint Y;

            /// <summary>
            /// X 크기
            /// </summary>
            public uint XSize;

            /// <summary>
            /// Y 크기
            /// </summary>
            public uint YSize;

            /// <summary>
            /// X 카운트 (문자 단위)
            /// </summary>
            public uint XCountCharacter;

            /// <summary>
            /// Y 카운트 (문자 단위)
            /// </summary>
            public uint YCountCharacter;

            /// <summary>
            /// 채우기 어트리뷰트
            /// </summary>
            public uint FillAttribute;

            /// <summary>
            /// 플래그
            /// </summary>
            public uint Flag;

            /// <summary>
            /// 윈도우 표시
            /// </summary>
            public short ShowWindow;

            /// <summary>
            /// 예약 핸들 바이트 카운트
            /// </summary>
            public short ByteCountReservedHandle;

            /// <summary>
            /// 예약 핸들
            /// </summary>
            public IntPtr ReservedHandle;

            /// <summary>
            /// 표준 입력 핸들
            /// </summary>
            public IntPtr StandardInputHandle;

            /// <summary>
            /// 표준 출력 핸들
            /// </summary>
            public IntPtr StandardOutputHandle;

            /// <summary>
            /// 표준 에러 핸들
            /// </summary>
            public IntPtr StandardErrorHandle;

            #endregion
        }

        #endregion
        #region WTS 세션 정보 - WTS_SESSION_INFO

        /// <summary>
        /// WTS 세션 정보
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private struct WTS_SESSION_INFO
        {
            //////////////////////////////////////////////////////////////////////////////////////////////////// Field
            ////////////////////////////////////////////////////////////////////////////////////////// Public
    
            #region Field

            /// <summary>
            /// 세션 ID
            /// </summary>
            public readonly uint SessionID;

            /// <summary>
            /// WIN 스테이션명
            /// </summary>
            [MarshalAs(UnmanagedType.LPStr)]
            public readonly string WinStationName;

            /// <summary>
            /// 상태
            /// </summary>
            public readonly WTS_CONNECTSTATE_CLASS State;

            #endregion
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Import
        ////////////////////////////////////////////////////////////////////////////////////////// Static

        #region 사용자로 프로세스 생성하기 - CreateProcessAsUser(tokenHandle, applicationName, commandLine, processAttributeHandle, threadAttributeHandle, inheritHandle, creationFlag, environmentHandle, currentDirectoryPath, startupInfo, processInformation)

        /// <summary>
        /// 사용자로 프로세스 생성하기
        /// </summary>
        /// <param name="tokenHandle">토큰 핸들</param>
        /// <param name="applicationName">애플리케이션명</param>
        /// <param name="commandLine">명령줄</param>
        /// <param name="processAttributeHandle">프로세스 어트리뷰트 핸들</param>
        /// <param name="threadAttributeHandle">스레드 어트리뷰트 핸들</param>
        /// <param name="inheritHandle">상속 핸들</param>
        /// <param name="creationFlag">생성 플래그</param>
        /// <param name="environmentHandle">환경 핸들</param>
        /// <param name="currentDirectoryPath">현재 디렉토리 경로</param>
        /// <param name="startupInfo">시작 정보</param>
        /// <param name="processInformation">프로세스 정보</param>
        /// <returns>처리 결과</returns>
        [DllImport("advapi32", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        private static extern bool CreateProcessAsUser
        (
            IntPtr                  tokenHandle,
            string                  applicationName,
            string                  commandLine,
            IntPtr                  processAttributeHandle,
            IntPtr                  threadAttributeHandle,
            bool                    inheritHandle,
            uint                    creationFlag,
            IntPtr                  environmentHandle,
            string                  currentDirectoryPath,
            ref STARTUPINFO         startupInfo,
            out PROCESS_INFORMATION processInformation
        );

        #endregion
        #region 토큰 복제하기 (확장) - DuplicateTokenEx(existingTokenHandle, desiredAccess, threadAttributeHandle, tokenType, impersonationLevel, duplicateTokenHandle)

        /// <summary>
        /// 토큰 복제하기 (확장)
        /// </summary>
        /// <param name="existingTokenHandle">기존 토클 핸들</param>
        /// <param name="desiredAccess">희망 액세스</param>
        /// <param name="threadAttributeHandle">스레드 어트리뷰트 핸들</param>
        /// <param name="tokenType">토큰 타입</param>
        /// <param name="impersonationLevel">가장 레벨</param>
        /// <param name="duplicateTokenHandle">복제 토큰 핸들</param>
        /// <returns>처리 결과</returns>
        [DllImport("advapi32", EntryPoint = "DuplicateTokenEx")]
        private static extern bool DuplicateTokenEx
        (
            IntPtr     existingTokenHandle,
            uint       desiredAccess,
            IntPtr     threadAttributeHandle,
            int        tokenType,
            int        impersonationLevel,
            ref IntPtr duplicateTokenHandle
        );

        #endregion
        #region 환경 블럭 생성하기 - CreateEnvironmentBlock(environmentHandle, tokenHandle, inherit)

        /// <summary>
        /// 환경 블럭 생성하기
        /// </summary>
        /// <param name="environmentHandle">환경 핸들</param>
        /// <param name="tokenHandle">토큰 핸들</param>
        /// <param name="inherit">상속 여부</param>
        /// <returns>처리 결과</returns>
        [DllImport("userenv", SetLastError = true)]
        private static extern bool CreateEnvironmentBlock(ref IntPtr environmentHandle, IntPtr tokenHandle, bool inherit);

        #endregion
        #region 환경 블럭 제거하기 - DestroyEnvironmentBlock(environmentHandle)

        /// <summary>
        /// 환경 블럭 제거하기
        /// </summary>
        /// <param name="environmentHandle">환경 핸들</param>
        /// <returns>처리 결과</returns>
        [DllImport("userenv", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool DestroyEnvironmentBlock(IntPtr environmentHandle);

        #endregion
        #region 핸들 닫기 - CloseHandle(snapshotHandle)

        /// <summary>
        /// 핸들 닫기
        /// </summary>
        /// <param name="snapshotHandle">스냅샷 핸들</param>
        /// <returns>처리 결과</returns>
        [DllImport("kernel32", SetLastError = true)]
        private static extern bool CloseHandle(IntPtr snapshotHandle);

        #endregion
        #region WTS 활성 콘솔 세션 ID 구하기 - WTSGetActiveConsoleSessionId()

        /// <summary>
        /// WTS 활성 콘솔 세션 ID 구하기
        /// </summary>
        /// <returns>활성 콘솔 세션 ID</returns>
        [DllImport("kernel32")]
        private static extern uint WTSGetActiveConsoleSessionId();

        #endregion
        #region WTS 사용자 토큰 질의하기 - WTSQueryUserToken(sessionID, tokenHandle)

        /// <summary>
        /// WTS 사용자 토큰 질의하기
        /// </summary>
        /// <param name="sessionID">세션 ID</param>
        /// <param name="tokenHandle">토큰 핸들</param>
        /// <returns>처리 결과</returns>
        [DllImport("wtsapi32")]
        private static extern uint WTSQueryUserToken(uint sessionID, ref IntPtr tokenHandle);

        #endregion
        #region WTS 세션 열거하기 - WTSEnumerateSessions(serverHandle, reserved, version, sessionInfoHandle, count)

        /// <summary>
        /// WTS 세션 열거하기
        /// </summary>
        /// <param name="serverHandle">서버 핸들</param>
        /// <param name="reserved">예약</param>
        /// <param name="version">버전</param>
        /// <param name="sessionInfoHandle">세션 정보 핸들</param>
        /// <param name="count">카운트</param>
        /// <returns>처리 결과</returns>
        [DllImport("wtsapi32", SetLastError = true)]
        private static extern int WTSEnumerateSessions
        (
            IntPtr     serverHandle,
            int        reserved,
            int        version,
            ref IntPtr sessionInfoHandle,
            ref int    count
        );

        #endregion
        #region WTF 메모리 해제하기 - WTSFreeMemory(memoryHandle)

        /// <summary>
        /// WTF 메모리 해제하기
        /// </summary>
        /// <param name="memoryHandle">메모리 핸들</param>
        [DllImport("wtsapi32")]
        private static extern void WTSFreeMemory(IntPtr memoryHandle);

        #endregion
        #region 토큰 정보 설정하기 - SetTokenInformation(tokenHandle, tokenInformationClass, tokenInformation, tokenInformationLenth)

        /// <summary>
        /// 토큰 정보 설정하기
        /// </summary>
        /// <param name="tokenHandle">토큰 핸들</param>
        /// <param name="tokenInformationClass">토큰 정보 클래스</param>
        /// <param name="tokenInformation">토큰 정보</param>
        /// <param name="tokenInformationLenth">토큰 정보 길이</param>
        /// <returns>처리 결과</returns>
        [DllImport("advapi32", SetLastError = true)]
        private static extern bool SetTokenInformation(IntPtr tokenHandle, TOKEN_INFORMATION_CLASS tokenInformationClass, ref uint tokenInformation, uint tokenInformationLenth);

        #endregion
        #region 프로세스 토큰 열기 - OpenProcessToken(processHandle, desiredAccess, tokenHandle)

        /// <summary>
        /// 프로세스 토큰 열기
        /// </summary>
        /// <param name="processHandle">프로세스 핸들</param>
        /// <param name="desiredAccess">희망 액세스</param>
        /// <param name="tokenHandle">토큰 핸들</param>
        /// <returns>처리 결과</returns>
        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool OpenProcessToken(IntPtr processHandle, uint desiredAccess, out IntPtr tokenHandle);

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// WTS_CURRENT_SERVER_HANDLE
        /// </summary>
        private static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// CREATE_UNICODE_ENVIRONMENT
        /// </summary>
        private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;

        /// <summary>
        /// CREATE_NO_WINDOW
        /// </summary>
        private const int CREATE_NO_WINDOW = 0x08000000;

        /// <summary>
        /// CREATE_NEW_CONSOLE
        /// </summary>
        private const int CREATE_NEW_CONSOLE = 0x00000010;

        /// <summary>
        /// INVALID_SESSION_ID
        /// </summary>
        private const uint INVALID_SESSION_ID = 0xffffffff;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 현재 사용자로 프로세스 실행하기 - ExecuteProcessAsCurrentUser(applicationFilePath, processInformation, commandLine, workingDirectoryPath, visible)

        /// <summary>
        /// 현재 사용자로 프로세스 실행하기
        /// </summary>
        /// <param name="applicationFilePath">애플리케이션 경로</param>
        /// <param name="processInformation">프로세스 정보</param>
        /// <param name="commandLine">명령줄</param>
        /// <param name="workingDirectoryPath">작업 디렉토리 경로</param>
        /// <param name="visible">표시 여부</param>
        /// <returns>처리 결과</returns>
        public static bool ExecuteProcessAsCurrentUser
        (
            string                  applicationFilePath,
            out PROCESS_INFORMATION processInformation,
            string                  commandLine = null,
            string                  workingDirectoryPath = null,
            bool                    visible = true
        )
        {
            IntPtr userTokenHandle   = IntPtr.Zero;
            IntPtr systemTokenHandle = IntPtr.Zero;
            IntPtr environmentHandle = IntPtr.Zero;
        
            processInformation = new PROCESS_INFORMATION();
        
            try
            {
                uint activeSessionID = GetActiveConsoleSessionID();
        
                if(activeSessionID == INVALID_SESSION_ID)
                {
                    return false;
                }
        
                if(!GetSessionUserToken(activeSessionID, ref userTokenHandle))
                {
                    throw new Exception("ExecuteProcessAsCurrentUser : GetSessionUserToken failed.");
                }
        
                if(!GetSystemToken(activeSessionID, ref systemTokenHandle))
                {
                    throw new Exception("ExecuteProcessAsCurrentUser : GetSystemToken failed.");
                }
        
                if(!CreateEnvironmentBlock(ref environmentHandle, userTokenHandle, false))
                {
                    throw new Exception("ExecuteProcessAsCurrentUser : CreateEnvironmentBlock failed.");
                }
        
                uint creationFlag = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
        
                STARTUPINFO startupInfo = new STARTUPINFO();
        
                startupInfo.ByteCount  = Marshal.SizeOf(typeof(STARTUPINFO));
                startupInfo.ShowWindow = (short)(visible ? ShowWindowType.SW_SHOW : ShowWindowType.SW_HIDE);
                startupInfo.Desktop    = "winsta0\\default";
        
                if
                (
                    !CreateProcessAsUser
                    (
                        systemTokenHandle,
                        applicationFilePath,
                        commandLine,
                        IntPtr.Zero,
                        IntPtr.Zero,
                        false,
                        creationFlag,
                        environmentHandle,
                        workingDirectoryPath,
                        ref startupInfo,
                        out processInformation
                    )
                )
                {
                    int errorCode = Marshal.GetLastWin32Error();
        
                    string errorMessage = $"ExecuteProcessAsCurrentUser: CreateProcessAsUser failed.";
        
                    throw new Exception(errorMessage);
                }
            }
            finally
            {
                CloseHandle(userTokenHandle);
                CloseHandle(systemTokenHandle);
        
                if(environmentHandle != IntPtr.Zero)
                {
                    DestroyEnvironmentBlock(environmentHandle);
                }
        
                CloseHandle(processInformation.ThreadHandle);
                CloseHandle(processInformation.ProcessHandle);
            }
        
            return true;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Private

        #region 세션 사용자 토큰 구하기 - GetSessionUserToken(activeSessionID, userTokenHandle)

        /// <summary>
        /// 세션 사용자 토큰 구하기
        /// </summary>
        /// <param name="activeSessionID">활성 세션 ID</param>
        /// <param name="userTokenHandle">사용자 토큰 핸들</param>
        /// <returns>처리 결과</returns>
        private static bool GetSessionUserToken(uint activeSessionID, ref IntPtr userTokenHandle)
        {
            bool   result                   = false;
            IntPtr impersonationTokenHandle = IntPtr.Zero;
        
            if(WTSQueryUserToken(activeSessionID, ref impersonationTokenHandle) != 0)
            {
                result = DuplicateTokenEx
                (
                    impersonationTokenHandle,
                    0,
                    IntPtr.Zero,
                    (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
                    (int)TOKEN_TYPE.TokenPrimary,
                    ref userTokenHandle
                );
        
                CloseHandle(impersonationTokenHandle);
            }
        
            return result;
        }

        #endregion
        #region 시스템 토큰 구하기 - GetSystemToken(activeSessionID, systemTokenHandle)

        /// <summary>
        /// 시스템 토큰 구하기
        /// </summary>
        /// <param name="activeSessionID">활성 세션 ID</param>
        /// <param name="systemTokenHandle">시스템 토큰 핸들</param>
        /// <returns>처리 결과</returns>
        private static bool GetSystemToken(uint activeSessionID, ref IntPtr systemTokenHandle)
        {
            using
            (
                WindowsIdentity windowsIdentity = WindowsIdentity.GetCurrent
                (
                    TokenAccessLevels.AssignPrimary   |
                    TokenAccessLevels.Duplicate       |
                    TokenAccessLevels.Impersonate     |
                    TokenAccessLevels.AdjustDefault   |
                    TokenAccessLevels.AdjustSessionId |
                    TokenAccessLevels.Read
                )
            )
            {
                IntPtr impersonationTokenHandle = windowsIdentity.Token;
        
                if(impersonationTokenHandle == IntPtr.Zero)
                {
                    return false;
                }
        
                if
                (
                    DuplicateTokenEx
                    (
                        impersonationTokenHandle,
                        0,
                        IntPtr.Zero,
                        (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
                        (int)TOKEN_TYPE.TokenPrimary,
                        ref systemTokenHandle
                    )
                )
                {
                    if(SetTokenInformation(systemTokenHandle, TOKEN_INFORMATION_CLASS.TokenSessionID, ref activeSessionID, (uint)Marshal.SizeOf(activeSessionID)))
                    {
                        return true;
                    }
                    else
                    {
                        CloseHandle(systemTokenHandle);
                    }
                }
            }
        
            return false;
        }

        #endregion
        #region 활성 콘솔 세션 ID 구하기 - GetActiveConsoleSessionID()

        /// <summary>
        /// 활성 콘솔 세션 ID 구하기
        /// </summary>
        /// <returns>활성 콘솔 세션 ID</returns>
        private static uint GetActiveConsoleSessionID()
        {
            uint   activeSessionID   = INVALID_SESSION_ID;
            IntPtr sessionInfoHandle = IntPtr.Zero;
            int    sessionCount      = 0;
        
            if(WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref sessionInfoHandle, ref sessionCount) != 0)
            {
                int arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
        
                IntPtr currentSessionHandle = sessionInfoHandle;
        
                for(int i = 0; i < sessionCount; i++)
                {
                    WTS_SESSION_INFO sessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(currentSessionHandle, typeof(WTS_SESSION_INFO));
        
                    currentSessionHandle += arrayElementSize;
        
                    if(sessionInfo.State == WTS_CONNECTSTATE_CLASS.WTSActive)
                    {
                        activeSessionID = sessionInfo.SessionID;
        
                        break;
                    }
                }
        
                WTSFreeMemory(sessionInfoHandle);
        
                if(activeSessionID == INVALID_SESSION_ID)
                {
                    activeSessionID = WTSGetActiveConsoleSessionId();
                }
            }
        
            return activeSessionID;
        }

        #endregion
    }
}

 

728x90

 

▶ TestNode.cs

using System;
using System.Diagnostics;
using System.IO;

namespace TestLibrary
{
    /// <summary>
    /// 테스트 노드
    /// </summary>
    public class TestNode
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        #region 로그

        /// <summary>
        /// 로그 헬퍼
        /// </summary>
        private ILogHelper logHelper;

        #endregion
        #region 실행 작업자

        /// <summary>
        /// 실행 작업자 주기 (단위 : 밀리초)
        /// </summary>
        private int executeWorkerInterval = 1000; // 1초

        /// <summary>
        /// 실행 작업자
        /// </summary>
        private RepeatWorker executeWorker = null;

        #endregion

        /// <summary>
        /// 실행 여부
        /// </summary>
        private bool isRunning = false;

        /// <summary>
        /// 틱 카운트
        /// </summary>
        private int tickCount = 0;

        /// <summary>
        /// 첫번째 여부
        /// </summary>
        private bool isFirst = true;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 실행 여부 - IsRunning

        /// <summary>
        /// 실행 여부
        /// </summary>
        public bool IsRunning
        {
            get
            {
                return this.isRunning;
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - TestNode()

        /// <summary>
        /// 생성자
        /// </summary>
        public TestNode()
        {
            #region 로그 헬퍼를 설정한다.

            this.logHelper = new FileLogHelper("d:\\", "TestNode.log");

            #endregion
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 시작하기 - Start()

        /// <summary>
        /// 시작하기
        /// </summary>
        public void Start()
        {
            try
            {
                File.Delete(@"D:\TestNode.log");

                this.logHelper?.WriteLog("BEGIN START FUNCTION");

                if(this.isRunning)
                {
                    this.logHelper?.WriteLog("STOP START FUNCTION : AlreadyRunning");

                    return;
                }

                this.isRunning = true;

                #region 실행 작업자를 설정한다.

                if(this.executeWorker != null)
                {
                    if(this.executeWorker.IsRunning)
                    {
                        this.executeWorker.Stop();
                    }

                    this.executeWorker = null;
                }

                this.executeWorker = new RepeatWorker(new Action<object>(ProcessExecute), null, this.executeWorkerInterval);

                #endregion

                this.executeWorker.Start();

                this.logHelper?.WriteLog("END START FUNCTION");
            }
            catch(Exception exception)
            {
                this.isRunning = false;

                this.logHelper?.WriteErrorLog(exception, "ERROR START FUNCTION");

                throw exception;
            }
        }

        #endregion
        #region 중단하기 - Stop()

        /// <summary>
        /// 중단하기
        /// </summary>
        public void Stop()
        {
            try
            {
                this.logHelper?.WriteLog("BEGIN STOP FUNCTION");

                if(!this.isRunning)
                {
                    this.logHelper?.WriteLog("STOP STOP FUNCTION : AlreadyStopped");

                    return;
                }

                this.isRunning = false;

                #region 실행 작업자를 중단한다.

                this.executeWorker.Stop();

                this.executeWorker = null;

                #endregion

                this.logHelper?.WriteLog("END STOP FUNCTION");
            }
            catch(Exception exception)
            {
                this.isRunning = false;

                this.logHelper?.WriteErrorLog(exception, "ERROR STOP FUNCTION");
            }
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 프로세스 죽이기 - KillProcess(string filePath)

        /// <summary>
        /// 프로세스 죽이기
        /// </summary>
        /// <param name="filePath">파일 경로</param>
        /// <returns>처리 결과</returns>
        private bool KillProcess(string filePath)
        {
            if(string.IsNullOrWhiteSpace(filePath))
            {
                return false;
            }
    
            bool result = false;
    
            try
            {
                string processName = Path.GetFileNameWithoutExtension(filePath);
    
                foreach(Process process in Process.GetProcessesByName(processName))
                {
                    try
                    {
                        if
                        (
                            process.MainModule != null          &&
                            process.MainModule.FileName != null &&
                            string.Compare(process.MainModule.FileName, filePath, true) == 0
                        )
                        {
                            process.Kill();

                            result = true;
                        }
                    }
                    catch
                    {
                    }
                }
            }
            catch
            {
            }

            return result;
        }

        #endregion
        #region 메모장 실행하기 - ExecuteNotepad()

        /// <summary>
        /// 메모장 실행하기
        /// </summary>
        private void ExecuteNotepad()
        {
            string filePath             = @"C:\Windows\System32\notepad.exe";
            string workingDirectoryPath = Path.GetDirectoryName(filePath);
    
            KillProcess(filePath);
    
            try
            {
                ProcessHelper.ExecuteProcessAsCurrentUser(filePath, out ProcessHelper.PROCESS_INFORMATION processInformation, "", workingDirectoryPath);

                this.logHelper?.WriteLog("EXECUTE PROCESS AS CURRENT USER");
            }
            catch(Exception exception)
            {
                this.logHelper?.WriteErrorLog(exception, "ERROR EXECUTE PROCESS AS CURRENT USER");
            }
        }

        #endregion
        #region 실행 처리하기 - ProcessExecute(parameter)

        /// <summary>
        /// 실행 처리하기
        /// </summary>
        /// <param name="parameter">매개 변수</param>
        private void ProcessExecute(object parameter)
        {
            try
            {
                this.logHelper?.WriteLog("테스트 메시지");

                if(this.isFirst)
                {
                    this.tickCount++;

                    if(this.tickCount == 30)
                    {
                        this.isFirst = false;

                        ExecuteNotepad();
                    }
                }
            }
            catch(Exception exception)
            {
                this.logHelper?.WriteErrorLog(exception, "ERROR PROCESS EXECUTE FUNCTION");
            }
        }

        #endregion
    }
}

 

300x250

 

[TestService 프로젝트]

▶ ProcessInstaller.cs

using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;

namespace TestService
{
    /// <summary>
    /// 프로젝트 설치자
    /// </summary>
    [RunInstaller(true)]
    public partial class ProjectInstaller : Installer
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 서비스 프로세스 설치자
        /// </summary>
        private ServiceProcessInstaller serviceProcessInstaller;

        /// <summary>
        /// 서비스 설치자
        /// </summary>
        private ServiceInstaller serviceInstaller;

        /// <summary>
        /// 서비스명
        /// </summary>
        private const string SERVICE_NAME = "TestNode";

        /// <summary>
        /// 서비스 설명
        /// </summary>
        private const string SERVICE_DESCRIPTION = "테스트 노드";

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - ProjectInstaller()

        /// <summary>
        /// 생성자
        /// </summary>
        public ProjectInstaller()
        {
            this.serviceProcessInstaller = new ServiceProcessInstaller();

            serviceProcessInstaller.Account = ServiceAccount.LocalSystem;
            serviceProcessInstaller.Password = null;
            serviceProcessInstaller.Username = null;

            this.serviceInstaller = new ServiceInstaller();

            serviceInstaller.ServiceName = SERVICE_NAME;
            serviceInstaller.DisplayName = SERVICE_NAME;
            serviceInstaller.Description = SERVICE_DESCRIPTION;
            serviceInstaller.StartType   = ServiceStartMode.Automatic;

            Installers.AddRange(new Installer[] { this.serviceProcessInstaller, this.serviceInstaller });
        }

        #endregion
    }
}

 

▶ ProcessService.cs

using System;
using System.ServiceProcess;

using TestLibrary;

namespace TestService
{
    /// <summary>
    /// 프로젝트 서비스
    /// </summary>
    public partial class ProjectService : ServiceBase
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 에이전트 노드
        /// </summary>
        private TestNode testNode;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - ProjectService()

        /// <summary>
        /// 생성자
        /// </summary>
        public ProjectService()
        {
            this.testNode = new TestNode();
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 테스트 하기 - Test(argumentArray)

        /// <summary>
        /// 테스트 하기
        /// </summary>
        /// <param name="argumentArray">인자 배열</param>
        public void Test(string[] argumentArray)
        {
            OnStart(argumentArray);

            Console.ReadKey(true);

            OnStop();
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region 시작시 처리하기 - OnStart(argumentArray)

        /// <summary>
        /// 시작시 처리하기
        /// </summary>
        /// <param name="argumentArray">인자 배열</param>
        protected override void OnStart(string[] argumentArray)
        {
            this.testNode.Start();
        }

        #endregion
        #region 중단시 처리하기 - OnStop()

        /// <summary>
        /// 중단시 처리하기
        /// </summary>
        protected override void OnStop()
        {
            this.testNode.Stop();
        }

        #endregion
    }
}

 

▶ Program.cs

using System;
using System.ServiceProcess;

namespace TestService
{
    /// <summary>
    /// 프로그램
    /// </summary>
    static class Program
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Private

        #region 프로그램 시작하기 - Main(argumentArray)

        /// <summary>
        /// 프로그램 시작하기
        /// </summary>
        /// <param name="argumentArray">인자 배열</param>
        private static void Main(string[] argumentArray)
        {
            if(Environment.UserInteractive)
            {
                ProjectService service = new ProjectService();

                service.Test(argumentArray);
            }
            else
            {
                ServiceBase[] serviceBaseArray;

                serviceBaseArray = new ServiceBase[] { new ProjectService() };

                ServiceBase.Run(serviceBaseArray);
            }
        }

        #endregion
    }
}
728x90
반응형
그리드형
Posted by 사용자 icodebroker

댓글을 달아 주세요