Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.InteropServices;

internal static partial class Interop
{
internal static partial class Kernel32
{
[LibraryImport(Libraries.Kernel32, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)]
internal static partial uint GetWindowsDirectoryW(ref char lpBuffer, uint uSize);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,17 @@ public ProcessStartInfo(string fileName, System.Collections.Generic.IEnumerable<
[System.Diagnostics.CodeAnalysis.AllowNullAttribute]
public string WorkingDirectory { get { throw null; } set { } }
}
public sealed partial class ProcessStartOptions
{
public ProcessStartOptions(string fileName) { }
public System.Collections.Generic.IList<string> Arguments { get { throw null; } set { } }
public bool CreateNewProcessGroup { get { throw null; } set { } }
public System.Collections.Generic.IDictionary<string, string?> Environment { get { throw null; } }
public string FileName { get { throw null; } }
public System.Collections.Generic.IList<System.Runtime.InteropServices.SafeHandle> InheritedHandles { get { throw null; } set { } }
public bool KillOnParentExit { get { throw null; } set { } }
public string? WorkingDirectory { get { throw null; } set { } }
}
[System.ComponentModel.DesignerAttribute("System.Diagnostics.Design.ProcessThreadDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
public partial class ProcessThread : System.ComponentModel.Component
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -336,4 +336,7 @@
<data name="InvalidPerfData" xml:space="preserve">
<value>Invalid performance counter data with type '{0}'.</value>
</data>
<data name="FileNotFoundResolvePath" xml:space="preserve">
<value>Could not resolve the file.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<TargetPlatformIdentifier>$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))</TargetPlatformIdentifier>
<GeneratePlatformNotSupportedAssemblyMessage Condition="'$(TargetPlatformIdentifier)' == ''">SR.Process_PlatformNotSupported</GeneratePlatformNotSupportedAssemblyMessage>
<IsiOSLike Condition="'$(TargetPlatformIdentifier)' == 'maccatalyst' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos'">true</IsiOSLike>
<DefineConstants Condition="'$(TargetPlatformIdentifier)' == 'windows'">$(DefineConstants);WINDOWS</DefineConstants>
</PropertyGroup>

<ItemGroup Condition="'$(TargetPlatformIdentifier)' != ''">
Expand All @@ -26,6 +27,7 @@
<Compile Include="System\Diagnostics\ProcessModuleCollection.cs" />
<Compile Include="System\Diagnostics\ProcessPriorityClass.cs" />
<Compile Include="System\Diagnostics\ProcessStartInfo.cs" />
<Compile Include="System\Diagnostics\ProcessStartOptions.cs" />
<Compile Include="System\Diagnostics\ProcessThread.cs" />
<Compile Include="System\Diagnostics\ProcessThreadCollection.cs" />
<Compile Include="System\Diagnostics\ProcessWindowStyle.cs" />
Expand All @@ -47,6 +49,8 @@
Link="Common\System\Text\ValueStringBuilder.cs" />
<Compile Include="$(CommonPath)System\Collections\Generic\ArrayBuilder.cs"
Link="Common\System\Collections\Generic\ArrayBuilder.cs" />
<Compile Include="$(CommonPath)System\IO\StringParser.cs"
Link="Common\System\IO\StringParser.cs" />
</ItemGroup>

<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'windows'">
Expand Down Expand Up @@ -136,6 +140,10 @@
Link="Common\Interop\Windows\Kernel32\Interop.GetProcessPriorityBoost.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SetProcessPriorityBoost.cs"
Link="Common\Interop\Windows\Kernel32\Interop.SetProcessPriorityBoost.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GetSystemDirectoryW.cs"
Link="Common\Interop\Windows\Kernel32\Interop.GetSystemDirectoryW.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.GetWindowsDirectoryW.cs"
Link="Common\Interop\Windows\Kernel32\Interop.GetWindowsDirectoryW.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.OpenThread.cs"
Link="Common\Interop\Windows\Kernel32\Interop.OpenThread.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.SetThreadPriority.cs"
Expand Down Expand Up @@ -235,8 +243,6 @@
<Compile Include="System\Diagnostics\ProcessStartInfo.Unix.cs" />
<Compile Include="System\Diagnostics\ProcessWaitHandle.Unix.cs" />
<Compile Include="System\Diagnostics\ProcessWaitState.Unix.cs" />
<Compile Include="$(CommonPath)System\IO\StringParser.cs"
Link="Common\System\IO\StringParser.cs" />
<Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs"
Link="Common\Interop\Unix\Interop.Libraries.cs" />
<Compile Include="$(CommonPath)Interop\Unix\Interop.Errors.cs"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ private static DateTime BootTime
ReadOnlySpan<string> allowedProgramsToRun = ["xdg-open", "gnome-open", "kfmclient"];
foreach (var program in allowedProgramsToRun)
{
string? pathToProgram = FindProgramInPath(program);
string? pathToProgram = ProcessStartOptions.FindProgramInPath(program);
if (!string.IsNullOrEmpty(pathToProgram))
{
return pathToProgram;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ internal DateTime StartTimeCore
/// <summary>Gets execution path</summary>
private static string? GetPathToOpenFile()
{
return FindProgramInPath("xdg-open");
return ProcessStartOptions.FindProgramInPath("xdg-open");
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,7 @@ private bool StartCore(ProcessStartInfo startInfo)
}
else
{
filename = ResolvePath(startInfo.FileName);
filename = ProcessStartOptions.ResolvePath(startInfo.FileName);
argv = ParseArgv(startInfo);
if (Directory.Exists(filename))
{
Expand Down Expand Up @@ -663,7 +663,7 @@ private static string[] CreateEnvp(ProcessStartInfo psi)
// find filename on PATH
else
{
resolvedFilename = FindProgramInPath(filename);
resolvedFilename = ProcessStartOptions.FindProgramInPath(filename);
}
}

Expand All @@ -682,79 +682,7 @@ private static string[] CreateEnvp(ProcessStartInfo psi)
}
}

/// <summary>Resolves a path to the filename passed to ProcessStartInfo. </summary>
/// <param name="filename">The filename.</param>
/// <returns>The resolved path. It can return null in case of URLs.</returns>
private static string? ResolvePath(string filename)
{
// Follow the same resolution that Windows uses with CreateProcess:
// 1. First try the exact path provided
// 2. Then try the file relative to the executable directory
// 3. Then try the file relative to the current directory
// 4. then try the file in each of the directories specified in PATH
// Windows does additional Windows-specific steps between 3 and 4,
// and we ignore those here.

// If the filename is a complete path, use it, regardless of whether it exists.
if (Path.IsPathRooted(filename))
{
// In this case, it doesn't matter whether the file exists or not;
// it's what the caller asked for, so it's what they'll get
return filename;
}

// Then check the executable's directory
string? path = Environment.ProcessPath;
if (path != null)
{
try
{
path = Path.Combine(Path.GetDirectoryName(path)!, filename);
if (File.Exists(path))
{
return path;
}
}
catch (ArgumentException) { } // ignore any errors in data that may come from the exe path
}

// Then check the current directory
path = Path.Combine(Directory.GetCurrentDirectory(), filename);
if (File.Exists(path))
{
return path;
}

// Then check each directory listed in the PATH environment variables
return FindProgramInPath(filename);
}

/// <summary>
/// Gets the path to the program
/// </summary>
/// <param name="program"></param>
/// <returns></returns>
private static string? FindProgramInPath(string program)
{
string path;
string? pathEnvVar = Environment.GetEnvironmentVariable("PATH");
if (pathEnvVar != null)
{
var pathParser = new StringParser(pathEnvVar, ':', skipEmpty: true);
while (pathParser.MoveNext())
{
string subPath = pathParser.ExtractCurrent();
path = Path.Combine(subPath, program);
if (IsExecutable(path))
{
return path;
}
}
}
return null;
}

private static bool IsExecutable(string fullPath)
internal static bool IsExecutable(string fullPath)
{
Interop.Sys.FileStatus fileinfo;

Expand Down
Loading