728x90
반응형
728x170
[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
반응형
그리드형(광고전용)
'C# > Common' 카테고리의 다른 글
[C#/COMMON] Thread 클래스 : 특정 사용자 권한으로 스레드 실행하기 (0) | 2021.05.14 |
---|---|
[C#/COMMON] 리소스 풀(Resource Pool) 사용하기 (0) | 2021.05.13 |
[C#/COMMON] Visual Studio 확장 개발하기 (0) | 2021.05.13 |
[C#/COMMON] Process 클래스 : UWP 프로세스 여부 구하기 (0) | 2021.05.13 |
[C#/COMMON] 스푸핑(spoofing) 기법을 사용해 시스템 권한으로 프로세스 실행하기 (0) | 2021.05.12 |
[C#/COMMON] Process 클래스 : Kill 메소드를 사용해 특정 디렉토리 내 프로세스 죽이기 (0) | 2021.05.11 |
[C#/COMMON] Process 클래스 : 메인 모듈 파일 경로 구하기 (0) | 2021.05.11 |
[C#/COMMON] 시간 계수기 사용하기 (0) | 2021.05.11 |
[C#/COMMON] 도메인명/사용자명 구하기 (0) | 2021.05.11 |
[C#/COMMON] UWP 앱 실행하기 (0) | 2021.05.10 |
댓글을 달아 주세요