첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
------------------------------------------------------------------------------------------------------------------------------------------------------
728x90
728x170

TestProject.zip
0.01MB

▶ ProcessHelper.cs

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace TestProject
{
    /// <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

        //////////////////////////////////////////////////////////////////////////////////////////////////// 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

        //////////////////////////////////////////////////////////////////////////////////////////////////// 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 사용자 토큰 구하기 - GetSessionUserToken(userTokenHandle)

        /// <summary>
        /// 사용자 토큰 구하기
        /// </summary>
        /// <param name="userTokenHandle">사용자 토큰 핸들</param>
        /// <returns>처리 결과</returns>
        public static bool GetSessionUserToken(ref IntPtr userTokenHandle)
        {
            bool   result                   = false;
            IntPtr impersonationTokenHandle = IntPtr.Zero;
            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 currentSessionInfoHandle = sessionInfoHandle;

                for(int i = 0; i < sessionCount; i++)
                {
                    WTS_SESSION_INFO sessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure
                    (
                        currentSessionInfoHandle,
                        typeof(WTS_SESSION_INFO)
                    );

                    currentSessionInfoHandle += arrayElementSize;

                    if(sessionInfo.State == WTS_CONNECTSTATE_CLASS.WTSActive)
                    {
                        activeSessionID = sessionInfo.SessionID;

                        break;
                    }
                }

                WTSFreeMemory(sessionInfoHandle);
            }

            if(activeSessionID == INVALID_SESSION_ID)
            {
                activeSessionID = WTSGetActiveConsoleSessionId();
            }

            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 현재 사용자로 프로세스 실행하기 - ExecuteProcessAsCurrentUser(applicationPath, processInformation, commandLine, workingDirectoryPath, visible)

        /// <summary>
        /// 현재 사용자로 프로세스 실행하기
        /// </summary>
        /// <param name="applicationPath">애플리케이션 경로</param>
        /// <param name="processInformation">프로세스 정보</param>
        /// <param name="commandLine">명령줄</param>
        /// <param name="workingDirectoryPath">작업 디렉토리 경로</param>
        /// <param name="visible">표시 여부</param>
        /// <returns>처리 결과</returns>
        public static bool ExecuteProcessAsCurrentUser
        (
            string                  applicationPath,
            out PROCESS_INFORMATION processInformation,
            string                  commandLine          = null,
            string                  workingDirectoryPath = null,
            bool                    visible              = true
        )
        {
            IntPtr userTokenHandle  = IntPtr.Zero;
            STARTUPINFO startupInfo = new STARTUPINFO();

            startupInfo.ByteCount  = Marshal.SizeOf(typeof(STARTUPINFO));
            startupInfo.ShowWindow = (short)(visible ? ShowWindowType.SW_SHOW : ShowWindowType.SW_HIDE);
            startupInfo.Desktop    = "winsta0\\default";

            processInformation = new PROCESS_INFORMATION();

            IntPtr environmentHandle = IntPtr.Zero;
            int    errorCode;

            try
            {
                if(!GetSessionUserToken(ref userTokenHandle))
                {
                    throw new Exception("StartProcessAsCurrentUser : GetSessionUserToken failed.");
                }

                uint creationFlag = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);

                if(!CreateEnvironmentBlock(ref environmentHandle, userTokenHandle, false))
                {
                    throw new Exception("StartProcessAsCurrentUser : CreateEnvironmentBlock failed.");
                }

                if
                (
                    !CreateProcessAsUser
                    (
                        userTokenHandle,
                        applicationPath,
                        commandLine,
                        IntPtr.Zero,
                        IntPtr.Zero,
                        false,
                        creationFlag,
                        environmentHandle,
                        workingDirectoryPath,
                        ref startupInfo,
                        out processInformation
                    )
                )
                {
                    errorCode = Marshal.GetLastWin32Error();

                    string errorMessage = string.Format
                    (
                        "StartProcessAsCurrentUser : CreateProcessAsUser failed. Error Code : {0}, applicationPath : {1}, commandLine : {2}",
                        errorCode,
                        applicationPath,
                        commandLine
                    );

                    throw new Exception(errorMessage);
                }
            }
            finally
            {
                CloseHandle(userTokenHandle);

                if(environmentHandle != IntPtr.Zero)
                {
                    DestroyEnvironmentBlock(environmentHandle);
                }

                CloseHandle(processInformation.ThreadHandle);
                CloseHandle(processInformation.ProcessHandle);
            }

            return true;
        }

        #endregion
        #region 프로세스 실행하기 - ExecuteProcess(filePath)

        /// <summary>
        /// 프로세스 실행하기
        /// </summary>
        /// <param name="filePath">파일 경로</param>
        /// <returns>프로세스 핸들</returns>
        public static int ExecuteProcess(string filePath)
        {
            if(string.IsNullOrWhiteSpace(filePath))
            {
                return 0;
            }

            if(!File.Exists(filePath))
            {
                return 0;
            }

            try
            {
                string directoryPath = Path.GetDirectoryName(filePath);

                if(!Environment.UserInteractive)
                {
                    if(ExecuteProcessAsCurrentUser(filePath, out PROCESS_INFORMATION processInformation))
                    {
                        return (int)processInformation.ProcessID;
                    }
                }
                else
                {
                    ProcessStartInfo processStartInfo = new ProcessStartInfo()
                    {
                        FileName         = filePath,
                        WindowStyle      = ProcessWindowStyle.Normal,
                        WorkingDirectory = directoryPath,
                        UseShellExecute  = true
                    };

                    Process process = Process.Start(processStartInfo);

                    return process.Id;
                }
            }
            catch
            {
            }

            return 0;
        }

        #endregion
    }
}

 

728x90

 

▶ Program.cs

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

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

        /// <summary>
        /// 프로그램 시작하기
        /// </summary>
        private static void Main()
        {
            string filePath = @"C:\Windows\System32\notepad.exe";

            ProcessHelper.ExecuteProcess(filePath);
        }

        #endregion
    }
}

※ 예제는 콘솔에서 실행했지만 윈도우즈 서비스 실행시 현재 사용자 계정으로 가장해서 실행할 수 있다.

728x90
그리드형(광고전용)
Posted by icodebroker
,