469 lines
16 KiB
C#
469 lines
16 KiB
C#
using System.Drawing;
|
|
using System.Windows.Forms;
|
|
using System.Diagnostics;
|
|
|
|
namespace Plandent.MstscScriptHook.Configurator;
|
|
|
|
internal sealed class MainForm : Form
|
|
{
|
|
private readonly TextBox scriptPathTextBox = new() { Dock = DockStyle.Fill };
|
|
private readonly TextBox argumentsTextBox = new() { Dock = DockStyle.Fill };
|
|
private readonly TextBox workingDirectoryTextBox = new() { Dock = DockStyle.Fill };
|
|
|
|
private readonly CheckBox enabledCheckBox = new() { Text = "Plugin-Funktion aktiviert", AutoSize = true };
|
|
private readonly CheckBox startOnConnectCheckBox = new() { Text = "Script bei erfolgreicher RDP-Verbindung starten", AutoSize = true };
|
|
private readonly CheckBox stopOnDisconnectCheckBox = new() { Text = "Script bei RDP-Trennung beenden", AutoSize = true };
|
|
private readonly CheckBox hiddenCheckBox = new() { Text = "Script unsichtbar starten", AutoSize = true };
|
|
private readonly CheckBox preventDuplicatesCheckBox = new() { Text = "Mehrfachstart verhindern", AutoSize = true };
|
|
private readonly CheckBox loggingCheckBox = new() { Text = "Plugin-Logging aktivieren", AutoSize = true };
|
|
|
|
private readonly Label statusLabel = new()
|
|
{
|
|
AutoSize = false,
|
|
Dock = DockStyle.Fill,
|
|
TextAlign = ContentAlignment.MiddleLeft,
|
|
Padding = new Padding(8)
|
|
};
|
|
|
|
private readonly Button stopTestButton = new() { Text = "Test beenden", AutoSize = true, Enabled = false };
|
|
private Process? testProcess;
|
|
|
|
public MainForm()
|
|
{
|
|
Text = "Plandent MSTSC Script Hook";
|
|
StartPosition = FormStartPosition.CenterScreen;
|
|
MinimumSize = new Size(760, 590);
|
|
Size = new Size(820, 640);
|
|
Font = new Font("Segoe UI", 9F);
|
|
|
|
Controls.Add(BuildMainLayout());
|
|
Load += (_, _) => LoadConfiguration();
|
|
FormClosed += (_, _) => DisposeTestProcessHandle();
|
|
}
|
|
|
|
private Control BuildMainLayout()
|
|
{
|
|
var root = new TableLayoutPanel
|
|
{
|
|
Dock = DockStyle.Fill,
|
|
Padding = new Padding(18),
|
|
ColumnCount = 1,
|
|
RowCount = 6
|
|
};
|
|
root.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
|
root.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
|
root.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
|
root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
|
|
root.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
|
root.RowStyles.Add(new RowStyle(SizeType.AutoSize));
|
|
|
|
var title = new Label
|
|
{
|
|
Text = "MSTSC Script Hook",
|
|
Font = new Font(Font.FontFamily, 16F, FontStyle.Bold),
|
|
AutoSize = true,
|
|
Margin = new Padding(0, 0, 0, 4)
|
|
};
|
|
root.Controls.Add(title, 0, 0);
|
|
|
|
var description = new Label
|
|
{
|
|
Text = "Startet bei einer erfolgreichen klassischen Remotedesktop-Verbindung lokal ein konfiguriertes Script im Hintergrund.",
|
|
AutoSize = true,
|
|
MaximumSize = new Size(740, 0),
|
|
Margin = new Padding(0, 0, 0, 16)
|
|
};
|
|
root.Controls.Add(description, 0, 1);
|
|
|
|
root.Controls.Add(BuildFileGrid(), 0, 2);
|
|
root.Controls.Add(BuildOptionsGroup(), 0, 3);
|
|
root.Controls.Add(BuildStatusPanel(), 0, 4);
|
|
root.Controls.Add(BuildButtons(), 0, 5);
|
|
return root;
|
|
}
|
|
|
|
private Control BuildFileGrid()
|
|
{
|
|
var grid = new TableLayoutPanel
|
|
{
|
|
Dock = DockStyle.Top,
|
|
AutoSize = true,
|
|
ColumnCount = 3,
|
|
RowCount = 3,
|
|
Margin = new Padding(0, 0, 0, 14)
|
|
};
|
|
grid.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
|
|
grid.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
|
|
grid.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
|
|
|
|
grid.Controls.Add(CreateFieldLabel("Script:"), 0, 0);
|
|
grid.Controls.Add(scriptPathTextBox, 1, 0);
|
|
var browseScriptButton = new Button { Text = "…", Width = 42, Height = scriptPathTextBox.PreferredHeight + 4 };
|
|
browseScriptButton.Click += (_, _) => BrowseScript();
|
|
grid.Controls.Add(browseScriptButton, 2, 0);
|
|
|
|
grid.Controls.Add(CreateFieldLabel("Argumente:"), 0, 1);
|
|
grid.Controls.Add(argumentsTextBox, 1, 1);
|
|
grid.SetColumnSpan(argumentsTextBox, 2);
|
|
|
|
grid.Controls.Add(CreateFieldLabel("Arbeitsverzeichnis:"), 0, 2);
|
|
grid.Controls.Add(workingDirectoryTextBox, 1, 2);
|
|
var browseWorkingButton = new Button { Text = "…", Width = 42, Height = workingDirectoryTextBox.PreferredHeight + 4 };
|
|
browseWorkingButton.Click += (_, _) => BrowseWorkingDirectory();
|
|
grid.Controls.Add(browseWorkingButton, 2, 2);
|
|
|
|
return grid;
|
|
}
|
|
|
|
private static Label CreateFieldLabel(string text)
|
|
{
|
|
return new Label
|
|
{
|
|
Text = text,
|
|
AutoSize = true,
|
|
Anchor = AnchorStyles.Left,
|
|
Margin = new Padding(0, 6, 12, 6)
|
|
};
|
|
}
|
|
|
|
private Control BuildOptionsGroup()
|
|
{
|
|
var group = new GroupBox
|
|
{
|
|
Text = "Verhalten",
|
|
Dock = DockStyle.Top,
|
|
AutoSize = true,
|
|
Padding = new Padding(12),
|
|
Margin = new Padding(0, 0, 0, 14)
|
|
};
|
|
|
|
var options = new FlowLayoutPanel
|
|
{
|
|
Dock = DockStyle.Top,
|
|
FlowDirection = FlowDirection.TopDown,
|
|
WrapContents = false,
|
|
AutoSize = true
|
|
};
|
|
options.Controls.Add(enabledCheckBox);
|
|
options.Controls.Add(startOnConnectCheckBox);
|
|
options.Controls.Add(stopOnDisconnectCheckBox);
|
|
options.Controls.Add(hiddenCheckBox);
|
|
options.Controls.Add(preventDuplicatesCheckBox);
|
|
options.Controls.Add(loggingCheckBox);
|
|
group.Controls.Add(options);
|
|
return group;
|
|
}
|
|
|
|
private Control BuildStatusPanel()
|
|
{
|
|
var panel = new Panel
|
|
{
|
|
Dock = DockStyle.Top,
|
|
Height = 58,
|
|
BorderStyle = BorderStyle.FixedSingle,
|
|
Margin = new Padding(0, 0, 0, 14)
|
|
};
|
|
panel.Controls.Add(statusLabel);
|
|
return panel;
|
|
}
|
|
|
|
private Control BuildButtons()
|
|
{
|
|
var buttons = new FlowLayoutPanel
|
|
{
|
|
Dock = DockStyle.Top,
|
|
AutoSize = true,
|
|
FlowDirection = FlowDirection.LeftToRight,
|
|
WrapContents = true,
|
|
Margin = new Padding(0)
|
|
};
|
|
|
|
var testButton = new Button { Text = "Script testen", AutoSize = true };
|
|
testButton.Click += (_, _) => StartTest();
|
|
stopTestButton.Click += (_, _) => StopTest();
|
|
|
|
var saveButton = new Button { Text = "Konfiguration speichern", AutoSize = true };
|
|
saveButton.Click += (_, _) => SaveConfiguration(showConfirmation: true);
|
|
|
|
var installButton = new Button { Text = "Plugin installieren", AutoSize = true };
|
|
installButton.Click += (_, _) => InstallPlugin();
|
|
|
|
var removeButton = new Button { Text = "Plugin entfernen", AutoSize = true };
|
|
removeButton.Click += (_, _) => RemovePlugin();
|
|
|
|
var logButton = new Button { Text = "Log öffnen", AutoSize = true };
|
|
logButton.Click += (_, _) => OpenLog();
|
|
|
|
buttons.Controls.Add(testButton);
|
|
buttons.Controls.Add(stopTestButton);
|
|
buttons.Controls.Add(saveButton);
|
|
buttons.Controls.Add(installButton);
|
|
buttons.Controls.Add(removeButton);
|
|
buttons.Controls.Add(logButton);
|
|
return buttons;
|
|
}
|
|
|
|
private void BrowseScript()
|
|
{
|
|
using var dialog = new OpenFileDialog
|
|
{
|
|
Title = "Script oder Programm auswählen",
|
|
Filter = "Unterstützte Dateien (*.bat;*.cmd;*.ps1;*.exe)|*.bat;*.cmd;*.ps1;*.exe|Alle Dateien (*.*)|*.*",
|
|
CheckFileExists = true
|
|
};
|
|
|
|
if (!string.IsNullOrWhiteSpace(scriptPathTextBox.Text))
|
|
{
|
|
string expanded = Environment.ExpandEnvironmentVariables(scriptPathTextBox.Text);
|
|
if (File.Exists(expanded))
|
|
{
|
|
dialog.InitialDirectory = Path.GetDirectoryName(expanded);
|
|
dialog.FileName = Path.GetFileName(expanded);
|
|
}
|
|
}
|
|
|
|
if (dialog.ShowDialog(this) != DialogResult.OK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
scriptPathTextBox.Text = dialog.FileName;
|
|
if (string.IsNullOrWhiteSpace(workingDirectoryTextBox.Text))
|
|
{
|
|
workingDirectoryTextBox.Text = Path.GetDirectoryName(dialog.FileName) ?? string.Empty;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(argumentsTextBox.Text) &&
|
|
Path.GetFileName(dialog.FileName).Contains("RemoteVDDS_Receiver_TS", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
argumentsTextBox.Text = "MIN";
|
|
}
|
|
}
|
|
|
|
private void BrowseWorkingDirectory()
|
|
{
|
|
using var dialog = new FolderBrowserDialog
|
|
{
|
|
Description = "Arbeitsverzeichnis auswählen",
|
|
UseDescriptionForTitle = true,
|
|
ShowNewFolderButton = true
|
|
};
|
|
|
|
string expanded = Environment.ExpandEnvironmentVariables(workingDirectoryTextBox.Text);
|
|
if (Directory.Exists(expanded))
|
|
{
|
|
dialog.SelectedPath = expanded;
|
|
}
|
|
|
|
if (dialog.ShowDialog(this) == DialogResult.OK)
|
|
{
|
|
workingDirectoryTextBox.Text = dialog.SelectedPath;
|
|
}
|
|
}
|
|
|
|
private HookConfig ReadForm()
|
|
{
|
|
return new HookConfig
|
|
{
|
|
Enabled = enabledCheckBox.Checked,
|
|
ScriptPath = scriptPathTextBox.Text.Trim(),
|
|
Arguments = argumentsTextBox.Text.Trim(),
|
|
WorkingDirectory = workingDirectoryTextBox.Text.Trim(),
|
|
StartOnConnect = startOnConnectCheckBox.Checked,
|
|
StopOnDisconnect = stopOnDisconnectCheckBox.Checked,
|
|
Hidden = hiddenCheckBox.Checked,
|
|
PreventDuplicates = preventDuplicatesCheckBox.Checked,
|
|
EnableLogging = loggingCheckBox.Checked
|
|
};
|
|
}
|
|
|
|
private void LoadConfiguration()
|
|
{
|
|
HookConfig config = RegistryConfig.Load();
|
|
scriptPathTextBox.Text = config.ScriptPath;
|
|
argumentsTextBox.Text = config.Arguments;
|
|
workingDirectoryTextBox.Text = config.WorkingDirectory;
|
|
enabledCheckBox.Checked = config.Enabled;
|
|
startOnConnectCheckBox.Checked = config.StartOnConnect;
|
|
stopOnDisconnectCheckBox.Checked = config.StopOnDisconnect;
|
|
hiddenCheckBox.Checked = config.Hidden;
|
|
preventDuplicatesCheckBox.Checked = config.PreventDuplicates;
|
|
loggingCheckBox.Checked = config.EnableLogging;
|
|
RefreshStatus();
|
|
}
|
|
|
|
private bool SaveConfiguration(bool showConfirmation)
|
|
{
|
|
try
|
|
{
|
|
HookConfig config = ReadForm();
|
|
ValidateConfig(config, requireScript: false);
|
|
RegistryConfig.Save(config);
|
|
if (showConfirmation)
|
|
{
|
|
MessageBox.Show(this, "Konfiguration wurde gespeichert.", "MSTSC Script Hook", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
}
|
|
RefreshStatus();
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show(this, ex.Message, "Konfiguration konnte nicht gespeichert werden", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void InstallPlugin()
|
|
{
|
|
try
|
|
{
|
|
HookConfig config = ReadForm();
|
|
ValidateConfig(config, requireScript: true);
|
|
RegistryConfig.Save(config);
|
|
PluginInstaller.Install();
|
|
RefreshStatus();
|
|
MessageBox.Show(
|
|
this,
|
|
"Plugin wurde für den aktuellen Benutzer installiert und die Konfiguration gespeichert.\n\n" +
|
|
"Bereits geöffnete mstsc.exe-Fenster vollständig schließen und anschließend neu starten, damit die DLL geladen wird.",
|
|
"Installation abgeschlossen",
|
|
MessageBoxButtons.OK,
|
|
MessageBoxIcon.Information);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show(this, ex.Message, "Installation fehlgeschlagen", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
private void RemovePlugin()
|
|
{
|
|
UninstallResult result = PluginInstaller.Uninstall();
|
|
RefreshStatus();
|
|
MessageBox.Show(
|
|
this,
|
|
result.Message,
|
|
"Plugin entfernen",
|
|
MessageBoxButtons.OK,
|
|
result.FullyRemoved ? MessageBoxIcon.Information : MessageBoxIcon.Warning);
|
|
}
|
|
|
|
private void StartTest()
|
|
{
|
|
try
|
|
{
|
|
StopTest(silent: true);
|
|
HookConfig config = ReadForm();
|
|
ValidateConfig(config, requireScript: true);
|
|
testProcess = ScriptTestLauncher.Start(config);
|
|
stopTestButton.Enabled = true;
|
|
statusLabel.Text = $"Testprozess gestartet: PID {testProcess.Id}. Der Test läuft unabhängig von MSTSC.";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show(this, ex.Message, "Script-Test fehlgeschlagen", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
|
|
private void StopTest() => StopTest(silent: false);
|
|
|
|
private void StopTest(bool silent)
|
|
{
|
|
if (testProcess is null)
|
|
{
|
|
stopTestButton.Enabled = false;
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
if (!testProcess.HasExited)
|
|
{
|
|
testProcess.Kill(entireProcessTree: true);
|
|
testProcess.WaitForExit(3000);
|
|
}
|
|
if (!silent)
|
|
{
|
|
statusLabel.Text = "Testprozess wurde beendet.";
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
if (!silent)
|
|
{
|
|
MessageBox.Show(this, ex.Message, "Testprozess konnte nicht beendet werden", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
DisposeTestProcessHandle();
|
|
}
|
|
}
|
|
|
|
private void DisposeTestProcessHandle()
|
|
{
|
|
testProcess?.Dispose();
|
|
testProcess = null;
|
|
stopTestButton.Enabled = false;
|
|
}
|
|
|
|
private void OpenLog()
|
|
{
|
|
string logPath = Path.Combine(PluginInstaller.InstallDirectory, "plugin.log");
|
|
if (!File.Exists(logPath))
|
|
{
|
|
MessageBox.Show(this, "Es existiert noch kein Plugin-Log.", "MSTSC Script Hook", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
return;
|
|
}
|
|
|
|
Process.Start(new ProcessStartInfo
|
|
{
|
|
FileName = logPath,
|
|
UseShellExecute = true
|
|
});
|
|
}
|
|
|
|
private void RefreshStatus()
|
|
{
|
|
bool installed = PluginInstaller.IsInstalled(out string pluginStatus);
|
|
string configStatus = string.IsNullOrWhiteSpace(scriptPathTextBox.Text)
|
|
? " Kein Script konfiguriert."
|
|
: $" Script: {scriptPathTextBox.Text}";
|
|
statusLabel.Text = (installed ? "✓ " : "○ ") + pluginStatus + configStatus;
|
|
}
|
|
|
|
private static void ValidateConfig(HookConfig config, bool requireScript)
|
|
{
|
|
if (!requireScript && string.IsNullOrWhiteSpace(config.ScriptPath))
|
|
{
|
|
return;
|
|
}
|
|
|
|
string scriptPath = Environment.ExpandEnvironmentVariables(config.ScriptPath);
|
|
if (string.IsNullOrWhiteSpace(scriptPath))
|
|
{
|
|
throw new InvalidOperationException("Bitte ein Script auswählen.");
|
|
}
|
|
|
|
if (!File.Exists(scriptPath))
|
|
{
|
|
throw new FileNotFoundException("Das ausgewählte Script wurde nicht gefunden.", scriptPath);
|
|
}
|
|
|
|
string extension = Path.GetExtension(scriptPath).ToLowerInvariant();
|
|
if (extension is not (".bat" or ".cmd" or ".ps1" or ".exe"))
|
|
{
|
|
throw new NotSupportedException("Unterstützt werden .bat, .cmd, .ps1 und .exe.");
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(config.WorkingDirectory))
|
|
{
|
|
string workDir = Environment.ExpandEnvironmentVariables(config.WorkingDirectory);
|
|
if (!Directory.Exists(workDir))
|
|
{
|
|
throw new DirectoryNotFoundException("Das Arbeitsverzeichnis wurde nicht gefunden: " + workDir);
|
|
}
|
|
}
|
|
}
|
|
}
|