728x90
728x170
▶ 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
그리드형(광고전용)
'C# > Common' 카테고리의 다른 글
[C#/COMMON] MIME 타입 딕셔너리 구하기 (0) | 2021.01.21 |
---|---|
[C#/COMMON] 다른 프로세스(윈도우)에 문자열 보내기 (0) | 2021.01.21 |
[C#/COMMON] Process 클래스 : 메모장 EDIT 윈도우 핸들 구하기 (0) | 2021.01.21 |
[C#/COMMON] 액티브 디렉토리 사용자 계정 여부 구하기 (0) | 2021.01.19 |
[C#/COMMON] 모니터명 구하기 (0) | 2021.01.19 |
[C#/COMMON] 실행 프로세스 여부 구하기 (0) | 2021.01.18 |
[C#/COMMON] 타블렛 사용 여부 및 정보 구하기 (0) | 2021.01.17 |
[C#/COMMON] 포터블 실행 파일(Portable Executable File) 만들기 (0) | 2021.01.14 |
[C#/COMMON] Process 클래스 : 프로세스 파일 경로 리스트 구하기 (0) | 2021.01.13 |
[C#/COMMON] ManagementObjectSearcher 클래스 : 플러그 앤 플레이 장치 조회하기 (0) | 2021.01.13 |