2020-02-17 00:25:25 +01:00
using System ;
using System.ComponentModel ;
using System.Diagnostics ;
using System.Runtime.InteropServices ;
namespace OpenSee {
// Based on https://github.com/josh4364/IL2cppStartProcess/blob/master/StartExternalProcess.cs
public class OpenSeeProcessInterface {
#region DllImport
2020-02-17 20:16:06 +01:00
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Auto)]
private static extern IntPtr CreateFile (
string lpFileName ,
EFileAccess dwDesiredAccess ,
EFileShare dwShareMode ,
SECURITY_ATTRIBUTES lpSecurityAttributes ,
ECreationDisposition dwCreationDisposition ,
EFileAttributes dwFlagsAndAttributes ,
IntPtr hTemplateFile
) ;
[StructLayout(LayoutKind.Sequential)]
private struct SECURITY_ATTRIBUTES
{
public int nLength ;
public IntPtr lpSecurityDescriptor ;
public int bInheritHandle ;
}
[Flags]
enum EFileAccess : uint {
//
// Standart Section
//
AccessSystemSecurity = 0x1000000 , // AccessSystemAcl access type
MaximumAllowed = 0x2000000 , // MaximumAllowed access type
Delete = 0x10000 ,
ReadControl = 0x20000 ,
WriteDAC = 0x40000 ,
WriteOwner = 0x80000 ,
Synchronize = 0x100000 ,
StandardRightsRequired = 0xF0000 ,
StandardRightsRead = ReadControl ,
StandardRightsWrite = ReadControl ,
StandardRightsExecute = ReadControl ,
StandardRightsAll = 0x1F0000 ,
SpecificRightsAll = 0xFFFF ,
FILE_READ_DATA = 0x0001 , // file & pipe
FILE_LIST_DIRECTORY = 0x0001 , // directory
FILE_WRITE_DATA = 0x0002 , // file & pipe
FILE_ADD_FILE = 0x0002 , // directory
FILE_APPEND_DATA = 0x0004 , // file
FILE_ADD_SUBDIRECTORY = 0x0004 , // directory
FILE_CREATE_PIPE_INSTANCE = 0x0004 , // named pipe
FILE_READ_EA = 0x0008 , // file & directory
FILE_WRITE_EA = 0x0010 , // file & directory
FILE_EXECUTE = 0x0020 , // file
FILE_TRAVERSE = 0x0020 , // directory
FILE_DELETE_CHILD = 0x0040 , // directory
FILE_READ_ATTRIBUTES = 0x0080 , // all
FILE_WRITE_ATTRIBUTES = 0x0100 , // all
//
// Generic Section
//
GenericRead = 0x80000000 ,
GenericWrite = 0x40000000 ,
GenericExecute = 0x20000000 ,
GenericAll = 0x10000000 ,
SPECIFIC_RIGHTS_ALL = 0x00FFFF ,
FILE_ALL_ACCESS =
StandardRightsRequired |
Synchronize |
0x1FF ,
FILE_GENERIC_READ =
StandardRightsRead |
FILE_READ_DATA |
FILE_READ_ATTRIBUTES |
FILE_READ_EA |
Synchronize ,
FILE_GENERIC_WRITE =
StandardRightsWrite |
FILE_WRITE_DATA |
FILE_WRITE_ATTRIBUTES |
FILE_WRITE_EA |
FILE_APPEND_DATA |
Synchronize ,
FILE_GENERIC_EXECUTE =
StandardRightsExecute |
FILE_READ_ATTRIBUTES |
FILE_EXECUTE |
Synchronize
}
[Flags]
public enum EFileShare : uint
{
/// <summary>
///
/// </summary>
None = 0x00000000 ,
/// <summary>
/// Enables subsequent open operations on an object to request read access.
/// Otherwise, other processes cannot open the object if they request read access.
/// If this flag is not specified, but the object has been opened for read access, the function fails.
/// </summary>
Read = 0x00000001 ,
/// <summary>
/// Enables subsequent open operations on an object to request write access.
/// Otherwise, other processes cannot open the object if they request write access.
/// If this flag is not specified, but the object has been opened for write access, the function fails.
/// </summary>
Write = 0x00000002 ,
/// <summary>
/// Enables subsequent open operations on an object to request delete access.
/// Otherwise, other processes cannot open the object if they request delete access.
/// If this flag is not specified, but the object has been opened for delete access, the function fails.
/// </summary>
Delete = 0x00000004
}
public enum ECreationDisposition : uint
{
/// <summary>
/// Creates a new file. The function fails if a specified file exists.
/// </summary>
New = 1 ,
/// <summary>
/// Creates a new file, always.
/// If a file exists, the function overwrites the file, clears the existing attributes, combines the specified file attributes,
/// and flags with FILE_ATTRIBUTE_ARCHIVE, but does not set the security descriptor that the SECURITY_ATTRIBUTES structure specifies.
/// </summary>
CreateAlways = 2 ,
/// <summary>
/// Opens a file. The function fails if the file does not exist.
/// </summary>
OpenExisting = 3 ,
/// <summary>
/// Opens a file, always.
/// If a file does not exist, the function creates a file as if dwCreationDisposition is CREATE_NEW.
/// </summary>
OpenAlways = 4 ,
/// <summary>
/// Opens a file and truncates it so that its size is 0 (zero) bytes. The function fails if the file does not exist.
/// The calling process must open the file with the GENERIC_WRITE access right.
/// </summary>
TruncateExisting = 5
}
[Flags]
public enum EFileAttributes : uint
{
Readonly = 0x00000001 ,
Hidden = 0x00000002 ,
System = 0x00000004 ,
Directory = 0x00000010 ,
Archive = 0x00000020 ,
Device = 0x00000040 ,
Normal = 0x00000080 ,
Temporary = 0x00000100 ,
SparseFile = 0x00000200 ,
ReparsePoint = 0x00000400 ,
Compressed = 0x00000800 ,
Offline = 0x00001000 ,
NotContentIndexed = 0x00002000 ,
Encrypted = 0x00004000 ,
Write_Through = 0x80000000 ,
Overlapped = 0x40000000 ,
NoBuffering = 0x20000000 ,
RandomAccess = 0x10000000 ,
SequentialScan = 0x08000000 ,
DeleteOnClose = 0x04000000 ,
BackupSemantics = 0x02000000 ,
PosixSemantics = 0x01000000 ,
OpenReparsePoint = 0x00200000 ,
OpenNoRecall = 0x00100000 ,
FirstPipeInstance = 0x00080000
}
2020-02-17 00:25:25 +01:00
[DllImport("kernel32.dll", SetLastError=true)]
private static extern UInt32 WaitForSingleObject ( IntPtr hHandle , UInt32 dwMilliseconds ) ;
const UInt32 INFINITE = 0xFFFFFFFF ;
const UInt32 WAIT_ABANDONED = 0x00000080 ;
const UInt32 WAIT_OBJECT_0 = 0x00000000 ;
const UInt32 WAIT_TIMEOUT = 0x00000102 ;
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CreateProcessW (
string lpApplicationName ,
[In] string lpCommandLine ,
IntPtr procSecAttrs ,
IntPtr threadSecAttrs ,
bool bInheritHandles ,
ProcessCreationFlags dwCreationFlags ,
IntPtr lpEnvironment ,
string lpCurrentDirectory ,
ref STARTUPINFO lpStartupInfo ,
ref PROCESS_INFORMATION lpProcessInformation
) ;
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle ( IntPtr hObject ) ;
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool TerminateProcess ( IntPtr processHandle , uint exitCode ) ;
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess ( ProcessAccessRights access , bool inherit , uint processId ) ;
[Flags]
private enum ProcessAccessRights : uint
{
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)]
private struct PROCESS_INFORMATION
{
internal IntPtr hProcess ;
internal IntPtr hThread ;
internal uint dwProcessId ;
internal uint dwThreadId ;
}
[StructLayout(LayoutKind.Sequential)]
private struct STARTUPINFO
{
internal uint cb ;
internal IntPtr lpReserved ;
internal IntPtr lpDesktop ;
internal IntPtr lpTitle ;
internal uint dwX ;
internal uint dwY ;
internal uint dwXSize ;
internal uint dwYSize ;
internal uint dwXCountChars ;
internal uint dwYCountChars ;
internal uint dwFillAttribute ;
internal uint dwFlags ;
internal ushort wShowWindow ;
internal ushort cbReserved2 ;
internal IntPtr lpReserved2 ;
internal IntPtr hStdInput ;
internal IntPtr hStdOutput ;
internal IntPtr hStdError ;
}
[Flags]
private enum ProcessCreationFlags : uint
{
NONE = 0 ,
CREATE_BREAKAWAY_FROM_JOB = 0x01000000 ,
CREATE_DEFAULT_ERROR_MODE = 0x04000000 ,
CREATE_NEW_CONSOLE = 0x00000010 ,
CREATE_NEW_PROCESS_GROUP = 0x00000200 ,
CREATE_NO_WINDOW = 0x08000000 ,
CREATE_PROTECTED_PROCESS = 0x00040000 ,
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000 ,
CREATE_SECURE_PROCESS = 0x00400000 ,
CREATE_SEPARATE_WOW_VDM = 0x00000800 ,
CREATE_SHARED_WOW_VDM = 0x00001000 ,
CREATE_SUSPENDED = 0x00000004 ,
CREATE_UNICODE_ENVIRONMENT = 0x00000400 ,
DEBUG_ONLY_THIS_PROCESS = 0x00000002 ,
DEBUG_PROCESS = 0x00000001 ,
DETACHED_PROCESS = 0x00000008 ,
EXTENDED_STARTUPINFO_PRESENT = 0x00080000 ,
INHERIT_PARENT_AFFINITY = 0x00010000
}
#endregion
2020-02-17 20:16:06 +01:00
public static uint Start ( string exePath , string commandline , string dir , bool hidden , string outputLog , string errorLog , out IntPtr handle , out IntPtr handleStdOut , out IntPtr handleStdErr )
2020-02-17 00:25:25 +01:00
{
2020-02-17 20:16:06 +01:00
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES ( ) ;
sa . nLength = Marshal . SizeOf < SECURITY_ATTRIBUTES > ( ) ;
sa . lpSecurityDescriptor = IntPtr . Zero ;
sa . bInheritHandle = 1 ;
2020-02-17 00:25:25 +01:00
handle = IntPtr . Zero ;
2020-02-17 20:16:06 +01:00
handleStdOut = IntPtr . Zero ;
handleStdErr = IntPtr . Zero ;
if ( outputLog ! = null )
handleStdOut = CreateFile ( outputLog , EFileAccess . FILE_WRITE_DATA , EFileShare . Read | EFileShare . Write , sa , ECreationDisposition . CreateAlways , EFileAttributes . Normal , IntPtr . Zero ) ;
if ( errorLog ! = null )
handleStdErr = CreateFile ( errorLog , EFileAccess . FILE_WRITE_DATA , EFileShare . Read | EFileShare . Write , sa , ECreationDisposition . CreateAlways , EFileAttributes . Normal , IntPtr . Zero ) ;
2020-02-17 00:25:25 +01:00
ProcessCreationFlags flags = hidden ? ProcessCreationFlags . CREATE_NO_WINDOW : ProcessCreationFlags . NONE ;
STARTUPINFO startupinfo = new STARTUPINFO
{
2020-02-17 20:16:06 +01:00
cb = ( uint ) Marshal . SizeOf < STARTUPINFO > ( ) ,
dwFlags = 0x00000100 ,
hStdInput = IntPtr . Zero ,
hStdOutput = handleStdOut ,
hStdError = handleStdErr
2020-02-17 00:25:25 +01:00
} ;
PROCESS_INFORMATION processinfo = new PROCESS_INFORMATION ( ) ;
2020-02-17 20:16:06 +01:00
if ( ! CreateProcessW ( exePath , commandline , IntPtr . Zero , IntPtr . Zero , true , flags , IntPtr . Zero , dir , ref startupinfo , ref processinfo ) )
2020-02-17 00:25:25 +01:00
{
throw new Win32Exception ( ) ;
}
handle = OpenProcess ( ProcessAccessRights . PROCESS_ALL_ACCESS , false , processinfo . dwProcessId ) ;
return processinfo . dwProcessId ;
}
public static bool Alive ( IntPtr handle ) {
uint ret = WaitForSingleObject ( handle , 0 ) ;
return ret = = WAIT_TIMEOUT ;
}
public static int KillProcess ( uint pid )
{
IntPtr handle = OpenProcess ( ProcessAccessRights . PROCESS_ALL_ACCESS , false , pid ) ;
if ( handle = = IntPtr . Zero )
{
return - 1 ;
}
if ( ! TerminateProcess ( handle , 0 ) )
{
throw new Win32Exception ( ) ;
}
if ( ! CloseHandle ( handle ) )
{
throw new Win32Exception ( ) ;
}
return 0 ;
}
}
}