diff --git a/lib/TweetLib.Browser.CEF/Interfaces/IFileDialogOpener.cs b/lib/TweetLib.Browser.CEF/Interfaces/IFileDialogOpener.cs
index fb449091..2f86c8b7 100644
--- a/lib/TweetLib.Browser.CEF/Interfaces/IFileDialogOpener.cs
+++ b/lib/TweetLib.Browser.CEF/Interfaces/IFileDialogOpener.cs
@@ -1,8 +1,9 @@
 using System;
 using System.Collections.Generic;
+using TweetLib.Utils.Dialogs;
 
 namespace TweetLib.Browser.CEF.Interfaces {
 	public interface IFileDialogOpener {
-		void OpenFile(string title, bool multiple, List<string> supportedExtensions, Action<string[]> onAccepted, Action onCancelled);
+		void OpenFile(string title, bool multiple, List<FileDialogFilter> filters, Action<string[]> onAccepted, Action onCancelled);
 	}
 }
diff --git a/lib/TweetLib.Browser.CEF/Logic/DialogHandlerLogic.cs b/lib/TweetLib.Browser.CEF/Logic/DialogHandlerLogic.cs
index 920bcd3e..d85ab24b 100644
--- a/lib/TweetLib.Browser.CEF/Logic/DialogHandlerLogic.cs
+++ b/lib/TweetLib.Browser.CEF/Logic/DialogHandlerLogic.cs
@@ -1,9 +1,11 @@
+using System;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using TweetLib.Browser.CEF.Dialogs;
 using TweetLib.Browser.CEF.Interfaces;
+using TweetLib.Utils.Dialogs;
 using TweetLib.Utils.Static;
 
 namespace TweetLib.Browser.CEF.Logic {
@@ -19,11 +21,16 @@ public DialogHandlerLogic(IFileDialogOpener fileDialogOpener, IFileDialogCallbac
 		public bool OnFileDialog(FileDialogType type, IEnumerable<string> acceptFilters, TCallback callback) {
 			if (type is FileDialogType.Open or FileDialogType.OpenMultiple) {
 				var multiple = type == FileDialogType.OpenMultiple;
-				var supportedExtensions = acceptFilters.SelectMany(ParseFileType).Where(static filter => !string.IsNullOrEmpty(filter)).ToList();
+				var supportedExtensions = acceptFilters.SelectMany(ParseFileType).Where(static filter => !string.IsNullOrEmpty(filter)).ToArray();
+				
+				var filters = new List<FileDialogFilter> {
+					new ("All Supported Formats", supportedExtensions),
+					new ("All Files", ".*")
+				};
 
-				fileDialogOpener.OpenFile("Open Files", multiple, supportedExtensions, files => {
+				fileDialogOpener.OpenFile("Open Files", multiple, filters, files => {
 					string ext = Path.GetExtension(files[0])!.ToLower();
-					callbackAdapter.Continue(callback, supportedExtensions.FindIndex(filter => ParseFileType(filter).Contains(ext)), files);
+					callbackAdapter.Continue(callback, Array.FindIndex(supportedExtensions, filter => ParseFileType(filter).Contains(ext)), files);
 					callbackAdapter.Dispose(callback);
 				}, () => {
 					callbackAdapter.Cancel(callback);
diff --git a/lib/TweetLib.Core/Features/Chromium/SpellCheck.cs b/lib/TweetLib.Browser.CEF/Utils/SpellCheck.cs
similarity index 95%
rename from lib/TweetLib.Core/Features/Chromium/SpellCheck.cs
rename to lib/TweetLib.Browser.CEF/Utils/SpellCheck.cs
index c29af9d7..9016ed41 100644
--- a/lib/TweetLib.Core/Features/Chromium/SpellCheck.cs
+++ b/lib/TweetLib.Browser.CEF/Utils/SpellCheck.cs
@@ -3,7 +3,7 @@
 using TweetLib.Utils.Globalization;
 using TweetLib.Utils.Static;
 
-namespace TweetLib.Core.Features.Chromium {
+namespace TweetLib.Browser.CEF.Utils {
 	public static class SpellCheck {
 		// https://cs.chromium.org/chromium/src/third_party/hunspell_dictionaries/
 		public static IEnumerable<Language> SupportedLanguages { get; } = new List<string> {
diff --git a/lib/TweetLib.Core/Resources/ResourceCache.cs b/lib/TweetLib.Browser/Request/ResourceCache.cs
similarity index 86%
rename from lib/TweetLib.Core/Resources/ResourceCache.cs
rename to lib/TweetLib.Browser/Request/ResourceCache.cs
index ef8411a3..630324ce 100644
--- a/lib/TweetLib.Core/Resources/ResourceCache.cs
+++ b/lib/TweetLib.Browser/Request/ResourceCache.cs
@@ -2,18 +2,17 @@
 using System.Collections.Generic;
 using System.IO;
 using System.Net;
-using TweetLib.Browser.Request;
 using IOFile = System.IO.File;
 
-namespace TweetLib.Core.Resources {
+namespace TweetLib.Browser.Request {
 	public sealed class ResourceCache {
 		private readonly Dictionary<string, SchemeResource> cache = new ();
 
-		public void ClearCache() {
+		public void Clear() {
 			cache.Clear();
 		}
 
-		internal SchemeResource ReadFile(string path) {
+		public SchemeResource ReadFile(string path) {
 			string key = new Uri(path).LocalPath;
 
 			if (cache.TryGetValue(key, out var cachedResource)) {
diff --git a/lib/TweetLib.Core/App.cs b/lib/TweetLib.Core/App.cs
index e871d2d4..ff5399dc 100644
--- a/lib/TweetLib.Core/App.cs
+++ b/lib/TweetLib.Core/App.cs
@@ -1,10 +1,10 @@
 using System;
 using System.IO;
 using System.Linq;
+using TweetLib.Browser.Request;
 using TweetLib.Core.Application;
 using TweetLib.Core.Features;
 using TweetLib.Core.Features.Plugins;
-using TweetLib.Core.Resources;
 using TweetLib.Core.Systems.Configuration;
 using TweetLib.Core.Systems.Logging;
 using TweetLib.Utils.Static;
diff --git a/lib/TweetLib.Core/Application/IAppFileDialogs.cs b/lib/TweetLib.Core/Application/IAppFileDialogs.cs
index 80a89cc9..9d925ed8 100644
--- a/lib/TweetLib.Core/Application/IAppFileDialogs.cs
+++ b/lib/TweetLib.Core/Application/IAppFileDialogs.cs
@@ -1,5 +1,5 @@
 using System;
-using TweetLib.Core.Systems.Dialogs;
+using TweetLib.Utils.Dialogs;
 
 namespace TweetLib.Core.Application {
 	public interface IAppFileDialogs {
diff --git a/lib/TweetLib.Core/Application/IAppSetup.cs b/lib/TweetLib.Core/Application/IAppSetup.cs
index 72db9d33..0b447ba0 100644
--- a/lib/TweetLib.Core/Application/IAppSetup.cs
+++ b/lib/TweetLib.Core/Application/IAppSetup.cs
@@ -1,5 +1,5 @@
+using TweetLib.Browser.Request;
 using TweetLib.Core.Features.Plugins;
-using TweetLib.Core.Resources;
 using TweetLib.Core.Systems.Configuration;
 
 namespace TweetLib.Core.Application {
diff --git a/lib/TweetLib.Core/Features/FileDownloadManager.cs b/lib/TweetLib.Core/Features/FileDownloadManager.cs
index 865370f2..8d609fde 100644
--- a/lib/TweetLib.Core/Features/FileDownloadManager.cs
+++ b/lib/TweetLib.Core/Features/FileDownloadManager.cs
@@ -4,7 +4,7 @@
 using System.Linq;
 using TweetLib.Browser.Interfaces;
 using TweetLib.Core.Features.Twitter;
-using TweetLib.Core.Systems.Dialogs;
+using TweetLib.Utils.Dialogs;
 using TweetLib.Utils.Static;
 
 namespace TweetLib.Core.Features {
diff --git a/lib/TweetLib.Core/Features/Plugins/PluginSchemeHandler.cs b/lib/TweetLib.Core/Features/Plugins/PluginSchemeHandler.cs
index 0bb37525..531e9b1a 100644
--- a/lib/TweetLib.Core/Features/Plugins/PluginSchemeHandler.cs
+++ b/lib/TweetLib.Core/Features/Plugins/PluginSchemeHandler.cs
@@ -4,7 +4,6 @@
 using TweetLib.Browser.Interfaces;
 using TweetLib.Browser.Request;
 using TweetLib.Core.Features.Plugins.Enums;
-using TweetLib.Core.Resources;
 
 namespace TweetLib.Core.Features.Plugins {
 	public sealed class PluginSchemeHandler : ICustomSchemeHandler {
diff --git a/lib/TweetLib.Core/Features/TweetDeck/TweetDuckSchemeHandler.cs b/lib/TweetLib.Core/Features/TweetDeck/TweetDuckSchemeHandler.cs
index a7033c2e..cbc695c4 100644
--- a/lib/TweetLib.Core/Features/TweetDeck/TweetDuckSchemeHandler.cs
+++ b/lib/TweetLib.Core/Features/TweetDeck/TweetDuckSchemeHandler.cs
@@ -2,7 +2,6 @@
 using System.Net;
 using TweetLib.Browser.Interfaces;
 using TweetLib.Browser.Request;
-using TweetLib.Core.Resources;
 using TweetLib.Utils.Static;
 
 namespace TweetLib.Core.Features.TweetDeck {
diff --git a/lib/TweetLib.Core/Systems/Dialogs/FileDialogFilter.cs b/lib/TweetLib.Core/Systems/Dialogs/FileDialogFilter.cs
deleted file mode 100644
index bd518e5c..00000000
--- a/lib/TweetLib.Core/Systems/Dialogs/FileDialogFilter.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System.Collections.Generic;
-
-namespace TweetLib.Core.Systems.Dialogs {
-	public sealed class FileDialogFilter {
-		public string Name { get; }
-		public IReadOnlyList<string> Extensions { get; }
-
-		internal FileDialogFilter(string name, params string[] extensions) {
-			Name = name;
-			Extensions = extensions;
-		}
-	}
-}
diff --git a/lib/TweetLib.Core/Systems/Dialogs/SaveFileDialogSettings.cs b/lib/TweetLib.Core/Systems/Dialogs/SaveFileDialogSettings.cs
deleted file mode 100644
index c6615867..00000000
--- a/lib/TweetLib.Core/Systems/Dialogs/SaveFileDialogSettings.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Collections.Generic;
-
-namespace TweetLib.Core.Systems.Dialogs {
-	public sealed class SaveFileDialogSettings {
-		public string DialogTitle { get; internal set; } = "Save File";
-		public bool OverwritePrompt { get; internal set; } = true;
-		public string? FileName { get; internal set; }
-		public IReadOnlyList<FileDialogFilter>? Filters { get; internal set; }
-	}
-}
diff --git a/lib/TweetLib.Utils/Dialogs/FileDialogFilter.cs b/lib/TweetLib.Utils/Dialogs/FileDialogFilter.cs
new file mode 100644
index 00000000..ebdfbe79
--- /dev/null
+++ b/lib/TweetLib.Utils/Dialogs/FileDialogFilter.cs
@@ -0,0 +1,38 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+namespace TweetLib.Utils.Dialogs {
+	[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
+	[SuppressMessage("ReSharper", "UnusedMember.Global")]
+	public sealed class FileDialogFilter {
+		public string Name { get; }
+		public IReadOnlyList<string> Extensions { get; }
+
+		public string FullName => FullNameAndPattern.Item1;
+		public string Pattern => FullNameAndPattern.Item2;
+
+		private (string, string) FullNameAndPattern {
+			get {
+				if (Extensions.Count == 0) {
+					return (Name, "*.*");
+				}
+				else {
+					string pattern = string.Join(";", Extensions.Select(static ext => "*" + ext));
+					string fullName = Name + " (" + pattern + ")";
+					return (fullName, pattern);
+				}
+			}
+		}
+
+		public FileDialogFilter(string name, params string[] extensions) {
+			Name = name;
+			Extensions = extensions;
+		}
+
+		public string JoinFullNameAndPattern(string separator) {
+			var (fullName, pattern) = FullNameAndPattern;
+			return fullName + separator + pattern;
+		}
+	}
+}
diff --git a/lib/TweetLib.Utils/Dialogs/SaveFileDialogSettings.cs b/lib/TweetLib.Utils/Dialogs/SaveFileDialogSettings.cs
new file mode 100644
index 00000000..82945f1d
--- /dev/null
+++ b/lib/TweetLib.Utils/Dialogs/SaveFileDialogSettings.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+
+namespace TweetLib.Utils.Dialogs {
+	public sealed class SaveFileDialogSettings {
+		public string DialogTitle { get; set; } = "Save File";
+		public bool OverwritePrompt { get; set; } = true;
+		public string? FileName { get; set; }
+		public IReadOnlyList<FileDialogFilter>? Filters { get; set; }
+	}
+}
diff --git a/lib/TweetLib.Utils/Globalization/Language.cs b/lib/TweetLib.Utils/Globalization/Language.cs
index d4c51e99..5089a88c 100644
--- a/lib/TweetLib.Utils/Globalization/Language.cs
+++ b/lib/TweetLib.Utils/Globalization/Language.cs
@@ -3,6 +3,8 @@
 
 namespace TweetLib.Utils.Globalization {
 	public sealed class Language : IComparable<Language> {
+		public static Language English { get; } = new ("en-US");
+		
 		public string Code { get; }
 
 		private string Name => cultureInfo?.NativeName ?? Code;
diff --git a/lib/TweetLib.Core/Systems/Startup/LockFile.cs b/lib/TweetLib.Utils/Startup/LockFile.cs
similarity index 95%
rename from lib/TweetLib.Core/Systems/Startup/LockFile.cs
rename to lib/TweetLib.Utils/Startup/LockFile.cs
index f08e8043..25e17543 100644
--- a/lib/TweetLib.Core/Systems/Startup/LockFile.cs
+++ b/lib/TweetLib.Utils/Startup/LockFile.cs
@@ -5,7 +5,7 @@
 using System.Threading;
 using TweetLib.Utils.Static;
 
-namespace TweetLib.Core.Systems.Startup {
+namespace TweetLib.Utils.Startup {
 	public sealed class LockFile {
 		private static int CurrentProcessID {
 			get {
@@ -79,17 +79,16 @@ public LockResult LockWait(int timeout, int retryDelay) {
 			return Lock();
 		}
 
-		public bool Unlock() {
+		public UnlockResult Unlock() {
 			if (ReleaseLockFileStream()) {
 				try {
 					File.Delete(path);
 				} catch (Exception e) {
-					App.Logger.Error(e.ToString());
-					return false;
+					return new UnlockResult.Fail(e);
 				}
 			}
 
-			return true;
+			return UnlockResult.Success;
 		}
 
 		[SuppressMessage("ReSharper", "PossibleNullReferenceException")]
diff --git a/lib/TweetLib.Core/Systems/Startup/LockResult.cs b/lib/TweetLib.Utils/Startup/LockResult.cs
similarity index 94%
rename from lib/TweetLib.Core/Systems/Startup/LockResult.cs
rename to lib/TweetLib.Utils/Startup/LockResult.cs
index fc320fc0..fd83b19d 100644
--- a/lib/TweetLib.Core/Systems/Startup/LockResult.cs
+++ b/lib/TweetLib.Utils/Startup/LockResult.cs
@@ -1,7 +1,7 @@
 using System;
 using System.Diagnostics;
 
-namespace TweetLib.Core.Systems.Startup {
+namespace TweetLib.Utils.Startup {
 	public class LockResult {
 		private readonly string name;
 
diff --git a/lib/TweetLib.Utils/Startup/UnlockResult.cs b/lib/TweetLib.Utils/Startup/UnlockResult.cs
new file mode 100644
index 00000000..2b48bf4d
--- /dev/null
+++ b/lib/TweetLib.Utils/Startup/UnlockResult.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace TweetLib.Utils.Startup {
+	public class UnlockResult {
+		private readonly string name;
+
+		private UnlockResult(string name) {
+			this.name = name;
+		}
+
+		public override string ToString() {
+			return name;
+		}
+
+		public static UnlockResult Success { get; } = new ("Success");
+
+		public sealed class Fail : UnlockResult {
+			public Exception Exception { get; }
+
+			internal Fail(Exception exception) : base("Fail") {
+				this.Exception = exception;
+			}
+		}
+	}
+}
diff --git a/lib/TweetTest.Utils/Dialogs/TestFileDialogFilter.fs b/lib/TweetTest.Utils/Dialogs/TestFileDialogFilter.fs
new file mode 100644
index 00000000..98a968c3
--- /dev/null
+++ b/lib/TweetTest.Utils/Dialogs/TestFileDialogFilter.fs
@@ -0,0 +1,41 @@
+namespace TweetTest.Utils.Dialogs.FileDialogFilter
+
+open TweetLib.Utils.Dialogs
+open Xunit
+
+
+type internal TestData =
+    
+    static member noExtensions = FileDialogFilter("Any Filter")
+    static member oneExtension = FileDialogFilter("Single Filter", ".1")
+    static member twoExtensions = FileDialogFilter("Double Filter", ".1", ".2")
+
+
+module Pattern =
+    
+    [<Fact>]
+    let ``no extension assumes any files`` () =
+        Assert.Equal("*.*", TestData.noExtensions.Pattern)
+        
+    [<Fact>]
+    let ``one extension prepends with asterisk`` () =
+        Assert.Equal("*.1", TestData.oneExtension.Pattern)
+        
+    [<Fact>]
+    let ``multiple extensions are separated by semicolons`` () =
+        Assert.Equal("*.1;*.2", TestData.twoExtensions.Pattern)
+
+
+module FullName =
+    
+    [<Fact>]
+    let ``no extension keeps original name`` () =
+        Assert.Equal("Any Filter", TestData.noExtensions.FullName)
+        
+    [<Fact>]
+    let ``one extension appends pattern in parentheses`` () =
+        Assert.Equal("Single Filter (*.1)", TestData.oneExtension.FullName)
+        
+    [<Fact>]
+    let ``multiple extensions in pattern are separated by semicolons`` () =
+        Assert.Equal("Double Filter (*.1;*.2)", TestData.twoExtensions.FullName)
diff --git a/lib/TweetTest.Utils/TweetTest.Utils.fsproj b/lib/TweetTest.Utils/TweetTest.Utils.fsproj
index 01f93983..54f4a5ff 100644
--- a/lib/TweetTest.Utils/TweetTest.Utils.fsproj
+++ b/lib/TweetTest.Utils/TweetTest.Utils.fsproj
@@ -27,6 +27,7 @@
     <Compile Include="Collections\TestTwoKeyDictionary.fs" />
     <Compile Include="Data\TestInjectedString.fs" />
     <Compile Include="Data\TestResult.fs" />
+    <Compile Include="Dialogs\TestFileDialogFilter.fs" />
     <Compile Include="IO\TestCombinedFileStream.fs" />
     <Compile Include="Static\TestStringUtils.fs" />
   </ItemGroup>
diff --git a/windows/TweetDuck/Application/FileDialogs.cs b/windows/TweetDuck/Application/FileDialogs.cs
index b2907ec6..eecfa9fe 100644
--- a/windows/TweetDuck/Application/FileDialogs.cs
+++ b/windows/TweetDuck/Application/FileDialogs.cs
@@ -1,37 +1,20 @@
 using System;
 using System.Linq;
-using System.Text;
 using System.Windows.Forms;
 using TweetDuck.Management;
 using TweetLib.Core.Application;
-using TweetLib.Core.Systems.Dialogs;
+using TweetLib.Utils.Dialogs;
 
 namespace TweetDuck.Application {
 	sealed class FileDialogs : IAppFileDialogs {
 		public void SaveFile(SaveFileDialogSettings settings, Action<string> onAccepted) {
-			static string FormatFilter(FileDialogFilter filter) {
-				var builder = new StringBuilder();
-				builder.Append(filter.Name);
-
-				var extensions = string.Join(";", filter.Extensions.Select(ext => "*" + ext));
-				if (extensions.Length > 0) {
-					builder.Append(" (");
-					builder.Append(extensions);
-					builder.Append(")");
-				}
-
-				builder.Append('|');
-				builder.Append(extensions.Length == 0 ? "*.*" : extensions);
-				return builder.ToString();
-			}
-
 			FormManager.RunOnUIThreadAsync(() => {
 				using SaveFileDialog dialog = new SaveFileDialog {
 					AutoUpgradeEnabled = true,
 					OverwritePrompt = settings.OverwritePrompt,
 					Title = settings.DialogTitle,
 					FileName = settings.FileName,
-					Filter = settings.Filters == null ? null : string.Join("|", settings.Filters.Select(FormatFilter))
+					Filter = settings.Filters == null ? null : string.Join("|", settings.Filters.Select(filter => filter.JoinFullNameAndPattern("|")))
 				};
 
 				if (dialog.ShowDialog() == DialogResult.OK) {
diff --git a/windows/TweetDuck/Browser/FormBrowser.cs b/windows/TweetDuck/Browser/FormBrowser.cs
index 16e05468..ab1dc9e9 100644
--- a/windows/TweetDuck/Browser/FormBrowser.cs
+++ b/windows/TweetDuck/Browser/FormBrowser.cs
@@ -14,15 +14,19 @@
 using TweetDuck.Dialogs;
 using TweetDuck.Dialogs.Settings;
 using TweetDuck.Management;
+using TweetDuck.Properties;
 using TweetDuck.Updates;
 using TweetDuck.Utils;
+using TweetLib.Browser.Request;
 using TweetLib.Core;
 using TweetLib.Core.Features.Notifications;
 using TweetLib.Core.Features.Plugins;
 using TweetLib.Core.Features.TweetDeck;
-using TweetLib.Core.Resources;
 using TweetLib.Core.Systems.Configuration;
 using TweetLib.Core.Systems.Updates;
+#if DEBUG
+using TweetLib.Core.Resources;
+#endif
 
 namespace TweetDuck.Browser {
 	sealed partial class FormBrowser : Form, CustomKeyboardHandler.IBrowserKeyHandler {
@@ -143,7 +147,7 @@ private void RestoreWindow() {
 		}
 
 		private void UpdateFormIcon() { // TODO fix to show icon in taskbar too
-			Icon = Config.MuteNotifications ? Properties.Resources.icon_muted : Properties.Resources.icon;
+			Icon = Config.MuteNotifications ? Resources.icon_muted : Resources.icon;
 		}
 
 		private void UpdateTray() {
@@ -346,10 +350,10 @@ public void ResumeNotification(NotificationPauseReason reason) {
 		public void ReloadToTweetDeck() {
 			#if DEBUG
 			ResourceHotSwap.Run();
-			resourceCache.ClearCache();
+			resourceCache.Clear();
 			#else
 			if (ModifierKeys.HasFlag(Keys.Shift)) {
-				resourceCache.ClearCache();
+				resourceCache.Clear();
 			}
 			#endif
 
diff --git a/windows/TweetDuck/Dialogs/Settings/TabSettingsGeneral.cs b/windows/TweetDuck/Dialogs/Settings/TabSettingsGeneral.cs
index 435d4e45..a747faae 100644
--- a/windows/TweetDuck/Dialogs/Settings/TabSettingsGeneral.cs
+++ b/windows/TweetDuck/Dialogs/Settings/TabSettingsGeneral.cs
@@ -5,8 +5,8 @@
 using TweetDuck.Browser.Base;
 using TweetDuck.Controls;
 using TweetDuck.Utils;
+using TweetLib.Browser.CEF.Utils;
 using TweetLib.Core;
-using TweetLib.Core.Features.Chromium;
 using TweetLib.Core.Features.Twitter;
 using TweetLib.Core.Systems.Updates;
 using TweetLib.Utils.Globalization;
@@ -115,7 +115,7 @@ public TabSettingsGeneral(Action reloadTweetDeck, Action reloadColumns, UpdateCh
 					comboBoxSpellCheckLanguage.Items.Add(item);
 				}
 			} catch {
-				comboBoxSpellCheckLanguage.Items.Add(new Language("en-US"));
+				comboBoxSpellCheckLanguage.Items.Add(Language.English);
 			}
 
 			comboBoxSpellCheckLanguage.SelectedItem = new Language(Config.SpellCheckLanguage);
diff --git a/windows/TweetDuck/Management/LockManager.cs b/windows/TweetDuck/Management/LockManager.cs
index 277a1776..1b9c796f 100644
--- a/windows/TweetDuck/Management/LockManager.cs
+++ b/windows/TweetDuck/Management/LockManager.cs
@@ -4,7 +4,7 @@
 using TweetDuck.Dialogs;
 using TweetDuck.Utils;
 using TweetLib.Core;
-using TweetLib.Core.Systems.Startup;
+using TweetLib.Utils.Startup;
 
 namespace TweetDuck.Management {
 	sealed class LockManager {
@@ -26,15 +26,25 @@ public bool Lock(bool wasRestarted) {
 		}
 
 		public bool Unlock() {
-			return lockFile.Unlock();
+			UnlockResult result = lockFile.Unlock();
+
+			if (result is UnlockResult.Fail fail) {
+				App.Logger.Error(fail.Exception.ToString());
+				return false;
+			}
+			else if (result != UnlockResult.Success) {
+				return false;
+			}
+			
+			return true;
 		}
 
 		// Locking
 
 		private bool LaunchNormally() {
-			LockResult lockResult = lockFile.Lock();
+			LockResult result = lockFile.Lock();
 
-			if (lockResult is LockResult.HasProcess info) {
+			if (result is LockResult.HasProcess info) {
 				if (!RestoreProcess(info.Process, WindowRestoreMessage) && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)) {
 					if (!CloseProcess(info.Process)) {
 						FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
@@ -42,18 +52,18 @@ private bool LaunchNormally() {
 					}
 
 					info.Dispose();
-					lockResult = lockFile.Lock();
+					result = lockFile.Lock();
 				}
 				else {
 					return false;
 				}
 			}
 
-			if (lockResult is LockResult.Fail fail) {
+			if (result is LockResult.Fail fail) {
 				ShowGenericException(fail);
 				return false;
 			}
-			else if (lockResult != LockResult.Success) {
+			else if (result != LockResult.Success) {
 				FormMessage.Error("TweetDuck Has Failed :(", "An unknown error occurred accessing the data folder. Please, make sure TweetDuck is not already running. If the problem persists, try restarting your system.", FormMessage.OK);
 				return false;
 			}
@@ -62,10 +72,10 @@ private bool LaunchNormally() {
 		}
 
 		private bool LaunchAfterRestart() {
-			LockResult lockResult = lockFile.LockWait(10000, WaitRetryDelay);
+			LockResult result = lockFile.LockWait(10000, WaitRetryDelay);
 
-			while (lockResult != LockResult.Success) {
-				if (lockResult is LockResult.Fail fail) {
+			while (result != LockResult.Success) {
+				if (result is LockResult.Fail fail) {
 					ShowGenericException(fail);
 					return false;
 				}
@@ -73,7 +83,7 @@ private bool LaunchAfterRestart() {
 					return false;
 				}
 
-				lockResult = lockFile.LockWait(5000, WaitRetryDelay);
+				result = lockFile.LockWait(5000, WaitRetryDelay);
 			}
 
 			return true;
diff --git a/windows/TweetDuck/Program.cs b/windows/TweetDuck/Program.cs
index 07281684..53ff420d 100644
--- a/windows/TweetDuck/Program.cs
+++ b/windows/TweetDuck/Program.cs
@@ -13,12 +13,12 @@
 using TweetDuck.Utils;
 using TweetImpl.CefSharp.Handlers;
 using TweetLib.Browser.CEF.Utils;
+using TweetLib.Browser.Request;
 using TweetLib.Core;
 using TweetLib.Core.Application;
 using TweetLib.Core.Features.Plugins;
 using TweetLib.Core.Features.Plugins.Config;
 using TweetLib.Core.Features.TweetDeck;
-using TweetLib.Core.Resources;
 using TweetLib.Core.Systems.Configuration;
 using TweetLib.Utils.Collections;
 using Win = System.Windows.Forms;
diff --git a/windows/TweetImpl.CefSharp/Dialogs/FileDialogOpener.cs b/windows/TweetImpl.CefSharp/Dialogs/FileDialogOpener.cs
index b4a9ccf3..b19998fc 100644
--- a/windows/TweetImpl.CefSharp/Dialogs/FileDialogOpener.cs
+++ b/windows/TweetImpl.CefSharp/Dialogs/FileDialogOpener.cs
@@ -3,6 +3,7 @@
 using System.Linq;
 using System.Windows.Forms;
 using TweetLib.Browser.CEF.Interfaces;
+using TweetLib.Utils.Dialogs;
 
 namespace TweetImpl.CefSharp.Dialogs {
 	sealed class FileDialogOpener : IFileDialogOpener {
@@ -10,15 +11,13 @@ sealed class FileDialogOpener : IFileDialogOpener {
 
 		private FileDialogOpener() {}
 
-		public void OpenFile(string title, bool multiple, List<string> supportedExtensions, Action<string[]> onAccepted, Action onCancelled) {
-			string supportedFormatsFilter = string.Join(";", supportedExtensions.Select(filter => "*" + filter));
-			
+		public void OpenFile(string title, bool multiple, List<FileDialogFilter> filters, Action<string[]> onAccepted, Action onCancelled) {
 			using OpenFileDialog dialog = new OpenFileDialog {
 				AutoUpgradeEnabled = true,
 				DereferenceLinks = true,
 				Multiselect = multiple,
 				Title = title,
-				Filter = $"All Supported Formats ({supportedFormatsFilter})|{supportedFormatsFilter}|All Files (*.*)|*.*"
+				Filter = string.Join("|", filters.Select(filter => filter.JoinFullNameAndPattern("|")))
 			};
 
 			if (dialog.ShowDialog() == DialogResult.OK) {
diff --git a/windows/TweetImpl.CefSharp/TweetImpl.CefSharp.csproj b/windows/TweetImpl.CefSharp/TweetImpl.CefSharp.csproj
index 18e83a70..312dffdf 100644
--- a/windows/TweetImpl.CefSharp/TweetImpl.CefSharp.csproj
+++ b/windows/TweetImpl.CefSharp/TweetImpl.CefSharp.csproj
@@ -62,6 +62,10 @@
       <Project>{eefb1f37-7cad-46bd-8042-66e7b502ab02}</Project>
       <Name>TweetLib.Browser</Name>
     </ProjectReference>
+    <ProjectReference Include="..\..\lib\TweetLib.Utils\TweetLib.Utils.csproj">
+      <Project>{476b1007-b12c-447f-b855-9886048201d6}</Project>
+      <Name>TweetLib.Utils</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="..\..\Version.cs" Link="Version.cs" />