diff --git a/CHANGELOG.md b/CHANGELOG.md index 24663b8bb2..71b7916fa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file. - [GUI] Multiple manual downloads, uncached filter, purge option (#2930 by: HebaruSan; reviewed: DasSkelett) - [Cmdline] Return failure on failed commands for headless prompt (#2941 by: HebaruSan; reviewed: DasSkelett) - [GUI] Obey system colors for dark theme support (#2937 by: HebaruSan; reviewed: DasSkelett) +- [Multiple] Workaround to launch URLs (#2958 by: HebaruSan; reviewed: DasSkelett) ### Internal diff --git a/ConsoleUI/ModInfoScreen.cs b/ConsoleUI/ModInfoScreen.cs index 75b3917ebb..9c72c32487 100644 --- a/ConsoleUI/ModInfoScreen.cs +++ b/ConsoleUI/ModInfoScreen.cs @@ -219,10 +219,7 @@ public static bool LaunchURL(Uri u) // then wait 1.5 seconds and refresh the screen when it closes. ConsoleMessageDialog d = new ConsoleMessageDialog("Launching...", new List()); d.Run(() => { - Process.Start(new ProcessStartInfo() { - UseShellExecute = true, - FileName = u.ToString() - }); + Utilities.ProcessStartURL(u.ToString()); System.Threading.Thread.Sleep(1500); }); return true; diff --git a/Core/Utilities.cs b/Core/Utilities.cs index c4187122f2..1b726f6479 100644 --- a/Core/Utilities.cs +++ b/Core/Utilities.cs @@ -1,7 +1,9 @@ using System; using System.IO; +using System.Diagnostics; using System.Transactions; using ChinhDo.Transactions.FileManager; +using log4net; namespace CKAN { @@ -75,5 +77,50 @@ private static void _CopyDirectory(string sourceDirPath, string destDirPath, boo } } } + + /// + /// Launch a URL. For YEARS this was done by Process.Start in a + /// cross-platform way, but Microsoft chose to break that, + /// so now every .NET app has to write its own custom code for it, + /// with special code for each platform. + /// https://github.com/dotnet/corefx/issues/10361 + /// + /// URL to launch + /// + /// true if launched, false otherwise + /// + public static bool ProcessStartURL(string url) + { + try + { + if (Platform.IsMac) + { + Process.Start("open", $"\"{url}\""); + return true; + } + else if (Platform.IsUnix) + { + Process.Start("xdg-open", $"\"{url}\""); + return true; + } + else + { + // Try the old way + Process.Start(new ProcessStartInfo(url) + { + UseShellExecute = true, + Verb = "open" + }); + return true; + } + } + catch (Exception exc) + { + log.Error($"Exception for URL {url}", exc); + } + return false; + } + + private static readonly ILog log = LogManager.GetLogger(typeof(Utilities)); } } diff --git a/GUI/Main.cs b/GUI/Main.cs index 68510d4b34..812833b7db 100644 --- a/GUI/Main.cs +++ b/GUI/Main.cs @@ -1151,7 +1151,7 @@ private void manageKspInstancesMenuItem_Click(object sender, EventArgs e) private void openKspDirectoryToolStripMenuItem_Click(object sender, EventArgs e) { - Process.Start(Instance.manager.CurrentInstance.GameDir()); + Utilities.ProcessStartURL(Instance.manager.CurrentInstance.GameDir()); } private void CompatibleKspVersionsToolStripMenuItem_Click(object sender, EventArgs e) @@ -1296,12 +1296,12 @@ private void MainTabControl_OnSelectedIndexChanged(object sender, EventArgs e) private void reportClientIssueToolStripMenuItem_Click(object sender, EventArgs e) { - Process.Start("https://github.com/KSP-CKAN/CKAN/issues/new/choose"); + Utilities.ProcessStartURL("https://github.com/KSP-CKAN/CKAN/issues/new/choose"); } private void reportMetadataIssueToolStripMenuItem_Click(object sender, EventArgs e) { - Process.Start("https://github.com/KSP-CKAN/NetKAN/issues/new/choose"); + Utilities.ProcessStartURL("https://github.com/KSP-CKAN/NetKAN/issues/new/choose"); } private void ModList_MouseDown(object sender, MouseEventArgs e) diff --git a/GUI/MainInstall.cs b/GUI/MainInstall.cs index e664d8d56a..1104d27e33 100644 --- a/GUI/MainInstall.cs +++ b/GUI/MainInstall.cs @@ -275,11 +275,7 @@ private void InstallMods(object sender, DoWorkEventArgs e) // Launch the URL describing this host's throttling practices, if any if (kraken.infoUrl != null) { - Process.Start(new ProcessStartInfo() - { - UseShellExecute = true, - FileName = kraken.infoUrl.ToString() - }); + Utilities.ProcessStartURL(kraken.infoUrl.ToString()); } // Now pretend they clicked the menu option for the settings Enabled = false; @@ -303,11 +299,7 @@ private void InstallMods(object sender, DoWorkEventArgs e) { if (GUI.user.RaiseYesNoDialog(Properties.Resources.MainInstallLibCurlMissing)) { - Process.Start(new ProcessStartInfo() - { - UseShellExecute = true, - FileName = "https://github.com/KSP-CKAN/CKAN/wiki/libcurl" - }); + Utilities.ProcessStartURL("https://github.com/KSP-CKAN/CKAN/wiki/libcurl"); } throw; } diff --git a/GUI/MainModInfo.cs b/GUI/MainModInfo.cs index 0610052b4a..4a8c0cb085 100644 --- a/GUI/MainModInfo.cs +++ b/GUI/MainModInfo.cs @@ -114,7 +114,7 @@ private void ContentsDownloadButton_Click(object sender, EventArgs e) private void ContentsOpenButton_Click(object sender, EventArgs e) { - Process.Start(manager.Cache.GetCachedFilename(SelectedModule.ToModule())); + Utilities.ProcessStartURL(manager.Cache.GetCachedFilename(SelectedModule.ToModule())); } private void LinkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) @@ -608,7 +608,7 @@ internal void OpenFileBrowser(TreeNode node) return; } - Process.Start(location); + Utilities.ProcessStartURL(location); } } } diff --git a/GUI/MainModList.cs b/GUI/MainModList.cs index 47ce5d6d8b..786dffd0ed 100644 --- a/GUI/MainModList.cs +++ b/GUI/MainModList.cs @@ -583,7 +583,7 @@ private async void ModList_CellValueChanged(object sender, DataGridViewCellEvent DataGridViewLinkCell cell = gridCell as DataGridViewLinkCell; string cmd = cell?.Value.ToString(); if (!string.IsNullOrEmpty(cmd)) - Process.Start(cmd); + Utilities.ProcessStartURL(cmd); } else { diff --git a/GUI/ManageKspInstances.cs b/GUI/ManageKspInstances.cs index 68672628b5..db59269b4a 100644 --- a/GUI/ManageKspInstances.cs +++ b/GUI/ManageKspInstances.cs @@ -207,7 +207,7 @@ private void OpenDirectoryMenuItem_Click(object sender, EventArgs e) return; } - System.Diagnostics.Process.Start(path); + Utilities.ProcessStartURL(path); } private void RenameButton_Click(object sender, EventArgs e) diff --git a/GUI/SettingsDialog.cs b/GUI/SettingsDialog.cs index b388eb5d14..977fcb30b9 100644 --- a/GUI/SettingsDialog.cs +++ b/GUI/SettingsDialog.cs @@ -225,12 +225,7 @@ private void ResetCacheButton_Click(object sender, EventArgs e) private void OpenCacheButton_Click(object sender, EventArgs e) { - Process.Start(new ProcessStartInfo() - { - FileName = config.DownloadCacheDir, - UseShellExecute = true, - Verb = "open" - }); + Utilities.ProcessStartURL(config.DownloadCacheDir); } private void ReposListBox_SelectedIndexChanged(object sender, EventArgs e) diff --git a/GUI/Util.cs b/GUI/Util.cs index a9b05bb54f..9d5e8beab4 100644 --- a/GUI/Util.cs +++ b/GUI/Util.cs @@ -70,7 +70,9 @@ public static void HideConsoleWindow() public static bool CheckURLValid(string source) { Uri uri_result; - return Uri.TryCreate(source, UriKind.Absolute, out uri_result) && uri_result.Scheme == Uri.UriSchemeHttp; + return Uri.TryCreate(source, UriKind.Absolute, out uri_result) + && (uri_result.Scheme == Uri.UriSchemeHttp + || uri_result.Scheme == Uri.UriSchemeHttps); } /// @@ -95,30 +97,17 @@ public static bool TryOpenWebPage(string url, IEnumerable prefixes = nul { // Default prefixes to try if not provided if (prefixes == null) - prefixes = new string[] { "http://", "https://" }; - - try // opening the page normally { - Process.Start(url); - return true; // we did it! return true + prefixes = new string[] { "https://", "http://" }; } - catch (Exception) // something bad happened + + foreach (string fullUrl in new string[] { url } + .Concat(prefixes.Select(p => p + url).Where(CheckURLValid))) { - foreach (string prefixed_url in prefixes.Select(p=>p+url).Where(CheckURLValid)) - { - try // with a new prefix - { - Process.Start(prefixed_url); - return true; - } - catch (Exception) - { - // move along to the next prefix - } - } - // We tried all prefixes, and still no luck. - return false; + if (Utilities.ProcessStartURL(fullUrl)) + return true; } + return false; } ///