diff --git a/src/Files.App/NativeMethods.txt b/src/Files.App/NativeMethods.txt index 283bbe917cda..39a4df749a6d 100644 --- a/src/Files.App/NativeMethods.txt +++ b/src/Files.App/NativeMethods.txt @@ -60,3 +60,13 @@ ResizePseudoConsole ClosePseudoConsole CreatePipe PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE +STARTUPINFOEXW +STARTUPINFOW +PROCESS_CREATION_FLAGS +PROCESS_INFORMATION +SECURITY_ATTRIBUTES +CloseHandle +DeleteProcThreadAttributeList +UpdateProcThreadAttribute +InitializeProcThreadAttributeList +CreateProcess \ No newline at end of file diff --git a/src/Files.App/Utils/Terminal/ConPTY/Native/ProcessApi.cs b/src/Files.App/Utils/Terminal/ConPTY/Native/ProcessApi.cs deleted file mode 100644 index abbb241d3ed9..000000000000 --- a/src/Files.App/Utils/Terminal/ConPTY/Native/ProcessApi.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Files.App.Utils.Terminal.ConPTY -{ - /// - /// PInvoke signatures for win32 process api - /// - static class ProcessApi - { - internal const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000; - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal struct STARTUPINFOEX - { - public STARTUPINFO StartupInfo; - public nint lpAttributeList; - } - - [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 nint lpReserved2; - public nint hStdInput; - public nint hStdOutput; - public nint hStdError; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PROCESS_INFORMATION - { - public nint hProcess; - public nint hThread; - public int dwProcessId; - public int dwThreadId; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct SECURITY_ATTRIBUTES - { - public int nLength; - public nint lpSecurityDescriptor; - public int bInheritHandle; - } - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool InitializeProcThreadAttributeList( - nint lpAttributeList, int dwAttributeCount, int dwFlags, ref nint lpSize); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool UpdateProcThreadAttribute( - nint lpAttributeList, uint dwFlags, nint attribute, nint lpValue, - nint cbSize, nint lpPreviousValue, nint lpReturnSize); - - [DllImport("kernel32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool CreateProcess( - string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, - ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, - nint lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo, - out PROCESS_INFORMATION lpProcessInformation); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool DeleteProcThreadAttributeList(nint lpAttributeList); - - [DllImport("kernel32.dll", SetLastError = true)] - internal static extern bool CloseHandle(nint hObject); - } -} diff --git a/src/Files.App/Utils/Terminal/ConPTY/Processes/Process.cs b/src/Files.App/Utils/Terminal/ConPTY/Processes/Process.cs index 652aaa703d4a..5d7cf5ec0edb 100644 --- a/src/Files.App/Utils/Terminal/ConPTY/Processes/Process.cs +++ b/src/Files.App/Utils/Terminal/ConPTY/Processes/Process.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.InteropServices; -using static Files.App.Utils.Terminal.ConPTY.ProcessApi; +using Windows.Win32; +using Windows.Win32.System.Threading; namespace Files.App.Utils.Terminal.ConPTY { @@ -9,13 +10,13 @@ namespace Files.App.Utils.Terminal.ConPTY /// internal sealed class Process : IDisposable { - public Process(STARTUPINFOEX startupInfo, PROCESS_INFORMATION processInfo) + public Process(STARTUPINFOEXW startupInfo, PROCESS_INFORMATION processInfo) { StartupInfo = startupInfo; ProcessInfo = processInfo; } - public STARTUPINFOEX StartupInfo { get; } + public STARTUPINFOEXW StartupInfo { get; } public PROCESS_INFORMATION ProcessInfo { get; } #region IDisposable Support @@ -34,20 +35,23 @@ void Dispose(bool disposing) // dispose unmanaged state // Free the attribute list - if (StartupInfo.lpAttributeList != nint.Zero) + unsafe { - DeleteProcThreadAttributeList(StartupInfo.lpAttributeList); - Marshal.FreeHGlobal(StartupInfo.lpAttributeList); + if ((void*)StartupInfo.lpAttributeList != null) + { + PInvoke.DeleteProcThreadAttributeList(StartupInfo.lpAttributeList); + Marshal.FreeHGlobal((nint)(void*)StartupInfo.lpAttributeList); + } } // Close process and thread handles if (ProcessInfo.hProcess != nint.Zero) { - CloseHandle(ProcessInfo.hProcess); + PInvoke.CloseHandle(ProcessInfo.hProcess); } if (ProcessInfo.hThread != nint.Zero) { - CloseHandle(ProcessInfo.hThread); + PInvoke.CloseHandle(ProcessInfo.hThread); } disposedValue = true; diff --git a/src/Files.App/Utils/Terminal/ConPTY/Processes/ProcessFactory.cs b/src/Files.App/Utils/Terminal/ConPTY/Processes/ProcessFactory.cs index 4966a9a31402..f66624cefb8f 100644 --- a/src/Files.App/Utils/Terminal/ConPTY/Processes/ProcessFactory.cs +++ b/src/Files.App/Utils/Terminal/ConPTY/Processes/ProcessFactory.cs @@ -1,6 +1,9 @@ using System; using System.Runtime.InteropServices; -using static Files.App.Utils.Terminal.ConPTY.ProcessApi; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Security; +using Windows.Win32.System.Threading; namespace Files.App.Utils.Terminal.ConPTY { @@ -22,77 +25,95 @@ internal static Process Start(string command, string directory, nint attributes, return new Process(startupInfo, processInfo); } - private static STARTUPINFOEX ConfigureProcessThread(nint hPC, nint attributes) + private static STARTUPINFOEXW ConfigureProcessThread(nint hPC, nint attributes) { // this method implements the behavior described in https://docs.microsoft.com/en-us/windows/console/creating-a-pseudoconsole-session#preparing-for-creation-of-the-child-process - - var lpSize = nint.Zero; - var success = InitializeProcThreadAttributeList( - lpAttributeList: nint.Zero, - dwAttributeCount: 1, - dwFlags: 0, - lpSize: ref lpSize - ); - if (success || lpSize == nint.Zero) // we're not expecting `success` here, we just want to get the calculated lpSize + unsafe { - throw new InvalidOperationException("Could not calculate the number of bytes for the attribute list. " + Marshal.GetLastWin32Error()); - } + var lpSize = nuint.Zero; + var success = PInvoke.InitializeProcThreadAttributeList( + lpAttributeList: new(null), + dwAttributeCount: 1, + lpSize: ref lpSize + ); + if (success || lpSize == nuint.Zero) // we're not expecting `success` here, we just want to get the calculated lpSize + { + throw new InvalidOperationException("Could not calculate the number of bytes for the attribute list. " + Marshal.GetLastWin32Error()); + } - var startupInfo = new STARTUPINFOEX(); - startupInfo.StartupInfo.cb = Marshal.SizeOf(); - startupInfo.lpAttributeList = Marshal.AllocHGlobal(lpSize); + var startupInfo = new STARTUPINFOEXW(); + startupInfo.StartupInfo.cb = (uint)Marshal.SizeOf(); + startupInfo.lpAttributeList = new((void*)Marshal.AllocHGlobal((int)lpSize)); - success = InitializeProcThreadAttributeList( - lpAttributeList: startupInfo.lpAttributeList, - dwAttributeCount: 1, - dwFlags: 0, - lpSize: ref lpSize - ); - if (!success) - { - throw new InvalidOperationException("Could not set up attribute list. " + Marshal.GetLastWin32Error()); - } + success = PInvoke.InitializeProcThreadAttributeList( + lpAttributeList: startupInfo.lpAttributeList, + dwAttributeCount: 1, + lpSize: ref lpSize + ); + if (!success) + { + throw new InvalidOperationException("Could not set up attribute list. " + Marshal.GetLastWin32Error()); + } - success = UpdateProcThreadAttribute( - lpAttributeList: startupInfo.lpAttributeList, - dwFlags: 0, - attribute: attributes, - lpValue: hPC, - cbSize: nint.Size, - lpPreviousValue: nint.Zero, - lpReturnSize: nint.Zero - ); - if (!success) - { - throw new InvalidOperationException("Could not set pseudoconsole thread attribute. " + Marshal.GetLastWin32Error()); - } + success = PInvoke.UpdateProcThreadAttribute( + lpAttributeList: startupInfo.lpAttributeList, + dwFlags: 0, + Attribute: (nuint)attributes, + lpValue: (void*)hPC, + cbSize: (nuint)nint.Size, + lpPreviousValue: null, + lpReturnSize: (nuint*)null + ); + if (!success) + { + throw new InvalidOperationException("Could not set pseudoconsole thread attribute. " + Marshal.GetLastWin32Error()); + } - return startupInfo; + return startupInfo; + } } - private static PROCESS_INFORMATION RunProcess(ref STARTUPINFOEX sInfoEx, string commandLine, string directory) + private static PROCESS_INFORMATION RunProcess(ref STARTUPINFOEXW sInfoEx, string commandLine, string directory) { - int securityAttributeSize = Marshal.SizeOf(); - var pSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize }; - var tSec = new SECURITY_ATTRIBUTES { nLength = securityAttributeSize }; - var success = CreateProcess( - lpApplicationName: null, - lpCommandLine: commandLine, - lpProcessAttributes: ref pSec, - lpThreadAttributes: ref tSec, - bInheritHandles: false, - dwCreationFlags: EXTENDED_STARTUPINFO_PRESENT, - lpEnvironment: nint.Zero, - lpCurrentDirectory: directory, - lpStartupInfo: ref sInfoEx, - lpProcessInformation: out PROCESS_INFORMATION pInfo - ); - if (!success) + unsafe { - throw new InvalidOperationException("Could not create process. " + Marshal.GetLastWin32Error()); - } + var success = false; + + int securityAttributeSize = Marshal.SizeOf(); + var pSec = new SECURITY_ATTRIBUTES { nLength = (uint)securityAttributeSize }; + var tSec = new SECURITY_ATTRIBUTES { nLength = (uint)securityAttributeSize }; + + PROCESS_INFORMATION lpProcessInformation; - return pInfo; + fixed (STARTUPINFOEXW* lpStartupInfo = &sInfoEx) + { + fixed (char* lpCurrentDirectory = directory) + { + fixed (char* lpCommandLine = commandLine) + { + success = PInvoke.CreateProcess( + lpApplicationName: new PCWSTR(null), + lpCommandLine: lpCommandLine, + lpProcessAttributes: &pSec, + lpThreadAttributes: &tSec, + bInheritHandles: false, + dwCreationFlags: PROCESS_CREATION_FLAGS.EXTENDED_STARTUPINFO_PRESENT, + lpEnvironment: null, + lpCurrentDirectory: lpCurrentDirectory, + lpStartupInfo: (STARTUPINFOW*)lpStartupInfo, + lpProcessInformation: &lpProcessInformation + ); + } + } + } + + if (!success) + { + throw new InvalidOperationException("Could not create process. " + Marshal.GetLastWin32Error()); + } + + return lpProcessInformation; + } } } }