예를 들어
C:\Windows\system32\notepad.exe
위 full path 를 DB에서 추출하여 실행하는 겁니다.
아래 파일 class ProgramInfo 의 start() 를 실행시 (new ProgramInfo().Start();
아래와 같은 예외오류가 납니다. ㅠㅠ ProcessExtensions.cs 는 제 능력밖의 영역입니다.
어떻게 왜 예외가 발생했는지 알고 싶습니다.
activeSessionId: 1 <<<<< hImpersonationToken: 0 >>>>> bResult: False
bResult: False <<<<<
Unhandled exception. System.Exception: StartProcessAsCurrentUser: GetSessionUserToken failed.
at Test.ProcessExtensions.StartProcessAsCurrentUser(String appPath, String cmdLine, PROCESS_INFORMATION& procInfo) in D:__NET\test\ProcessExtensions.cs:line 309
at Test.ProgramInfo.StartWindow() in D:__NET\test\ProgramInfo.cs:line 53
at Test.ProgramInfo.Start() in D:__NET\test\ProgramInfo.cs:line 42
at Test.Program.Main(String args) in D:__NET\test\Program.cs:line 23
PS D:__NET\test>
/// ProgramInfo.cs
using System;
using System.Linq;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
using System.Diagnostics;
namespace Test
{
internal class ProgramInfo
{
public bool IsPaused { get; set; }
public bool IsProcessAlive
{
get
{
uint dwProcessId = pProcessInfo.dwProcessId;
if (dwProcessId != 0)
{
return ProcessExtensions.IsProcessRunning(dwProcessId);
}
return false;
}
}
ProcessExtensions.PROCESS_INFORMATION pProcessInfo = new ProcessExtensions.PROCESS_INFORMATION();
public ProgramInfo()
{
pProcessInfo.hProcess = IntPtr.Zero;
pProcessInfo.hThread = IntPtr.Zero;
pProcessInfo.dwProcessId = 0;
pProcessInfo.dwThreadId = 0;
}
public void Start()
{
StartWindow();
}
public int StartWindow()
{
if (IsProcessAlive)
return -2;
string exePath = "C:\\Windows\\system32\\notepad.exe";
string exeParam = "";
if (ProcessExtensions.StartProcessAsCurrentUser(exePath, exeParam, ref pProcessInfo))
{
IsPaused = false;
return -1;
}
return 0;
}
}
}
// / ProcessExtension.cs
using System;
using System.Diagnostics;
using System.Security;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace Test
{
public static class ProcessExtensions
{
#region Win32 Constants
private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const int CREATE_NO_WINDOW = 0x08000000;
private const int CREATE_NEW_CONSOLE = 0x00000010;
public const uint MAXIMUM_ALLOWED = 0x2000000;
private const uint INVALID_SESSION_ID = 0xFFFFFFFF;
private static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
private const uint INFINITE = 0xFFFFFFFF;
private const uint WAIT_ABANDONED = 0x00000080;
private const uint WAIT_OBJECT_0 = 0x00000000;
private const uint WAIT_TIMEOUT = 0x00000102;
#endregion
#region DllImports
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
private static extern bool CreateProcessAsUser(
IntPtr hToken,
String lpApplicationName,
String lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandle,
uint dwCreationFlags,
IntPtr lpEnvironment,
String lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation
);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
private static extern bool DuplicateTokenEx(
IntPtr ExistingTokenHandle,
uint dwDesiredAccess,
IntPtr lpThreadAttributes,
int TokenType,
int ImpersonationLevel,
ref IntPtr DuplicateTokenHandle
);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
[DllImport("userenv.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hSnapshot);
[DllImport("kernel32.dll")]
private static extern uint WTSGetActiveConsoleSessionId();
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr OpenProcess(uint Access, bool InheritHandle, uint ProcessId);
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CreateProcess(
string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags,
IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll")]
public static extern uint TerminateProcess(IntPtr hProcess, uint ExitCode);
[DllImport("kernel32.dll", SetLastError=true)]
static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
[DllImport("Wtsapi32.dll")]
private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);
[DllImport("wtsapi32.dll", SetLastError = true)]
private static extern int WTSEnumerateSessions(
IntPtr hServer,
int Reserved,
int Version,
ref IntPtr ppSessionInfo,
ref int pCount
);
#endregion
#region Win32 Structs
private enum SW
{
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_MAX = 10
}
private enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
public enum ProcessAccessRights
{
PROCESS_CREATE_PROCESS = 0x0080, // Required to create a process.
PROCESS_CREATE_THREAD = 0x0002, // Required to create a thread.
PROCESS_DUP_HANDLE = 0x0040, // Required to duplicate a handle using DuplicateHandle.
PROCESS_QUERY_INFORMATION = 0x0400, // Required to retrieve certain information about a process, such as its token, exit code, and priority class (see OpenProcessToken, GetExitCodeProcess, GetPriorityClass, and IsProcessInJob).
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, // Required to retrieve certain information about a process (see QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted PROCESS_QUERY_LIMITED_INFORMATION. Windows Server 2003 and Windows XP/2000: This access right is not supported.
PROCESS_SET_INFORMATION = 0x0200, // Required to set certain information about a process, such as its priority class (see SetPriorityClass).
PROCESS_SET_QUOTA = 0x0100, // Required to set memory limits using SetProcessWorkingSetSize.
PROCESS_SUSPEND_RESUME = 0x0800, // Required to suspend or resume a process.
PROCESS_TERMINATE = 0x0001, // Required to terminate a process using TerminateProcess.
PROCESS_VM_OPERATION = 0x0008, // Required to perform an operation on the address space of a process (see VirtualProtectEx and WriteProcessMemory).
PROCESS_VM_READ = 0x0010, // Required to read memory in a process using ReadProcessMemory.
PROCESS_VM_WRITE = 0x0020, // Required to write to memory in a process using WriteProcessMemory.
DELETE = 0x00010000, // Required to delete the object.
READ_CONTROL = 0x00020000, // Required to read information in the security descriptor for the object, not including the information in the SACL. To read or write the SACL, you must request the ACCESS_SYSTEM_SECURITY access right. For more information, see SACL Access Right.
SYNCHRONIZE = 0x00100000, // The right to use the object for synchronization. This enables a thread to wait until the object is in the signaled state.
WRITE_DAC = 0x00040000, // Required to modify the DACL in the security descriptor for the object.
WRITE_OWNER = 0x00080000, // Required to change the owner in the security descriptor for the object.
STANDARD_RIGHTS_REQUIRED = 0x000f0000,
PROCESS_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF),// All possible access rights for a process object.
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
private enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3,
}
[StructLayout(LayoutKind.Sequential)]
private struct STARTUPINFO
{
public int cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
private enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation = 2
}
[StructLayout(LayoutKind.Sequential)]
private struct WTS_SESSION_INFO
{
public readonly UInt32 SessionID;
[MarshalAs(UnmanagedType.LPStr)]
public readonly String pWinStationName;
public readonly WTS_CONNECTSTATE_CLASS State;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public uint nLength;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
#endregion
// Gets the user token from the currently active session
private static bool GetSessionUserToken(ref IntPtr phUserToken)
{
var bResult = false;
var hImpersonationToken = IntPtr.Zero;
var activeSessionId = INVALID_SESSION_ID;
var pSessionInfo = IntPtr.Zero;
var sessionCount = 0;
// Get a handle to the user access token for the current active session.
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref sessionCount) != 0)
{
var arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
IntPtr current = (IntPtr)pSessionInfo;
for (var i = 0; i < sessionCount; i++)
{
var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
current += arrayElementSize;
if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive)
{
activeSessionId = si.SessionID;
}
}
}
// If enumerating did not work, fall back to the old method
if (activeSessionId == INVALID_SESSION_ID)
{
activeSessionId = WTSGetActiveConsoleSessionId();
}
Console.WriteLine(" >>>>> activeSessionId: " + activeSessionId + " <<<<< hImpersonationToken: " + hImpersonationToken + " >>>>> bResult: " + bResult);
if (WTSQueryUserToken(activeSessionId, ref hImpersonationToken) != 0)
{
Console.WriteLine(">>>>> hImpersonationToken: " + hImpersonationToken);
// Convert the impersonation token to a primary token
bResult = DuplicateTokenEx(hImpersonationToken, 0, IntPtr.Zero,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary,
ref phUserToken);
CloseHandle(hImpersonationToken);
}
Console.WriteLine(">>>>> bResult: " + bResult + " <<<<<");
return bResult;
}
internal static bool StartProcessAsCurrentUser(string appPath, string cmdLine, ref PROCESS_INFORMATION procInfo)
{
string workDir = null;
bool visible = true;
var hUserToken = IntPtr.Zero;
var startInfo = new STARTUPINFO();
//var procInfo = new PROCESS_INFORMATION();
var pEnv = IntPtr.Zero;
int iResultOfCreateProcessAsUser;
startInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));
try
{
if (!GetSessionUserToken(ref hUserToken))
{
throw new Exception("StartProcessAsCurrentUser: GetSessionUserToken failed.");
}
uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE);
startInfo.lpDesktop = "winsta0\\default";
if (!CreateEnvironmentBlock(ref pEnv, hUserToken, false))
{
throw new Exception("StartProcessAsCurrentUser: CreateEnvironmentBlock failed.");
}
if (!CreateProcessAsUser(hUserToken,
appPath, // Application Name
cmdLine, // Command Line
IntPtr.Zero,
IntPtr.Zero,
false,
dwCreationFlags,
pEnv,
workDir, // Working directory
ref startInfo,
out procInfo))
{
throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed.");
}
iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
}
finally
{
CloseHandle(hUserToken);
if (pEnv != IntPtr.Zero)
{
DestroyEnvironmentBlock(pEnv);
}
CloseHandle(procInfo.hThread);
CloseHandle(procInfo.hProcess);
}
return true;
}
internal static bool StartProcessAsConsole(string appPath, string cmdLine, ref PROCESS_INFORMATION procInfo)
{
var startInfo = new STARTUPINFOEX();
startInfo.StartupInfo.cb = Marshal.SizeOf(typeof(STARTUPINFOEX));
var pSec = new SECURITY_ATTRIBUTES();
var tSec = new SECURITY_ATTRIBUTES();
pSec.nLength = (uint)Marshal.SizeOf(pSec);
tSec.nLength = (uint)Marshal.SizeOf(tSec);
try
{
if (!CreateProcess(null, appPath, ref pSec, ref tSec, true, CREATE_NO_WINDOW, IntPtr.Zero, null, ref startInfo, out procInfo))
{
throw new Exception("CreateProcess failed.");
}
}
finally
{
CloseHandle(procInfo.hThread);
CloseHandle(procInfo.hProcess);
}
return true;
}
public static void StopProcess(uint pid)
{
try
{
// uint dwThreadId = procInfo.dwThreadId;
// uint dwProcessId = procInfo.dwProcessId;
IntPtr hProcess = OpenProcess((uint)ProcessAccessRights.PROCESS_TERMINATE, false, (uint)pid);
if (hProcess != IntPtr.Zero)
{
// Kill the process
TerminateProcess(hProcess, 9);
CloseHandle(hProcess);
}
else
{
//_logger.warning("Could not OpenProcess.");
throw new Exception("Could not OpenProcess.");
}
}
catch (Exception ex)
{
throw ex;
// _logger.error("unknown error in Stop " + m_strName);
}
}
static public bool IsProcessRunning(uint pid)
{
IntPtr process = OpenProcess(MAXIMUM_ALLOWED, false, pid); // 원본 : SYNCHRONIZE
uint ret = WaitForSingleObject(process, 0);
CloseHandle(process);
// _logger.info("WaitForSingleObject={0}", ret);
return ret == WAIT_TIMEOUT;
}
}
}