diff --git a/.gitignore b/.gitignore index d9fe42b..909f9f0 100644 --- a/.gitignore +++ b/.gitignore @@ -824,6 +824,7 @@ FodyWeavers.xml.bak *.exe *.pdb *.dll +!EasyExtractUnitypackageRework/EasyExtractCrossPlatform/ThirdParty/EverythingSdk/Everything64.dll *.dylib # Other @@ -832,4 +833,4 @@ FodyWeavers.xml.bak *.psess *.vsp *.vspx -*.sap \ No newline at end of file +*.sap diff --git a/EasyExtractUnitypackageRework/EasyExtractCrossPlatform.Tests/Services/EverythingSdkBootstrapperTests.cs b/EasyExtractUnitypackageRework/EasyExtractCrossPlatform.Tests/Services/EverythingSdkBootstrapperTests.cs index 3e212ec..19b6116 100644 --- a/EasyExtractUnitypackageRework/EasyExtractCrossPlatform.Tests/Services/EverythingSdkBootstrapperTests.cs +++ b/EasyExtractUnitypackageRework/EasyExtractCrossPlatform.Tests/Services/EverythingSdkBootstrapperTests.cs @@ -118,7 +118,8 @@ public async Task EnsureDllForTestsAsync_ReusesValidExistingDllWithoutRewrite() EverythingSdkBootstrapper.ConfigureForTests( appDataPathOverride: () => appDataPath, validateDllHashOverride: (path, _) => - string.Equals(Path.GetFullPath(path), Path.GetFullPath(dllPath), StringComparison.OrdinalIgnoreCase)); + string.Equals(Path.GetFullPath(path), Path.GetFullPath(dllPath), StringComparison.OrdinalIgnoreCase), + localProbeDirectoriesOverride: () => new[] { sdkDirectory }); var resolvedPath = await EverythingSdkBootstrapper.EnsureDllForTestsAsync(); var currentWriteTime = File.GetLastWriteTimeUtc(dllPath); @@ -132,7 +133,7 @@ public async Task EnsureDllForTestsAsync_ReusesValidExistingDllWithoutRewrite() } [Fact] - public async Task EnsureDllForTestsAsync_SerializesConcurrentReuseWhileValidationRetries() + public async Task EnsureDllForTestsAsync_SerializesConcurrentBundledDllResolution() { if (!OperatingSystem.IsWindows()) return; @@ -160,11 +161,10 @@ public async Task EnsureDllForTestsAsync_SerializesConcurrentReuseWhileValidatio StringComparison.OrdinalIgnoreCase)) return true; - if (Interlocked.Increment(ref validationAttempts) == 1) - throw new SharingViolationIOException(); - + Interlocked.Increment(ref validationAttempts); return true; - }); + }, + localProbeDirectoriesOverride: () => new[] { sdkDirectory }); var callers = Enumerable.Range(0, 4) .Select(_ => EverythingSdkBootstrapper.EnsureDllForTestsAsync()) @@ -173,13 +173,13 @@ public async Task EnsureDllForTestsAsync_SerializesConcurrentReuseWhileValidatio var resolvedPaths = await Task.WhenAll(callers); Assert.All(resolvedPaths, path => Assert.Equal(dllPath, path, true)); - Assert.True(validationAttempts >= 2); + Assert.Equal(1, validationAttempts); Directory.Delete(root, true); } [Fact] - public async Task EnsureDllForTestsAsync_CleansOrphanedTemporaryDlls() + public async Task EnsureDllForTestsAsync_LeavesUnrelatedTemporaryFilesUntouched() { if (!OperatingSystem.IsWindows()) return; @@ -214,12 +214,13 @@ public async Task EnsureDllForTestsAsync_CleansOrphanedTemporaryDlls() return string.Equals(Path.GetFullPath(path), Path.GetFullPath(dllPath), StringComparison.OrdinalIgnoreCase); - }); + }, + localProbeDirectoriesOverride: () => new[] { sdkDirectory }); var resolvedPath = await EverythingSdkBootstrapper.EnsureDllForTestsAsync(); Assert.Equal(dllPath, resolvedPath, true); - Assert.False(File.Exists(orphanedTempPath)); + Assert.True(File.Exists(orphanedTempPath)); Directory.Delete(root, true); } @@ -291,6 +292,18 @@ public void IsTransientDownloadFailure_TruncatedHttpResponse_IsRetryable() Assert.True(EverythingSdkBootstrapper.IsTransientDownloadFailure(exception)); } + [Fact] + public async Task EnsureDllForTestsAsync_MissingBundledDll_DoesNotDownload() + { + if (!OperatingSystem.IsWindows()) + return; + + EverythingSdkBootstrapper.ResetForTests(); + EverythingSdkBootstrapper.ConfigureForTests(localProbeDirectoriesOverride: Array.Empty); + + await Assert.ThrowsAsync(() => EverythingSdkBootstrapper.EnsureDllForTestsAsync()); + } + [Fact] public async Task EnsureDllForTestsAsync_FallsBackToDllNextToTheExecutable() { @@ -368,4 +381,4 @@ public SharingViolationIOException() : base("Simulated sharing violation") HResult = SharingViolationHResult; } } -} \ No newline at end of file +} diff --git a/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/EasyExtractCrossPlatform.csproj b/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/EasyExtractCrossPlatform.csproj index f78d31c..f631468 100644 --- a/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/EasyExtractCrossPlatform.csproj +++ b/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/EasyExtractCrossPlatform.csproj @@ -48,6 +48,14 @@ + + + + Everything64.dll + PreserveNewest + PreserveNewest + + diff --git a/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/Services/EverythingSdkBootstrapper.cs b/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/Services/EverythingSdkBootstrapper.cs index d31c357..85d756f 100644 --- a/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/Services/EverythingSdkBootstrapper.cs +++ b/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/Services/EverythingSdkBootstrapper.cs @@ -13,6 +13,7 @@ internal static class EverythingSdkBootstrapper private static readonly TimeSpan DllPromotionInitialDelay = TimeSpan.FromMilliseconds(40); private static readonly TimeSpan DownloadInitialRetryDelay = TimeSpan.FromMilliseconds(500); + // Legacy helpers and their tests remain below, but EnsureDllAsync no longer calls the network path. private static readonly Uri SdkDownloadUri = new("https://www.voidtools.com/Everything-SDK.zip"); private static readonly HttpClient HttpClient = new(); private static readonly SemaphoreSlim DllInstallGate = new(1, 1); @@ -24,7 +25,7 @@ internal static class EverythingSdkBootstrapper private static readonly IReadOnlyDictionary ExpectedDllHashes = new Dictionary(StringComparer.OrdinalIgnoreCase) { - ["Everything64.dll"] = "C7AB8B47F7DD4C41AA735F4BA40B35AD5460A86FA7ABE0C94383F12BCE33BFB6", + ["Everything64.dll"] = "81B5BE18126ACD2C2B913F8F4A821E476B18393CDD3DEBD03387C50AFD8DB88F", ["Everything32.dll"] = "C28CD066AF36CAE4403A9933847AFF01DB928787D86751F014A1FA60D8B97FDA" }; @@ -105,42 +106,17 @@ private static async Task EnsureDllAsync() return _everythingDllPath; } - var appData = (_appDataPathOverride?.Invoke() ?? - Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)).Trim(); - var sdkDirectory = Path.Combine(appData, "EasyExtract", "ThirdParty", "EverythingSdk"); - Directory.CreateDirectory(sdkDirectory); - - var dllPath = Path.Combine(sdkDirectory, TargetDllFileName); - await DllInstallGate.WaitAsync().ConfigureAwait(false); try { - CleanupOrphanedTemporaryDlls(sdkDirectory); - - if (_everythingDllPath is { Length: > 0 } existingPath && File.Exists(existingPath)) - { - LoggingService.LogInformation($"Everything SDK DLL already present at '{existingPath}'."); - return existingPath; - } - - if (await TryReuseExistingDllAsync(dllPath).ConfigureAwait(false)) - { - _everythingDllPath = dllPath; - return dllPath; - } - - // The error messages tell users to drop the SDK DLL next to the executable, so honour - // that before going to the network. It is also the only way out on an offline machine. if (TryUseLocallyShippedDll(out var localDllPath)) { _everythingDllPath = localDllPath; return localDllPath; } - await DownloadDllWithRetryAsync(sdkDirectory, dllPath).ConfigureAwait(false); - - _everythingDllPath = dllPath; - return dllPath; + throw new FileNotFoundException( + $"Bundled {TargetDllFileName} was not found next to the EasyExtract executable."); } finally { @@ -594,4 +570,4 @@ internal static bool ValidateDllHashForTests(string dllPath, bool deleteOnMismat { return ValidateDllHash(dllPath, deleteOnMismatch); } -} \ No newline at end of file +} diff --git a/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/ThirdParty/EverythingSdk/Everything64.dll b/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/ThirdParty/EverythingSdk/Everything64.dll new file mode 100644 index 0000000..1df08c3 Binary files /dev/null and b/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/ThirdParty/EverythingSdk/Everything64.dll differ diff --git a/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/ThirdParty/EverythingSdk/README.md b/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/ThirdParty/EverythingSdk/README.md new file mode 100644 index 0000000..b18c0ba --- /dev/null +++ b/EasyExtractUnitypackageRework/EasyExtractCrossPlatform/ThirdParty/EverythingSdk/README.md @@ -0,0 +1,9 @@ +# Everything SDK runtime + +`Everything64.dll` is bundled next to the Windows executable at build and publish time. +It is the x64 file from the Voidtools Everything SDK archive verified on 2026-08-22. + +- SHA-256: `81B5BE18126ACD2C2B913F8F4A821E476B18393CDD3DEBD03387C50AFD8DB88F` +- Size: 91,304 bytes + +The application validates this exact hash and does not download an SDK DLL at runtime.