using System.Reflection; namespace Plandent.MstscScriptHook.Configurator; internal static class PluginInstaller { private const string ResourceName = "PlandentMstscScriptHook.Native.PlandentMstscScriptHook.dll"; public const string DllFileName = "PlandentMstscScriptHook.dll"; public static string InstallDirectory => Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Plandent", "MstscScriptHook"); public static string InstalledDllPath => Path.Combine(InstallDirectory, DllFileName); public static void Install() { Directory.CreateDirectory(InstallDirectory); ExtractPluginBinary(InstalledDllPath); RegistryConfig.RegisterPlugin(InstalledDllPath); } public static UninstallResult Uninstall() { RegistryConfig.UnregisterPlugin(); if (!File.Exists(InstalledDllPath)) { return new UninstallResult(true, "Plugin-Registrierung entfernt."); } try { File.Delete(InstalledDllPath); TryDeleteEmptyInstallDirectory(); return new UninstallResult(true, "Plugin-Registrierung und DLL wurden entfernt."); } catch (IOException) { return new UninstallResult( false, "Die Registrierung wurde entfernt. Die DLL wird noch von mstsc.exe verwendet. " + "Alle Remote-Desktop-Clients schließen und die Datei anschließend löschen bzw. erneut auf „Plugin entfernen“ klicken."); } catch (UnauthorizedAccessException ex) { return new UninstallResult(false, "Registrierung entfernt, DLL konnte aber nicht gelöscht werden: " + ex.Message); } } public static bool IsInstalled(out string status) { string? registered = RegistryConfig.GetRegisteredPluginPath(); if (string.IsNullOrWhiteSpace(registered)) { status = "Plugin ist nicht registriert."; return false; } string expanded = Environment.ExpandEnvironmentVariables(registered); if (!File.Exists(expanded)) { status = $"Plugin ist registriert, DLL fehlt jedoch: {expanded}"; return false; } status = $"Plugin ist registriert: {expanded}"; return true; } private static void ExtractPluginBinary(string destination) { Assembly assembly = Assembly.GetExecutingAssembly(); using Stream? resource = assembly.GetManifestResourceStream(ResourceName); if (resource is not null) { using FileStream output = new(destination, FileMode.Create, FileAccess.Write, FileShare.None); resource.CopyTo(output); return; } // Praktischer Fallback für Debugging/Entwicklung: DLL direkt neben der EXE akzeptieren. string adjacent = Path.Combine(AppContext.BaseDirectory, DllFileName); if (File.Exists(adjacent)) { File.Copy(adjacent, destination, overwrite: true); return; } throw new FileNotFoundException( "Die native Plugin-DLL ist weder als Resource eingebettet noch neben der Konfigurations-EXE vorhanden. " + "Bitte zuerst build-release.ps1 ausführen.", DllFileName); } private static void TryDeleteEmptyInstallDirectory() { try { if (Directory.Exists(InstallDirectory) && !Directory.EnumerateFileSystemEntries(InstallDirectory).Any()) { Directory.Delete(InstallDirectory); } } catch { // Unkritisch: ein leeres Verzeichnis darf bestehen bleiben. } } } internal sealed record UninstallResult(bool FullyRemoved, string Message);