diff --git a/Application/LockHandler.cs b/Application/LockHandler.cs
deleted file mode 100644
index fb768a00..00000000
--- a/Application/LockHandler.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Diagnostics;
-using TweetDuck.Utils;
-using TweetLib.Core.Application;
-
-namespace TweetDuck.Application{
-    class LockHandler : IAppLockHandler{
-        private const int WaitRetryDelay = 250;
-        private const int RestoreFailTimeout = 2000;
-        private const int CloseNaturallyTimeout = 10000;
-        private const int CloseKillTimeout = 5000;
-
-        bool IAppLockHandler.RestoreProcess(Process process){
-            if (process.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
-                NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)process.Id, 0);
-
-                if (WindowsUtils.TrySleepUntil(() => CheckProcessExited(process) || (process.MainWindowHandle != IntPtr.Zero && process.Responding), RestoreFailTimeout, WaitRetryDelay)){
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        bool IAppLockHandler.CloseProcess(Process process){
-            try{
-                if (process.CloseMainWindow()){
-                    // ReSharper disable once AccessToDisposedClosure
-                    WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseNaturallyTimeout, WaitRetryDelay);
-                }
-
-                if (!process.HasExited){
-                    process.Kill();
-                    // ReSharper disable once AccessToDisposedClosure
-                    WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseKillTimeout, WaitRetryDelay);
-                }
-
-                if (process.HasExited){
-                    process.Dispose();
-                    return true;
-                }
-                else{
-                    return false;
-                }
-            }catch(Exception ex) when (ex is InvalidOperationException || ex is Win32Exception){
-                bool hasExited = CheckProcessExited(process);
-                process.Dispose();
-                return hasExited;
-            }
-        }
-
-        private static bool CheckProcessExited(Process process){
-            process.Refresh();
-            return process.HasExited;
-        }
-    }
-}
diff --git a/Management/LockManager.cs b/Management/LockManager.cs
new file mode 100644
index 00000000..61b08c1d
--- /dev/null
+++ b/Management/LockManager.cs
@@ -0,0 +1,129 @@
+using System;
+using System.ComponentModel;
+using System.Diagnostics;
+using TweetDuck.Dialogs;
+using TweetDuck.Utils;
+using TweetLib.Core.Systems.Startup;
+
+namespace TweetDuck.Management{
+    sealed class LockManager{
+        private const int WaitRetryDelay = 250;
+        private const int RestoreFailTimeout = 2000;
+        private const int CloseNaturallyTimeout = 10000;
+        private const int CloseKillTimeout = 5000;
+
+        private readonly LockFile lockFile;
+
+        public LockManager(string path){
+            this.lockFile = new LockFile(path);
+        }
+
+        public bool Lock(bool wasRestarted){
+            return wasRestarted ? LaunchAfterRestart() : LaunchNormally();
+        }
+
+        public bool Unlock(){
+            return lockFile.Unlock();
+        }
+
+        // Locking
+
+        private bool LaunchNormally(){
+            LockResult lockResult = lockFile.Lock();
+                
+            if (lockResult is LockResult.HasProcess info){
+                if (!RestoreProcess(info.Process) && 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);
+                        return false;
+                    }
+
+                    info.Dispose();
+                    lockResult = lockFile.Lock();
+                }
+                else{
+                    return false;
+                }
+            }
+
+            if (lockResult is LockResult.Fail fail){
+                ShowGenericException(fail);
+                return false;
+            }
+            else if (lockResult != 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;
+            }
+
+            return true;
+        }
+
+        private bool LaunchAfterRestart(){
+            LockResult lockResult = lockFile.LockWait(10000, WaitRetryDelay);
+
+            while(lockResult != LockResult.Success){
+                if (lockResult is LockResult.Fail fail){
+                    ShowGenericException(fail);
+                    return false;
+                }
+                else if (!FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)){
+                    return false;
+                }
+
+                lockResult = lockFile.LockWait(5000, WaitRetryDelay);
+            }
+
+            return true;
+        }
+
+        // Helpers
+
+        private static void ShowGenericException(LockResult.Fail fail){
+            Program.Reporter.HandleException("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.", false, fail.Exception);
+        }
+
+        private static bool RestoreProcess(Process process){
+            if (process.MainWindowHandle == IntPtr.Zero){ // restore if the original process is in tray
+                NativeMethods.BroadcastMessage(Program.WindowRestoreMessage, (uint)process.Id, 0);
+
+                if (WindowsUtils.TrySleepUntil(() => CheckProcessExited(process) || (process.MainWindowHandle != IntPtr.Zero && process.Responding), RestoreFailTimeout, WaitRetryDelay)){
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        private static bool CloseProcess(Process process){
+            try{
+                if (process.CloseMainWindow()){
+                    // ReSharper disable once AccessToDisposedClosure
+                    WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseNaturallyTimeout, WaitRetryDelay);
+                }
+
+                if (!process.HasExited){
+                    process.Kill();
+                    // ReSharper disable once AccessToDisposedClosure
+                    WindowsUtils.TrySleepUntil(() => CheckProcessExited(process), CloseKillTimeout, WaitRetryDelay);
+                }
+
+                if (process.HasExited){
+                    process.Dispose();
+                    return true;
+                }
+                else{
+                    return false;
+                }
+            }catch(Exception ex) when (ex is InvalidOperationException || ex is Win32Exception){
+                bool hasExited = CheckProcessExited(process);
+                process.Dispose();
+                return hasExited;
+            }
+        }
+
+        private static bool CheckProcessExited(Process process){
+            process.Refresh();
+            return process.HasExited;
+        }
+    }
+}
diff --git a/Program.cs b/Program.cs
index 26e8f438..226afb38 100644
--- a/Program.cs
+++ b/Program.cs
@@ -16,7 +16,6 @@
 using TweetDuck.Utils;
 using TweetLib.Core;
 using TweetLib.Core.Collections;
-using TweetLib.Core.Systems.Startup;
 using TweetLib.Core.Utils;
 using Win = System.Windows.Forms;
 
@@ -72,7 +71,6 @@ static Program(){
 
             Lib.Initialize(new App.Builder{
                 ErrorHandler = Reporter,
-                LockHandler = new LockHandler(),
                 SystemHandler = new SystemHandler(),
                 ResourceHandler = Resources
             });
@@ -95,42 +93,8 @@ private static void Main(){
                 return;
             }
             
-            if (Arguments.HasFlag(Arguments.ArgRestart)){
-                LockManager.Result lockResult = LockManager.LockWait(10000);
-
-                while(lockResult != LockManager.Result.Success){
-                    if (lockResult == LockManager.Result.Fail){
-                        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;
-                    }
-                    else if (!FormMessage.Warning("TweetDuck Cannot Restart", "TweetDuck is taking too long to close.", FormMessage.Retry, FormMessage.Exit)){
-                        return;
-                    }
-
-                    lockResult = LockManager.LockWait(5000);
-                }
-            }
-            else{
-                LockManager.Result lockResult = LockManager.Lock();
-                
-                if (lockResult == LockManager.Result.HasProcess){
-                    if (!LockManager.RestoreLockingProcess() && FormMessage.Error("TweetDuck is Already Running", "Another instance of TweetDuck is already running.\nDo you want to close it?", FormMessage.Yes, FormMessage.No)){
-                        if (!LockManager.CloseLockingProcess()){
-                            FormMessage.Error("TweetDuck Has Failed :(", "Could not close the other process.", FormMessage.OK);
-                            return;
-                        }
-
-                        lockResult = LockManager.Lock();
-                    }
-                    else{
-                        return;
-                    }
-                }
-
-                if (lockResult != LockManager.Result.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;
-                }
+            if (!LockManager.Lock(Arguments.HasFlag(Arguments.ArgRestart))){
+                return;
             }
             
             Config.LoadAll();
diff --git a/TweetDuck.csproj b/TweetDuck.csproj
index 2a6b5750..f5105c85 100644
--- a/TweetDuck.csproj
+++ b/TweetDuck.csproj
@@ -53,7 +53,7 @@
     <Reference Include="System.Windows.Forms" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="Application\LockHandler.cs" />
+    <Compile Include="Management\LockManager.cs" />
     <Compile Include="Application\SystemHandler.cs" />
     <Compile Include="Browser\Adapters\CefScriptExecutor.cs" />
     <Compile Include="Browser\Bridge\PropertyBridge.cs" />
@@ -436,4 +436,4 @@ IF EXIST "$(ProjectDir)bld\post_build.exe" (
   </Target>
   <Import Project="packages\CefSharp.Common.81.3.100\build\CefSharp.Common.targets" Condition="Exists('packages\CefSharp.Common.81.3.100\build\CefSharp.Common.targets')" />
   <Import Project="packages\CefSharp.WinForms.81.3.100\build\CefSharp.WinForms.targets" Condition="Exists('packages\CefSharp.WinForms.81.3.100\build\CefSharp.WinForms.targets')" />
-</Project>
+</Project>
\ No newline at end of file
diff --git a/lib/TweetLib.Core/App.cs b/lib/TweetLib.Core/App.cs
index eacc6019..73499cfe 100644
--- a/lib/TweetLib.Core/App.cs
+++ b/lib/TweetLib.Core/App.cs
@@ -5,7 +5,6 @@ namespace TweetLib.Core{
     public sealed class App{
         #pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
         public static IAppErrorHandler ErrorHandler { get; private set; }
-        public static IAppLockHandler LockHandler { get; private set; }
         public static IAppSystemHandler SystemHandler { get; private set; }
         public static IAppResourceHandler ResourceHandler { get; private set; }
         #pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable.
@@ -14,7 +13,6 @@ public sealed class App{
 
         public sealed class Builder{
             public IAppErrorHandler? ErrorHandler { get; set; }
-            public IAppLockHandler? LockHandler { get; set; }
             public IAppSystemHandler? SystemHandler { get; set; }
             public IAppResourceHandler? ResourceHandler { get; set; }
 
@@ -22,7 +20,6 @@ public sealed class Builder{
 
             internal void Initialize(){
                 App.ErrorHandler = Validate(ErrorHandler, nameof(ErrorHandler))!;
-                App.LockHandler = Validate(LockHandler, nameof(LockHandler))!;
                 App.SystemHandler = Validate(SystemHandler, nameof(SystemHandler))!;
                 App.ResourceHandler = Validate(ResourceHandler, nameof(ResourceHandler))!;
             }
diff --git a/lib/TweetLib.Core/Application/IAppLockHandler.cs b/lib/TweetLib.Core/Application/IAppLockHandler.cs
deleted file mode 100644
index 03bf89b8..00000000
--- a/lib/TweetLib.Core/Application/IAppLockHandler.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using System.Diagnostics;
-
-namespace TweetLib.Core.Application{
-    public interface IAppLockHandler{
-        bool RestoreProcess(Process process);
-        bool CloseProcess(Process process);
-    }
-}
diff --git a/lib/TweetLib.Core/Systems/Startup/LockFile.cs b/lib/TweetLib.Core/Systems/Startup/LockFile.cs
new file mode 100644
index 00000000..3da6ae43
--- /dev/null
+++ b/lib/TweetLib.Core/Systems/Startup/LockFile.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using TweetLib.Core.Utils;
+
+namespace TweetLib.Core.Systems.Startup{
+    public sealed class LockFile{
+        private static int CurrentProcessID{
+            get{
+                using Process me = Process.GetCurrentProcess();
+                return me.Id;
+            }
+        }
+
+        private readonly string path;
+        private FileStream? stream;
+
+        public LockFile(string path){
+            this.path = path;
+        }
+
+        private void CreateLockFileStream(){
+            stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read);
+            stream.Write(BitConverter.GetBytes(CurrentProcessID), 0, sizeof(int));
+            stream.Flush(true);
+        }
+
+        private bool ReleaseLockFileStream(){
+            if (stream != null){
+                stream.Dispose();
+                stream = null;
+                return true;
+            }
+            else{
+                return false;
+            }
+        }
+
+        public LockResult Lock(){
+            if (stream != null){
+                return LockResult.Success;
+            }
+
+            try{
+                CreateLockFileStream();
+                return LockResult.Success;
+            }catch(DirectoryNotFoundException){
+                try{
+                    FileUtils.CreateDirectoryForFile(path);
+                    CreateLockFileStream();
+                    return LockResult.Success;
+                }catch(Exception e){
+                    ReleaseLockFileStream();
+                    return new LockResult.Fail(e);
+                }
+            }catch(IOException e){
+                return DetermineLockingProcessOrFail(e);
+            }catch(Exception e){
+                ReleaseLockFileStream();
+                return new LockResult.Fail(e);
+            }
+        }
+
+        public LockResult LockWait(int timeout, int retryDelay){
+            for(int elapsed = 0; elapsed < timeout; elapsed += retryDelay){
+                var result = Lock();
+
+                if (result is LockResult.HasProcess info){
+                    info.Dispose();
+                    Thread.Sleep(retryDelay);
+                }
+                else{
+                    return result;
+                }
+            }
+
+            return Lock();
+        }
+
+        public bool Unlock(){
+            if (ReleaseLockFileStream()){
+                try{
+                    File.Delete(path);
+                }catch(Exception e){
+                    App.ErrorHandler.Log(e.ToString());
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private LockResult DetermineLockingProcessOrFail(Exception originalException){
+            try{
+                int pid;
+
+                using(var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
+                    byte[] bytes = new byte[sizeof(int)];
+                    fileStream.Read(bytes, 0, bytes.Length);
+                    pid = BitConverter.ToInt32(bytes, 0);
+                }
+
+                try{
+                    var foundProcess = Process.GetProcessById(pid);
+                    using var currentProcess = Process.GetCurrentProcess();
+
+                    if (currentProcess.MainModule.FileVersionInfo.InternalName == foundProcess.MainModule.FileVersionInfo.InternalName){
+                        return new LockResult.HasProcess(foundProcess);
+                    }
+                    else{
+                        foundProcess.Close();
+                    }
+                }catch{
+                    // GetProcessById throws ArgumentException if the process is missing
+                    // Process.MainModule can throw exceptions in some cases
+                }
+
+                return new LockResult.Fail(originalException);
+            }catch(Exception e){
+                return new LockResult.Fail(e);
+            }
+        }
+    }
+}
diff --git a/lib/TweetLib.Core/Systems/Startup/LockManager.cs b/lib/TweetLib.Core/Systems/Startup/LockManager.cs
deleted file mode 100644
index 6bbe852f..00000000
--- a/lib/TweetLib.Core/Systems/Startup/LockManager.cs
+++ /dev/null
@@ -1,162 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.Threading;
-
-namespace TweetLib.Core.Systems.Startup{
-    public sealed class LockManager{
-        private const int RetryDelay = 250;
-
-        public enum Result{
-            Success, HasProcess, Fail
-        }
-
-        private readonly string file;
-        private FileStream? lockStream;
-        private Process? lockingProcess;
-
-        public LockManager(string file){
-            this.file = file;
-        }
-
-        // Lock file
-
-        private bool ReleaseLockFileStream(){
-            if (lockStream != null){
-                lockStream.Dispose();
-                lockStream = null;
-                return true;
-            }
-            else{
-                return false;
-            }
-        }
-
-        private Result TryCreateLockFile(){
-            void CreateLockFileStream(){
-                lockStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read);
-                lockStream.Write(BitConverter.GetBytes(CurrentProcessID), 0, sizeof(int));
-                lockStream.Flush(true);
-            }
-
-            try{
-                CreateLockFileStream();
-                return Result.Success;
-            }catch(DirectoryNotFoundException){
-                try{
-                    CreateLockFileStream();
-                    return Result.Success;
-                }catch{
-                    ReleaseLockFileStream();
-                    return Result.Fail;
-                }
-            }catch(IOException){
-                return Result.HasProcess;
-            }catch{
-                ReleaseLockFileStream();
-                return Result.Fail;
-            }
-        }
-
-        // Lock management
-
-        public Result Lock(){
-            if (lockStream != null){
-                return Result.Success;
-            }
-
-            Result initialResult = TryCreateLockFile();
-
-            if (initialResult == Result.HasProcess){
-                try{
-                    int pid;
-
-                    using(FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)){
-                        byte[] bytes = new byte[sizeof(int)];
-                        fileStream.Read(bytes, 0, bytes.Length);
-                        pid = BitConverter.ToInt32(bytes, 0);
-                    }
-
-                    try{
-                        Process foundProcess = Process.GetProcessById(pid);
-
-                        if (MatchesCurrentProcess(foundProcess)){
-                            lockingProcess = foundProcess;
-                        }
-                        else{
-                            foundProcess.Close();
-                        }
-                    }catch{
-                        // GetProcessById throws ArgumentException if the process is missing
-                        // Process.MainModule can throw exceptions in some cases
-                    }
-
-                    return lockingProcess == null ? Result.Fail : Result.HasProcess;
-                }catch{
-                    return Result.Fail;
-                }
-            }
-
-            return initialResult;
-        }
-
-        public Result LockWait(int timeout){
-            for(int elapsed = 0; elapsed < timeout; elapsed += RetryDelay){
-                Result result = Lock();
-
-                if (result == Result.HasProcess){
-                    Thread.Sleep(RetryDelay);
-                }
-                else{
-                    return result;
-                }
-            }
-
-            return Lock();
-        }
-
-        public bool Unlock(){
-            if (ReleaseLockFileStream()){
-                try{
-                    File.Delete(file);
-                }catch(Exception e){
-                    App.ErrorHandler.Log(e.ToString());
-                    return false;
-                }
-            }
-
-            return true;
-        }
-
-        // Locking process
-        
-        public bool RestoreLockingProcess(){
-            return lockingProcess != null && App.LockHandler.RestoreProcess(lockingProcess);
-        }
-
-        public bool CloseLockingProcess(){
-            if (lockingProcess != null && App.LockHandler.CloseProcess(lockingProcess)){
-                lockingProcess = null;
-                return true;
-            }
-
-            return false;
-        }
-
-        // Utilities
-
-        private static int CurrentProcessID{
-            get{
-                using Process me = Process.GetCurrentProcess();
-                return me.Id;
-            }
-        }
-
-        [SuppressMessage("ReSharper", "PossibleNullReferenceException")]
-        private static bool MatchesCurrentProcess(Process process){
-            using Process current = Process.GetCurrentProcess();
-            return current.MainModule.FileVersionInfo.InternalName == process.MainModule.FileVersionInfo.InternalName;
-        }
-    }
-}
diff --git a/lib/TweetLib.Core/Systems/Startup/LockResult.cs b/lib/TweetLib.Core/Systems/Startup/LockResult.cs
new file mode 100644
index 00000000..7314cfa3
--- /dev/null
+++ b/lib/TweetLib.Core/Systems/Startup/LockResult.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Diagnostics;
+
+namespace TweetLib.Core.Systems.Startup{
+    public class LockResult{
+        private readonly string name;
+
+        private LockResult(string name){
+            this.name = name;
+        }
+
+        public override string ToString(){
+            return name;
+        }
+
+        public static LockResult Success { get; } = new LockResult("Success");
+
+        public sealed class Fail : LockResult{
+            public Exception Exception { get; }
+
+            public Fail(Exception exception) : base("Fail"){
+                this.Exception = exception;
+            }
+        }
+
+        public sealed class HasProcess : LockResult, IDisposable{
+            public Process Process { get; }
+
+            public HasProcess(Process process) : base("HasProcess"){
+                this.Process = process;
+            }
+
+            public void Dispose(){
+                Process.Dispose();
+            }
+        }
+    }
+}