// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.
#nullable enable
#if FEATURE_PROCESS
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using IronPython.Runtime;
using IronPython.Runtime.Operations;
[assembly: PythonModule("_winapi", typeof(IronPython.Modules.PythonWinApi), PlatformsAttribute.PlatformFamily.Windows)]
namespace IronPython.Modules {
public static class PythonWinApi {
#region Public API
public static object? ConnectNamedPipe(BigInteger handle, bool overlapped = false) {
if (overlapped) throw new NotImplementedException();
if (overlapped) {
throw new NotImplementedException();
}
else {
var result = ConnectNamedPipe(checked((IntPtr)(long)handle), IntPtr.Zero);
if (!result) throw PythonNT.GetLastWin32Error();
return null;
}
}
public static BigInteger CreateFile([NotNone] string file_name, int desired_access, int share_mode, int security_attributes, int creation_disposition, int flags_and_attributes, BigInteger template_file) {
if (security_attributes != 0) throw new NotImplementedException();
if (template_file != 0) throw new NotImplementedException();
var handle = CreateFile(file_name, desired_access, share_mode, IntPtr.Zero, creation_disposition, flags_and_attributes, IntPtr.Zero);
if (handle == new IntPtr(-1)) throw PythonNT.GetLastWin32Error();
return (long)handle;
}
public static BigInteger CreateNamedPipe([NotNone] string name, int open_mode, int pipe_mode, int max_instances, int out_buffer_size, int in_buffer_size, int default_timeout, int security_attributes) {
if (security_attributes != 0) throw new NotImplementedException();
var handle = CreateNamedPipePI(name, (uint)open_mode, (uint)pipe_mode, (uint)max_instances, (uint)out_buffer_size, (uint)in_buffer_size, (uint)default_timeout, IntPtr.Zero);
if (handle == new IntPtr(-1)) throw PythonNT.GetLastWin32Error();
return (long)handle;
}
public static PythonTuple CreatePipe(object? pipe_attrs, int size) {
SECURITY_ATTRIBUTES pSecA = new SECURITY_ATTRIBUTES();
pSecA.nLength = Marshal.SizeOf(pSecA);
if (pipe_attrs != null) {
/* If pSec passed in from Python is not NULL
* there needs to be some conversion done here...*/
throw new NotImplementedException();
}
var result = CreatePipePI(out IntPtr hReadPipe, out IntPtr hWritePipe, ref pSecA, (uint)size);
if (!result) throw PythonNT.GetLastWin32Error();
return PythonTuple.MakeTuple((BigInteger)(long)hReadPipe, (BigInteger)(long)hWritePipe);
}
public static PythonTuple CreateProcess(
CodeContext context,
string? application_name,
string? command_line,
object? proc_attrs /*subprocess.py passes None*/,
object? thread_attrs /*subprocess.py passes None*/,
int? inherit_handles,
uint? creation_flags,
object? env_mapping,
string? current_directory,
object? startup_info /* subprocess.py passes STARTUPINFO*/) {
PythonOps.TryGetBoundAttr(context, startup_info, "dwFlags", out object? dwFlags); //public Int32 dwFlags;
PythonOps.TryGetBoundAttr(context, startup_info, "hStdInput", out object? hStdInput); //public IntPtr hStdInput;
PythonOps.TryGetBoundAttr(context, startup_info, "hStdOutput", out object? hStdOutput); //public IntPtr hStdOutput;
PythonOps.TryGetBoundAttr(context, startup_info, "hStdError", out object? hStdError); //public IntPtr hStdError;
PythonOps.TryGetBoundAttr(context, startup_info, "wShowWindow", out object? wShowWindow); //Int16 wShowWindow;
int dwFlagsInt32 = dwFlags != null ? Converter.ConvertToInt32(dwFlags) : 0;
IntPtr hStdInputIntPtr = hStdInput != null ? new IntPtr(Converter.ConvertToInt32(hStdInput)) : IntPtr.Zero;
IntPtr hStdOutputIntPtr = hStdOutput != null ? new IntPtr(Converter.ConvertToInt32(hStdOutput)) : IntPtr.Zero;
IntPtr hStdErrorIntPtr = hStdError != null ? new IntPtr(Converter.ConvertToInt32(hStdError)) : IntPtr.Zero;
short wShowWindowInt16 = wShowWindow != null ? Converter.ConvertToInt16(wShowWindow) : (short)0;
STARTUPINFO startupInfo = new STARTUPINFO {
dwFlags = dwFlagsInt32,
hStdInput = hStdInputIntPtr,
hStdOutput = hStdOutputIntPtr,
hStdError = hStdErrorIntPtr,
wShowWindow = wShowWindowInt16
};
// No special security
SECURITY_ATTRIBUTES pSecSA = new SECURITY_ATTRIBUTES();
pSecSA.nLength = Marshal.SizeOf(pSecSA);
SECURITY_ATTRIBUTES tSecSA = new SECURITY_ATTRIBUTES();
tSecSA.nLength = Marshal.SizeOf(tSecSA);
if (proc_attrs != null) {
/* If pSec paseed in from Python is not NULL
* there needs to be some conversion done here...*/
}
if (thread_attrs != null) {
/* If tSec paseed in from Python is not NULL
* there needs to be some conversion done here...*/
}
// If needed convert lpEnvironment Dictionary to lpEnvironmentIntPtr
string? lpEnvironment = EnvironmentToNative(context, env_mapping);
bool result = CreateProcessPI(
string.IsNullOrEmpty(application_name) ? null : application_name/*applicationNameHelper*//*processStartInfo.FileName*/,
string.IsNullOrEmpty(command_line) ? null : command_line/*commandLineArgsHelper*//*processStartInfo.Arguments*/,
ref pSecSA, ref tSecSA,
inherit_handles.HasValue && inherit_handles.Value > 0,
creation_flags ?? 0,
lpEnvironment,
current_directory,
ref startupInfo,
out PROCESS_INFORMATION lpProcessInformation);
if (!result) throw PythonNT.GetLastWin32Error();
IntPtr hp = lpProcessInformation.hProcess;
IntPtr ht = lpProcessInformation.hThread;
int pid = lpProcessInformation.dwProcessId;
int tid = lpProcessInformation.dwThreadId;
return PythonTuple.MakeTuple((BigInteger)(long)hp, (BigInteger)(long)ht, pid, tid);
static string? EnvironmentToNative(CodeContext context, object? environment) {
if (environment == null) {
return null;
}
var dict = environment as PythonDictionary ?? new PythonDictionary(context, environment);
var res = new StringBuilder();
foreach (var keyValue in dict) {
res.Append(keyValue.Key);
res.Append('=');
res.Append(keyValue.Value);
res.Append('\0');
}
return res.ToString();
}
}
public static void CloseHandle(BigInteger handle) {
CloseHandle(new IntPtr((long)handle));
}
public static BigInteger DuplicateHandle(
BigInteger source_process_handle,
BigInteger source_handle,
BigInteger target_process_handle,
int desired_access,
bool inherit_handle,
object? DUPLICATE_SAME_ACCESS) {
IntPtr currentProcessIntPtr = new IntPtr((long)source_process_handle);
IntPtr handleIntPtr = new IntPtr((long)source_handle);
IntPtr currentProcess2IntPtr = new IntPtr((long)target_process_handle);
bool sameAccess = DUPLICATE_SAME_ACCESS != null && Converter.ConvertToBoolean(DUPLICATE_SAME_ACCESS);
var result = DuplicateHandlePI(
currentProcessIntPtr,
handleIntPtr,
currentProcess2IntPtr,
out IntPtr lpTargetHandle,
Converter.ConvertToUInt32(desired_access),
inherit_handle,
sameAccess ? (uint)DuplicateOptions.DUPLICATE_SAME_ACCESS : (uint)DuplicateOptions.DUPLICATE_CLOSE_SOURCE
);
if (!result) throw PythonNT.GetLastWin32Error();
return (long)lpTargetHandle;
}
public static void ExitProcess(int exit_code) => Environment.Exit(exit_code);
public static BigInteger GetCurrentProcess() => (long)GetCurrentProcessPI();
public static int GetExitCodeProcess(BigInteger process) {
GetExitCodeProcessPI(new IntPtr((long)process), out int exitCode);
return exitCode;
}
public static int GetLastError() => Marshal.GetLastWin32Error();
public static string? GetModuleFileName(int module_handle) {
// Alternative Managed API: System.Diagnostics.ProcessModule.FileName or System.Reflection.Module.FullyQualifiedName.
return System.Diagnostics.Process.GetCurrentProcess().MainModule?.FileName;
}
public static object? GetStdHandle(int std_handle) {
var handle = GetStdHandlePI(std_handle);
if (handle == IntPtr.Zero) return null;
return handle.ToPython();
}
public static int GetVersion() {
return GetVersionPI();
}
public static BigInteger OpenProcess(int desired_access, bool inherit_handle, int process_id) {
return OpenProcessPI(desired_access, inherit_handle, process_id).ToInt64();
}
public static void TerminateProcess(BigInteger handle, object? exit_code) {
uint uExitCodeUint = Converter.ConvertToUInt32(exit_code);
bool result = TerminateProcessPI(new IntPtr((long)handle), uExitCodeUint);
if (!result) throw PythonNT.GetLastWin32Error();
}
public static int WaitForSingleObject(BigInteger handle, int dwMilliseconds) {
return WaitForSingleObjectPI(new IntPtr((long)handle), dwMilliseconds);
}
#endregion
#region struct's and enum's
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct STARTUPINFO {
public int cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public int dwX;
public int dwY;
public int dwXSize;
public int dwYSize;
public int dwXCountChars;
public int dwYCountChars;
public int dwFillAttribute;
public int dwFlags;
public short wShowWindow;
public short 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)]
internal /*class*/ struct SECURITY_ATTRIBUTES {
public int nLength;
public IntPtr lpSecurityDescriptor;
public int bInheritHandle;
}
[Flags]
internal enum DuplicateOptions : uint {
DUPLICATE_CLOSE_SOURCE = (0x00000001),// Closes the source handle. This occurs regardless of any error status returned.
DUPLICATE_SAME_ACCESS = (0x00000002), //Ignores the dwDesiredAccess parameter. The duplicate handle has the same access as the source handle.
}
#endregion
#region Privates / PInvokes
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ConnectNamedPipe(IntPtr hNamedPipe, IntPtr lpOverlapped);
[DllImport("kernel32.dll", EntryPoint = "CreateFileW", SetLastError = true, CharSet = CharSet.Unicode, BestFitMapping = false)]
private static extern IntPtr CreateFile(
string lpFileName,
int dwDesiredAccess,
int dwShareMode,
IntPtr securityAttrs,
int dwCreationDisposition,
int dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("kernel32.dll", EntryPoint = "CreateNamedPipe", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr CreateNamedPipePI(string lpName, uint dwOpenMode, uint dwPipeMode, uint nMaxInstances, uint nOutBufferSize, uint nInBufferSize, uint nDefaultTimeOut, IntPtr lpSecurityAttributes);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", EntryPoint = "CreateProcess", SetLastError = true)]
private static extern bool CreateProcessPI(string? lpApplicationName,
string? lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes, [MarshalAs(UnmanagedType.Bool)]bool bInheritHandles,
uint dwCreationFlags, string? lpEnvironment, string? lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("kernel32.dll", EntryPoint = "CreatePipe")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CreatePipePI(out IntPtr hReadPipe, out IntPtr hWritePipe,
ref SECURITY_ATTRIBUTES lpPipeAttributes, uint nSize);
[DllImport("kernel32.dll", SetLastError = true, EntryPoint = "DuplicateHandle")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DuplicateHandlePI(IntPtr hSourceProcessHandle,
IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,
uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
[DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess")]
private static extern IntPtr GetCurrentProcessPI();
[DllImport("kernel32.dll", SetLastError = true, EntryPoint = "GetExitCodeProcess")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetExitCodeProcessPI(IntPtr hProcess, out /*uint*/ int lpExitCode);
[DllImport("kernel32.dll", SetLastError = true, EntryPoint = "GetStdHandle")]
private static extern IntPtr GetStdHandlePI(int nStdHandle);
[DllImport("kernel32.dll", EntryPoint = "GetVersion")]
private static extern int GetVersionPI();
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", SetLastError = true, EntryPoint = "CloseHandle")]
internal static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true, EntryPoint = "OpenProcess")]
private static extern IntPtr OpenProcessPI(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true, EntryPoint = "TerminateProcess")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool TerminateProcessPI(IntPtr hProcess, uint uExitCode);
[DllImport("kernel32", CharSet = CharSet.Ansi,
SetLastError = true, ExactSpelling = true, EntryPoint = "WaitForSingleObject")]
internal static extern int WaitForSingleObjectPI(IntPtr hHandle, int dwMilliseconds);
#endregion
#region Constants
public const int CREATE_NEW_CONSOLE = 0x10;
public const int CREATE_NEW_PROCESS_GROUP = 0x200;
public const int DUPLICATE_CLOSE_SOURCE = 0x1;
public const int DUPLICATE_SAME_ACCESS = 0x2;
public const int ERROR_ALREADY_EXISTS = 0xb7;
public const int ERROR_BROKEN_PIPE = 0x6d;
public const int ERROR_IO_PENDING = 0x3e5;
public const int ERROR_MORE_DATA = 0xea;
public const int ERROR_NETNAME_DELETED = 0x40;
public const int ERROR_NO_DATA = 0xe8;
public const int ERROR_NO_SYSTEM_RESOURCES = 0x5aa;
public const int ERROR_OPERATION_ABORTED = 0x3e3;
public const int ERROR_PIPE_BUSY = 0xe7;
public const int ERROR_PIPE_CONNECTED = 0x217;
public const int ERROR_SEM_TIMEOUT = 0x79;
public const int FILE_FLAG_FIRST_PIPE_INSTANCE = 0x80000;
public const int FILE_FLAG_OVERLAPPED = 0x40000000;
public const int FILE_GENERIC_READ = 0x120089;
public const int FILE_GENERIC_WRITE = 0x120116;
public const int GENERIC_READ = unchecked((int)0x80000000);
public const int GENERIC_WRITE = 0x40000000;
public const int INFINITE = unchecked((int)0xffffffff);
public const int NMPWAIT_WAIT_FOREVER = unchecked((int)0xffffffff);
public const int NULL = 0x0;
public const int OPEN_EXISTING = 0x3;
public const int PIPE_ACCESS_DUPLEX = 0x3;
public const int PIPE_ACCESS_INBOUND = 0x1;
public const int PIPE_READMODE_MESSAGE = 0x2;
public const int PIPE_TYPE_MESSAGE = 0x4;
public const int PIPE_UNLIMITED_INSTANCES = 0xff;
public const int PIPE_WAIT = 0x0;
public const int PROCESS_ALL_ACCESS = 0x1f0fff;
public const int PROCESS_DUP_HANDLE = 0x40;
public const int STARTF_USESHOWWINDOW = 0x1;
public const int STARTF_USESTDHANDLES = 0x100;
public const int STD_ERROR_HANDLE = -12;
public const int STD_INPUT_HANDLE = -10;
public const int STD_OUTPUT_HANDLE = -11;
public const int STILL_ACTIVE = 0x103;
public const int SW_HIDE = 0x0;
public const int WAIT_ABANDONED_0 = 0x80;
public const int WAIT_OBJECT_0 = 0x0;
public const int WAIT_TIMEOUT = 0x102;
#endregion
}
}
#endif