Skip to content

Commit 7907053

Browse files
authored
Fix extension comparison (#43877)
1 parent 6a7f86a commit 7907053

File tree

4 files changed

+76
-4
lines changed

4 files changed

+76
-4
lines changed

src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,9 @@ public static void Initialize()
2323
// When launching the application process dotnet-watch sets Hot Reload environment variables via CLI environment directives (dotnet [env:X=Y] run).
2424
// Currently, the CLI parser sets the env variables to the dotnet.exe process itself, rather then to the target process.
2525
// This may cause the dotnet.exe process to connect to the named pipe and break it for the target process.
26-
var processExe = Path.ChangeExtension(processPath, ".exe");
27-
var expectedExe = Path.ChangeExtension(s_targetProcessPath, ".exe");
28-
if (!string.Equals(processExe, expectedExe, Path.DirectorySeparatorChar == '\\' ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
26+
if (!IsMatchingProcess(processPath, s_targetProcessPath))
2927
{
30-
Log($"Ignoring process '{processExe}', expecting '{expectedExe}'");
28+
Log($"Ignoring process '{processPath}', expecting '{s_targetProcessPath}'");
3129
return;
3230
}
3331

@@ -67,6 +65,24 @@ public static void Initialize()
6765
});
6866
}
6967

68+
public static bool IsMatchingProcess(string processPath, string targetProcessPath)
69+
{
70+
var comparison = Path.DirectorySeparatorChar == '\\' ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
71+
var (shorter, longer) = (processPath.Length > targetProcessPath.Length) ? (targetProcessPath, processPath) : (processPath, targetProcessPath);
72+
73+
// one or both have no extension, or they have the same extension
74+
if (longer.StartsWith(shorter, comparison))
75+
{
76+
var suffix = longer[shorter.Length..];
77+
return suffix is "" || suffix.Equals(".exe", comparison) || suffix.Equals(".dll", comparison);
78+
}
79+
80+
// different extension:
81+
return (processPath.EndsWith(".exe", comparison) || processPath.EndsWith(".dll", comparison)) &&
82+
(targetProcessPath.EndsWith(".exe", comparison) || targetProcessPath.EndsWith(".dll", comparison)) &&
83+
string.Equals(processPath[..^4], targetProcessPath[..^4], comparison);
84+
}
85+
7086
internal static void ClearHotReloadEnvironmentVariables()
7187
{
7288
// Clear any hot-reload specific environment variables. This prevents child processes from being

test/Microsoft.Extensions.DotNetDeltaApplier.Tests/Microsoft.Extensions.DotNetDeltaApplier.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<ItemGroup>
1212
<ProjectReference Include="..\..\src\BuiltInTools\DotNetDeltaApplier\Microsoft.Extensions.DotNetDeltaApplier.csproj" />
1313
<ProjectReference Include="..\Microsoft.NET.TestFramework\Microsoft.NET.TestFramework.csproj" />
14+
<PackageReference Include="Xunit.Combinatorial" />
1415
<PackageReference Include="Moq" />
1516
</ItemGroup>
1617

test/Microsoft.Extensions.DotNetDeltaApplier.Tests/StartupHookTests.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,60 @@ public void ClearHotReloadEnvironmentVariables_RemovesHotReloadStartup_InCaseInv
2424
var customStartupHook = "/path/mycoolstartup.dll";
2525
Assert.Equal(customStartupHook, StartupHook.RemoveCurrentAssembly(customStartupHook + Path.PathSeparator + typeof(StartupHook).Assembly.Location.ToUpperInvariant()));
2626
}
27+
28+
[Theory]
29+
[CombinatorialData]
30+
public void IsMatchingProcess_Matching_SimpleName(
31+
[CombinatorialValues("", ".dll", ".exe")] string extension,
32+
[CombinatorialValues("", ".dll", ".exe")] string targetExtension)
33+
{
34+
var dir = Path.GetDirectoryName(typeof(StartupHookTests).Assembly.Location)!;
35+
var name = "a";
36+
var processPath = Path.Combine(dir, name + extension);
37+
var targetProcessPath = Path.Combine(dir, "a" + targetExtension);
38+
39+
Assert.True(StartupHook.IsMatchingProcess(processPath, targetProcessPath));
40+
}
41+
42+
[Theory]
43+
[CombinatorialData]
44+
public void IsMatchingProcess_Matching_DotInName(
45+
[CombinatorialValues("", ".dll", ".exe")] string extension,
46+
[CombinatorialValues("", ".dll", ".exe")] string targetExtension)
47+
{
48+
var dir = Path.GetDirectoryName(typeof(StartupHookTests).Assembly.Location)!;
49+
var name = "a.b";
50+
var processPath = Path.Combine(dir, name + extension);
51+
var targetProcessPath = Path.Combine(dir, name + targetExtension);
52+
53+
Assert.True(StartupHook.IsMatchingProcess(processPath, targetProcessPath));
54+
}
55+
56+
[Theory]
57+
[CombinatorialData]
58+
public void IsMatchingProcess_Matching_DotDllInName(
59+
[CombinatorialValues("", ".dll", ".exe")] string extension,
60+
[CombinatorialValues("", ".dll", ".exe")] string targetExtension)
61+
{
62+
var dir = Path.GetDirectoryName(typeof(StartupHookTests).Assembly.Location)!;
63+
var name = "a.dll";
64+
var processPath = Path.Combine(dir, name + extension);
65+
var targetProcessPath = Path.Combine(dir, name + targetExtension);
66+
67+
Assert.True(StartupHook.IsMatchingProcess(processPath, targetProcessPath));
68+
}
69+
70+
[Theory]
71+
[CombinatorialData]
72+
public void IsMatchingProcess_NotMatching(
73+
[CombinatorialValues("", ".dll", ".exe")] string extension,
74+
[CombinatorialValues("", ".dll", ".exe")] string targetExtension)
75+
{
76+
var dir = Path.GetDirectoryName(typeof(StartupHookTests).Assembly.Location)!;
77+
var processPath = Path.Combine(dir, "a" + extension);
78+
var targetProcessPath = Path.Combine(dir, "b" + targetExtension);
79+
80+
Assert.False(StartupHook.IsMatchingProcess(processPath, targetProcessPath));
81+
}
2782
}
2883
}

0 commit comments

Comments
 (0)