Skip to content

Commit

Permalink
Switch to CsWin32 for process api
Browse files Browse the repository at this point in the history
  • Loading branch information
gave92 committed May 11, 2024
1 parent 2ae01a1 commit 5da01eb
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 154 deletions.
10 changes: 10 additions & 0 deletions src/Files.App/NativeMethods.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
86 changes: 0 additions & 86 deletions src/Files.App/Utils/Terminal/ConPTY/Native/ProcessApi.cs

This file was deleted.

20 changes: 12 additions & 8 deletions src/Files.App/Utils/Terminal/ConPTY/Processes/Process.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -9,13 +10,13 @@ namespace Files.App.Utils.Terminal.ConPTY
/// </summary>
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
Expand All @@ -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;
Expand Down
141 changes: 81 additions & 60 deletions src/Files.App/Utils/Terminal/ConPTY/Processes/ProcessFactory.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -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<STARTUPINFOEX>();
startupInfo.lpAttributeList = Marshal.AllocHGlobal(lpSize);
var startupInfo = new STARTUPINFOEXW();
startupInfo.StartupInfo.cb = (uint)Marshal.SizeOf<STARTUPINFOEXW>();
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<SECURITY_ATTRIBUTES>();
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<SECURITY_ATTRIBUTES>();
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;
}
}
}
}

0 comments on commit 5da01eb

Please sign in to comment.