-
-
Save jborean93/94b3eea93dbca15e60c51247c3a7b399 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$ErrorActionPreference = 'Stop' | |
Add-Type -TypeDefinition @' | |
using Microsoft.Win32.SafeHandles; | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.IO; | |
using System.Linq; | |
using System.Runtime.InteropServices; | |
using System.Text; | |
using System.Threading; | |
namespace Ansible.Windows.Process | |
{ | |
internal class NativeHelpers | |
{ | |
[StructLayout(LayoutKind.Sequential)] | |
public class SECURITY_ATTRIBUTES | |
{ | |
public UInt32 nLength; | |
public IntPtr lpSecurityDescriptor; | |
public bool bInheritHandle = false; | |
public SECURITY_ATTRIBUTES() | |
{ | |
nLength = (UInt32)Marshal.SizeOf(this); | |
} | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
public class STARTUPINFOW | |
{ | |
public UInt32 cb; | |
public IntPtr lpReserved; | |
[MarshalAs(UnmanagedType.LPWStr)] public string lpDesktop; | |
[MarshalAs(UnmanagedType.LPWStr)] public string lpTitle; | |
public UInt32 dwX; | |
public UInt32 dwY; | |
public UInt32 dwXSize; | |
public UInt32 dwYSize; | |
public UInt32 dwXCountChars; | |
public UInt32 dwYCountChars; | |
public UInt32 dwFillAttribute; | |
public StartupInfoFlags dwFlags; | |
public UInt16 wShowWindow; | |
public UInt16 cbReserved2; | |
public IntPtr lpReserved2; | |
public SafeHandle hStdInput = new SafeNativeHandle(IntPtr.Zero, false); | |
public SafeHandle hStdOutput = new SafeNativeHandle(IntPtr.Zero, false); | |
public SafeHandle hStdError = new SafeNativeHandle(IntPtr.Zero, false); | |
public STARTUPINFOW() | |
{ | |
cb = (UInt32)Marshal.SizeOf(this); | |
} | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
public class STARTUPINFOEX | |
{ | |
public STARTUPINFOW startupInfo; | |
public SafeHandle lpAttributeList = new SafeNativeHandle(IntPtr.Zero, false); | |
public STARTUPINFOEX() | |
{ | |
startupInfo = new STARTUPINFOW(); | |
startupInfo.cb = (UInt32)Marshal.SizeOf(this); | |
} | |
} | |
[StructLayout(LayoutKind.Sequential)] | |
public struct PROCESS_INFORMATION | |
{ | |
public IntPtr hProcess; | |
public IntPtr hThread; | |
public int dwProcessId; | |
public int dwThreadId; | |
} | |
[Flags] | |
public enum DuplicateHandleOptions : uint | |
{ | |
NONE = 0x0000000, | |
DUPLICATE_CLOSE_SOURCE = 0x00000001, | |
DUPLICATE_SAME_ACCESS = 0x00000002, | |
} | |
[Flags] | |
public enum StartupInfoFlags : uint | |
{ | |
STARTF_USESHOWWINDOW = 0x00000001, | |
USESTDHANDLES = 0x00000100, | |
} | |
} | |
internal class NativeMethods | |
{ | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern bool CloseHandle( | |
IntPtr hObject); | |
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] | |
public static extern bool CreateProcessW( | |
[MarshalAs(UnmanagedType.LPWStr)] string lpApplicationName, | |
StringBuilder lpCommandLine, | |
SafeMemoryBuffer lpProcessAttributes, | |
SafeMemoryBuffer lpThreadAttributes, | |
bool bInheritHandles, | |
ProcessCreationFlags dwCreationFlags, | |
SafeMemoryBuffer lpEnvironment, | |
[MarshalAs(UnmanagedType.LPWStr)] string lpCurrentDirectory, | |
NativeHelpers.STARTUPINFOEX lpStartupInfo, | |
out NativeHelpers.PROCESS_INFORMATION lpProcessInformation); | |
[DllImport("kernel32.dll")] | |
public static extern void DeleteProcThreadAttributeList( | |
IntPtr lpAttributeList); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern bool DuplicateHandle( | |
SafeHandle hSourceProcessHandle, | |
SafeHandle hSourceHandle, | |
SafeHandle hTargetProcessHandle, | |
out IntPtr lpTargetHandle, | |
UInt32 dwDesiredAccess, | |
bool bInheritHandle, | |
NativeHelpers.DuplicateHandleOptions dwOptions); | |
[DllImport("kernel32.dll")] | |
public static extern IntPtr GetCurrentProcess(); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern bool GetExitCodeProcess( | |
SafeHandle hProcess, | |
out UInt32 lpExitCode); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern bool InitializeProcThreadAttributeList( | |
IntPtr lpAttributeList, | |
Int32 dwAttributeCount, | |
UInt32 dwFlags, | |
ref IntPtr lpSize); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern SafeNativeHandle OpenProcess( | |
Int32 dwDesiredAccess, | |
bool bInheritHandle, | |
Int32 dwProcessId); | |
[DllImport("kernel32.dll", SetLastError = true)] | |
public static extern bool UpdateProcThreadAttribute( | |
SafeHandle lpAttributeList, | |
UInt32 dwFlags, | |
UIntPtr Attribute, | |
SafeHandle lpValue, | |
UIntPtr cbSize, | |
IntPtr lpPreviousValue, | |
IntPtr lpReturnSize); | |
[DllImport("kernel32.dll")] | |
public static extern UInt32 WaitForSingleObject( | |
SafeHandle hHandle, | |
UInt32 dwMilliseconds); | |
} | |
internal class SafeDuplicateHandle : SafeHandleZeroOrMinusOneIsInvalid | |
{ | |
private readonly SafeHandle _process; | |
private readonly bool _ownsHandle; | |
public SafeDuplicateHandle(IntPtr handle, SafeHandle process) : this(handle, process, true) { } | |
public SafeDuplicateHandle(IntPtr handle, SafeHandle process, bool ownsHandle) : base(true) | |
{ | |
SetHandle(handle); | |
_process = process; | |
_ownsHandle = ownsHandle; | |
} | |
protected override bool ReleaseHandle() | |
{ | |
if (_ownsHandle) | |
{ | |
// Cannot pass this SafeHandle object to DuplicateHandle as it | |
// will appeared as closed/invalid already. Use a temporary | |
// SafeHandle that is set not to dispose itself. | |
ProcessUtil.DuplicateHandle( | |
_process, | |
new SafeNativeHandle(handle, false), | |
null, | |
0, | |
false, | |
NativeHelpers.DuplicateHandleOptions.DUPLICATE_CLOSE_SOURCE, | |
false); | |
_process.Dispose(); | |
} | |
return true; | |
} | |
} | |
internal class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid | |
{ | |
public SafeMemoryBuffer() : base(true) { } | |
public SafeMemoryBuffer(int cb) : base(true) | |
{ | |
base.SetHandle(Marshal.AllocHGlobal(cb)); | |
} | |
public SafeMemoryBuffer(IntPtr handle) : base(true) | |
{ | |
base.SetHandle(handle); | |
} | |
protected override bool ReleaseHandle() | |
{ | |
Marshal.FreeHGlobal(handle); | |
return true; | |
} | |
} | |
internal class SafeProcThreadAttribute : SafeHandleZeroOrMinusOneIsInvalid | |
{ | |
internal List<SafeHandle> values = new List<SafeHandle>(); | |
public SafeProcThreadAttribute() : base(true) { } | |
public SafeProcThreadAttribute(IntPtr preexistingHandle, bool ownsHandle) : base(ownsHandle) | |
{ | |
SetHandle(preexistingHandle); | |
} | |
public void AddValue(SafeHandle value) | |
{ | |
values.Add(value); | |
} | |
protected override bool ReleaseHandle() | |
{ | |
foreach (SafeHandle val in values) | |
{ | |
val.Dispose(); | |
} | |
NativeMethods.DeleteProcThreadAttributeList(handle); | |
Marshal.FreeHGlobal(handle); | |
return true; | |
} | |
} | |
[Flags] | |
public enum ProcessCreationFlags : uint | |
{ | |
None = 0x00000000, | |
DebugProcess = 0x00000001, | |
DebugOnlyThisProcess = 0x00000002, | |
CreateSuspended = 0x00000004, | |
DetachedProcess = 0x00000008, | |
CreateNewConsole = 0x00000010, | |
NormalPriorityClass = 0x00000020, | |
IdlePriorityClass = 0x00000040, | |
HighPriorityClass = 0x00000080, | |
RealtimePriorityClass = 0x00000100, | |
CreateNewProcessGroup = 0x00000200, | |
CreateUnicodeEnvironment = 0x00000400, | |
CreateSeparateWowVdm = 0x00000800, | |
CreateSharedWowVdm = 0x00001000, | |
CreateForceDos = 0x00002000, | |
BelowNormalPriorityClass = 0x00004000, | |
AboveNormalPriorityClass = 0x00008000, | |
InheritParentAffinity = 0x00010000, | |
InheritCallerPriority = 0x00020000, | |
CreateProtectedProcess = 0x00040000, | |
ExtendedStartupInfoPresent = 0x00080000, | |
ProcessModeBackgroundBegin = 0x00100000, | |
ProcessModeBackgroundEnd = 0x00200000, | |
CreateSecureProcess = 0x00400000, | |
CreateBreakawayFromJob = 0x01000000, | |
CreatePreserveCodeAuthzLevel = 0x02000000, | |
CreateDefaultErrorMode = 0x04000000, | |
CreateNoWindow = 0x08000000, | |
ProfileUser = 0x10000000, | |
ProfileKernel = 0x20000000, | |
ProfileServer = 0x40000000, | |
CreateIgnoreSystemDefault = 0x80000000, | |
} | |
public class SafeNativeHandle : SafeHandleZeroOrMinusOneIsInvalid | |
{ | |
public SafeNativeHandle() : base(true) { } | |
public SafeNativeHandle(IntPtr handle) : this(handle, true) { } | |
public SafeNativeHandle(IntPtr handle, bool ownsHandle) : base(ownsHandle) { this.handle = handle; } | |
protected override bool ReleaseHandle() | |
{ | |
return NativeMethods.CloseHandle(handle); | |
} | |
} | |
public class Win32Exception : System.ComponentModel.Win32Exception | |
{ | |
private string _msg; | |
public Win32Exception(string message) : this(Marshal.GetLastWin32Error(), message) { } | |
public Win32Exception(int errorCode, string message) : base(errorCode) | |
{ | |
_msg = String.Format("{0} ({1}, Win32ErrorCode {2} - 0x{2:X8})", message, base.Message, errorCode); | |
} | |
public override string Message { get { return _msg; } } | |
public static explicit operator Win32Exception(string message) { return new Win32Exception(message); } | |
} | |
public class ProcessInformation : IDisposable | |
{ | |
public SafeNativeHandle Process { get; internal set; } | |
public SafeNativeHandle Thread { get; internal set; } | |
public int ProcessId { get; internal set; } | |
public int ThreadId { get; internal set; } | |
public void Dispose() | |
{ | |
if (Process != null) | |
Process.Dispose(); | |
if (Thread != null) | |
Thread.Dispose(); | |
GC.SuppressFinalize(this); | |
} | |
~ProcessInformation() { Dispose(); } | |
} | |
public class SecurityAttributes | |
{ | |
public bool InheritHandle { get; set; } | |
// TODO: Support SecurityDescriptor at some point. | |
// Should it use RawSecurityDescriptor or create a Process SD class that inherits NativeObjectSecurity? | |
} | |
public class StartupInfo | |
{ | |
public string Desktop { get; set; } | |
public string Title { get; set; } | |
public ProcessWindowStyle? WindowStyle { get; set; } | |
public SafeHandle StandardInput { get; set; } | |
public SafeHandle StandardOutput { get; set; } | |
public SafeHandle StandardError { get; set; } | |
public int ParentProcess { get; set; } | |
// TODO: Support PROC_THREAD_ATTRIBUTE_HANDLE_LIST | |
} | |
public class ProcessUtil | |
{ | |
/// <summary> | |
/// Wrapper around the Win32 CreateProcess API for low level use. This just spawns the new process and does not | |
/// wait until it is complete before returning. | |
/// </summary> | |
/// <param name="applicationName">The name of the executable or batch file to execute</param> | |
/// <param name="commandLine">The command line to execute, typically this includes applicationName as the first argument</param> | |
/// <param name="processAttributes">SecurityAttributes to assign to the new process, set to null to use the defaults</param> | |
/// <param name="threadAttributes">SecurityAttributes to assign to the new thread, set to null to use the defaults</param> | |
/// <param name="inheritHandles">Any inheritable handles in the calling process is inherited in the new process</param> | |
/// <param name="creationFlags">Custom creation flags to use when creating the new process</param> | |
/// <param name="environment">A dictionary of key/value pairs to define the new process environment</param> | |
/// <param name="currentDirectory">The full path to the current directory for the process, null will have the same cwd as the calling process</param> | |
/// <param name="startupInfo">Custom StartupInformation to use when creating the new process</param> | |
/// <returns>ProcessInformation containing a handle to the process and main thread as well as the pid/tid.</returns> | |
public static ProcessInformation NativeCreateProcess(string applicationName, string commandLine, | |
SecurityAttributes processAttributes, SecurityAttributes threadAttributes, bool inheritHandles, | |
ProcessCreationFlags creationFlags, IDictionary environment, string currentDirectory, StartupInfo startupInfo) | |
{ | |
// We always have the extended version present. | |
creationFlags |= ProcessCreationFlags.ExtendedStartupInfoPresent; | |
// $null from PowerShell ends up as an empty string, we need to convert back as an empty string doesn't | |
// make sense for these parameters | |
if (String.IsNullOrWhiteSpace(applicationName)) | |
applicationName = null; | |
if (String.IsNullOrWhiteSpace(currentDirectory)) | |
currentDirectory = null; | |
NativeHelpers.STARTUPINFOEX si = new NativeHelpers.STARTUPINFOEX(); | |
if (!String.IsNullOrWhiteSpace(startupInfo.Desktop)) | |
si.startupInfo.lpDesktop = startupInfo.Desktop; | |
if (!String.IsNullOrWhiteSpace(startupInfo.Title)) | |
si.startupInfo.lpTitle = startupInfo.Title; | |
if (startupInfo.WindowStyle != null) | |
{ | |
switch (startupInfo.WindowStyle) | |
{ | |
case ProcessWindowStyle.Normal: | |
si.startupInfo.wShowWindow = 1; // SW_SHOWNORMAL | |
break; | |
case ProcessWindowStyle.Hidden: | |
si.startupInfo.wShowWindow = 0; // SW_HIDE | |
break; | |
case ProcessWindowStyle.Minimized: | |
si.startupInfo.wShowWindow = 6; // SW_MINIMIZE | |
break; | |
case ProcessWindowStyle.Maximized: | |
si.startupInfo.wShowWindow = 3; // SW_MAXIMIZE | |
break; | |
} | |
si.startupInfo.dwFlags |= NativeHelpers.StartupInfoFlags.STARTF_USESHOWWINDOW; | |
} | |
si.lpAttributeList = CreateProcThreadAttributes(startupInfo); | |
NativeHelpers.PROCESS_INFORMATION pi = new NativeHelpers.PROCESS_INFORMATION(); | |
using (SafeHandle stdinHandle = PrepareStdioHandle(startupInfo.StandardInput, startupInfo)) | |
using (SafeHandle stdoutHandle = PrepareStdioHandle(startupInfo.StandardOutput, startupInfo)) | |
using (SafeHandle stderrHandle = PrepareStdioHandle(startupInfo.StandardError, startupInfo)) | |
using (SafeMemoryBuffer lpProcessAttr = CreateSecurityAttributes(processAttributes)) | |
using (SafeMemoryBuffer lpThreadAttributes = CreateSecurityAttributes(threadAttributes)) | |
using (SafeMemoryBuffer lpEnvironment = CreateEnvironmentPointer(environment)) | |
{ | |
si.startupInfo.hStdInput = stdinHandle; | |
si.startupInfo.hStdOutput = stdoutHandle; | |
si.startupInfo.hStdError = stderrHandle; | |
if ( | |
si.startupInfo.hStdInput.DangerousGetHandle() != IntPtr.Zero || | |
si.startupInfo.hStdOutput.DangerousGetHandle() != IntPtr.Zero || | |
si.startupInfo.hStdError.DangerousGetHandle() != IntPtr.Zero | |
) | |
{ | |
si.startupInfo.dwFlags |= NativeHelpers.StartupInfoFlags.USESTDHANDLES; | |
} | |
StringBuilder commandLineBuff = new StringBuilder(commandLine); | |
if (!NativeMethods.CreateProcessW(applicationName, commandLineBuff, lpProcessAttr, lpThreadAttributes, | |
inheritHandles, creationFlags, lpEnvironment, currentDirectory, si, out pi)) | |
{ | |
throw new Win32Exception("CreateProcessW() failed"); | |
} | |
} | |
return new ProcessInformation | |
{ | |
Process = new SafeNativeHandle(pi.hProcess), | |
Thread = new SafeNativeHandle(pi.hThread), | |
ProcessId = pi.dwProcessId, | |
ThreadId = pi.dwThreadId, | |
}; | |
} | |
/// <summary> | |
/// Gets the exit code for the specified process handle. | |
/// </summary> | |
/// <param name="processHandle">The process handle to get the exit code for.</param> | |
/// <returns>The process exit code.</returns> | |
public static UInt32 GetProcessExitCode(SafeHandle processHandle) | |
{ | |
NativeMethods.WaitForSingleObject(processHandle, 0xFFFFFFFF); | |
UInt32 exitCode; | |
if (!NativeMethods.GetExitCodeProcess(processHandle, out exitCode)) | |
throw new Win32Exception("GetExitCodeProcess() failed"); | |
return exitCode; | |
} | |
internal static SafeMemoryBuffer CreateEnvironmentPointer(IDictionary environment) | |
{ | |
IntPtr lpEnvironment = IntPtr.Zero; | |
if (environment != null && environment.Count > 0) | |
{ | |
StringBuilder environmentString = new StringBuilder(); | |
foreach (DictionaryEntry kv in environment) | |
environmentString.AppendFormat("{0}={1}\0", kv.Key, kv.Value); | |
environmentString.Append('\0'); | |
lpEnvironment = Marshal.StringToHGlobalUni(environmentString.ToString()); | |
} | |
return new SafeMemoryBuffer(lpEnvironment); | |
} | |
internal static SafeMemoryBuffer CreateSecurityAttributes(SecurityAttributes attributes) | |
{ | |
IntPtr lpAttributes = IntPtr.Zero; | |
if (attributes != null) | |
{ | |
NativeHelpers.SECURITY_ATTRIBUTES attr = new NativeHelpers.SECURITY_ATTRIBUTES() | |
{ | |
bInheritHandle = attributes.InheritHandle, | |
}; | |
lpAttributes = Marshal.AllocHGlobal(Marshal.SizeOf(attr)); | |
Marshal.StructureToPtr(attr, lpAttributes, false); | |
} | |
return new SafeMemoryBuffer(lpAttributes); | |
} | |
internal static SafeDuplicateHandle DuplicateHandle(SafeHandle sourceProcess, SafeHandle sourceHandle, | |
SafeHandle targetProcess, UInt32 access, bool inherit, NativeHelpers.DuplicateHandleOptions options, | |
bool ownsHandle) | |
{ | |
if (targetProcess == null) | |
{ | |
targetProcess = new SafeNativeHandle(IntPtr.Zero, false); | |
// If closing the duplicate then mark the returned handle so it doesn't try to close itself again. | |
ownsHandle = (options & NativeHelpers.DuplicateHandleOptions.DUPLICATE_CLOSE_SOURCE) == 0; | |
} | |
IntPtr dup = IntPtr.Zero; | |
if (!NativeMethods.DuplicateHandle(sourceProcess, sourceHandle, targetProcess, out dup, access, | |
inherit, options)) | |
{ | |
throw new Win32Exception("DuplicateHandle() failed"); | |
} | |
return new SafeDuplicateHandle(dup, targetProcess, ownsHandle); | |
} | |
private static SafeHandle CreateProcThreadAttributes(StartupInfo startupInfo) | |
{ | |
int count = 0; | |
if (startupInfo.ParentProcess > 0) | |
{ | |
count++; | |
} | |
if (count == 0) | |
{ | |
return new SafeNativeHandle(IntPtr.Zero, false); | |
} | |
SafeProcThreadAttribute attr = InitializeProcThreadAttributeList(count); | |
try | |
{ | |
if (startupInfo.ParentProcess > 0) | |
{ | |
SafeNativeHandle parentProcess = OpenProcess(startupInfo.ParentProcess, | |
0x00000080, // PROCESS_CREATE_PROCESS | |
false); | |
attr.AddValue(parentProcess); | |
SafeMemoryBuffer val = new SafeMemoryBuffer(IntPtr.Size); | |
attr.AddValue(val); | |
Marshal.WriteIntPtr(val.DangerousGetHandle(), parentProcess.DangerousGetHandle()); | |
UpdateProcThreadAttribute(attr, | |
0x00020000, // PROC_THREAD_ATTRIBUTE_PARENT_PROCESS | |
val, | |
IntPtr.Size); | |
} | |
} | |
catch | |
{ | |
attr.Dispose(); | |
throw; | |
} | |
return attr; | |
} | |
private static SafeProcThreadAttribute InitializeProcThreadAttributeList(int count) | |
{ | |
IntPtr size = IntPtr.Zero; | |
NativeMethods.InitializeProcThreadAttributeList(IntPtr.Zero, count, 0, ref size); | |
IntPtr h = Marshal.AllocHGlobal((int)size); | |
try | |
{ | |
if (!NativeMethods.InitializeProcThreadAttributeList(h, count, 0, ref size)) | |
throw new Win32Exception("Failed to create process thread attribute list"); | |
return new SafeProcThreadAttribute(h, true); | |
} | |
catch | |
{ | |
Marshal.FreeHGlobal(h); | |
throw; | |
} | |
} | |
private static SafeNativeHandle OpenProcess(int processId, int access, bool inherit) | |
{ | |
SafeNativeHandle proc = NativeMethods.OpenProcess(access, inherit, processId); | |
if (proc.DangerousGetHandle() == IntPtr.Zero) | |
{ | |
throw new Win32Exception(string.Format( | |
"OpenProcess(0x{0:X8}, {1}, {2}) failed", | |
access, inherit, processId)); | |
} | |
return proc; | |
} | |
private static SafeHandle PrepareStdioHandle(SafeHandle handle, StartupInfo startupInfo) | |
{ | |
if (handle == null || handle.DangerousGetHandle() == IntPtr.Zero) | |
return new SafeNativeHandle(IntPtr.Zero, false); | |
if (startupInfo.ParentProcess > 0) | |
{ | |
// The handle needs to be duplicated into the target process so | |
// it can be inherited. | |
SafeNativeHandle currentProcess = new SafeNativeHandle(NativeMethods.GetCurrentProcess(), false); | |
SafeNativeHandle targetProcess = OpenProcess(startupInfo.ParentProcess, | |
0x00000040, // PROCESS_DUP_HANDLE | |
false); | |
return DuplicateHandle(currentProcess, handle, targetProcess, 0, true, | |
NativeHelpers.DuplicateHandleOptions.DUPLICATE_SAME_ACCESS, true); | |
} | |
else | |
{ | |
// Create a copy of the handle and ensure it won't be disposed. | |
// The original owner is still in charge of it. | |
return new SafeNativeHandle(handle.DangerousGetHandle(), false); | |
} | |
} | |
private static void UpdateProcThreadAttribute(SafeProcThreadAttribute attributeList, int attr, | |
SafeHandle value, int size) | |
{ | |
if (!NativeMethods.UpdateProcThreadAttribute(attributeList, 0, (UIntPtr)attr, value, (UIntPtr)size, | |
IntPtr.Zero, IntPtr.Zero)) | |
{ | |
throw new Win32Exception("UpdateProcThreadAttribute() failed"); | |
} | |
attributeList.AddValue(value); | |
} | |
} | |
} | |
'@ | |
$applicationName = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe" | |
$commandLine = "`"$applicationName`" -Command exit 1" | |
$parentProc = $procInfo = $null | |
try { | |
# -UseNewEnvironment forces PowerShell to use CreateProcess internally like Ansible | |
# Remove that parameter if the script is also having issues. | |
$parentProc = Start-Process -FilePath $applicationName -UseNewEnvironment -PassThru | |
$startupInfo = [Ansible.Windows.Process.StartupInfo]@{ | |
ParentProcess = $parentProc.Id | |
} | |
$procInfo = [Ansible.Windows.Process.ProcessUtil]::NativeCreateProcess( | |
$applicationName, | |
$commandLine, | |
$null, | |
$null, | |
$false, | |
[Ansible.Windows.Process.ProcessCreationFlags]::CreateNewConsole, | |
$null, | |
$null, | |
$startupInfo | |
) | |
$rc = [Ansible.Windows.Process.ProcessUtil]::GetProcessExitCode($procInfo.Process) | |
Write-Host "Done, exited with rc $rc" | |
} | |
finally { | |
if ($procInfo) { $procInfo.Dispose() } | |
if ($parentProc) { $parentProc | Stop-Process -Force -ErrorAction SilentlyContinue } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment