using PuppeteerSharp; using PuppeteerSharp.BrowserData; namespace GB5Shared.Export.HybridReport { /// /// Resolves a working Chromium executable for PuppeteerSharp, independent of OS, process working /// directory, or deployment folder. Cache path is derived from the running assembly's own directory /// (AppContext.BaseDirectory) rather than the process working directory PuppeteerSharp otherwise /// assumes — this is what produced the stale "D:\...\BusinessHost\Chrome\Win64-...\chrome.exe not /// found" error at the client site. Moves automatically with the build folder on any OS/machine. /// public static class ChromiumResolver { private static readonly SemaphoreSlim _sem = new(1, 1); private static string? _cachedExecutablePath; public static async Task GetExecutablePathAsync() { if (_cachedExecutablePath != null && File.Exists(_cachedExecutablePath)) return _cachedExecutablePath; await _sem.WaitAsync().ConfigureAwait(false); try { if (_cachedExecutablePath != null && File.Exists(_cachedExecutablePath)) return _cachedExecutablePath; var cachePath = Path.Combine(AppContext.BaseDirectory, "ChromiumCache"); var buildId = Chrome.DefaultBuildId; var fetcher = new BrowserFetcher(new BrowserFetcherOptions { Path = cachePath }); var executablePath = fetcher.GetExecutablePath(buildId); // A cache folder can exist with a partial download or an AV-quarantined binary — // its presence is not proof the exe is runnable, so always verify the file itself // and re-download if it's missing, rather than trusting the cache blindly. if (!File.Exists(executablePath)) { await fetcher.DownloadAsync(buildId).ConfigureAwait(false); executablePath = fetcher.GetExecutablePath(buildId); } if (!File.Exists(executablePath)) { throw new FileNotFoundException( $"Chromium executable could not be resolved or downloaded at: {executablePath}. " + $"Verify outbound network access to the Chromium download host and write permission on '{cachePath}'."); } _cachedExecutablePath = executablePath; return executablePath; } finally { _sem.Release(); } } } }