PolyTrader.App.csproj verwies per ProjectReference nach ..\..\LicenseLabrador\client-dotnet\... und baute damit nur auf einer Maschine, auf der das Schwester-Repo danebenliegt. Auf dem Zielland-System waere der Build fehlgeschlagen. Stattdessen liegt das SDK als versioniertes Paket in lib/nuget und wird ueber die NuGet.Config-Quelle "local" aufgeloest. Ein DLL-Verweis haette nicht gereicht: das Paket traegt die transitiven Abhaengigkeiten (BouncyCastle, ProtectedData, System.Text.Json) in seinen Metadaten. packageSourceMapping bindet LicenseLabrador.* fest an den lokalen Feed, damit ein gleichnamiges Paket auf nuget.org unseres nicht verdraengt (Dependency Confusion). Enthaelt ausserdem die bereits vorgemerkte Umbenennung LicenseDialog.Validate -> ValidateKey: der alte Name verdeckte ContainerControl.Validate() (CS0108), ein Aufruf ueber eine Form-Referenz haette einen Netzwerk-Call ausgeloest statt zu validieren. Build und Tests gruen (438 Tests). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
136 lines
5.5 KiB
C#
136 lines
5.5 KiB
C#
using System;
|
||
using System.Drawing;
|
||
using System.Windows.Forms;
|
||
using LicenseLabrador.Client;
|
||
using PolyTraderSharp.Licensing;
|
||
|
||
namespace PolyTraderSharp.Ui
|
||
{
|
||
/// <summary>
|
||
/// Modaler Lizenzdialog beim Programmstart, wenn keine gültige Lizenz vorliegt. Zeigt die
|
||
/// Hardware-ID (kopierbar, für die Aktivierung im Lizenz-Admin), nimmt einen Lizenzschlüssel
|
||
/// entgegen und validiert ihn gegen den Lizenzserver. Bei Erfolg werden <see cref="ValidatedKey"/>
|
||
/// und <see cref="ValidatedResult"/> gesetzt; der Aufrufer (LicenseGate) speichert den Schlüssel.
|
||
/// Ohne gültige Lizenz kann der Nutzer eingeschränkt (nur Core-Shell) weiterstarten.
|
||
/// </summary>
|
||
public partial class LicenseDialog : Form
|
||
{
|
||
private readonly LicenseClient _client;
|
||
|
||
/// <summary>Nur gesetzt, wenn im Dialog erfolgreich validiert wurde.</summary>
|
||
public LicenseResult? ValidatedResult { get; private set; }
|
||
|
||
/// <summary>Der erfolgreich validierte Schlüssel (für die verschlüsselte Ablage).</summary>
|
||
public string? ValidatedKey { get; private set; }
|
||
|
||
/// <param name="startupContext">
|
||
/// true = Aufruf beim Programmstart (keine gültige Lizenz): der Abbruch-Button bietet
|
||
/// „eingeschränkt starten". false = Aufruf aus den Einstellungen (App läuft bereits): der
|
||
/// Button heißt „Schließen".
|
||
/// </param>
|
||
public LicenseDialog(LicenseClient client, string prefillKey, LicenseResult? initialResult, bool startupContext = true)
|
||
{
|
||
_client = client ?? throw new ArgumentNullException(nameof(client));
|
||
InitializeComponent();
|
||
|
||
txtHardwareId.Text = SafeHardwareId(client);
|
||
txtKey.Text = prefillKey ?? string.Empty;
|
||
|
||
if (initialResult != null)
|
||
{
|
||
lblStatus.Text = "Status: " + LicenseGate.Describe(initialResult) +
|
||
(string.IsNullOrEmpty(initialResult.Message) ? string.Empty : $" – {initialResult.Message}");
|
||
}
|
||
else
|
||
{
|
||
lblStatus.Text = "Status: noch nicht geprüft – bitte „Validieren“ klicken.";
|
||
}
|
||
|
||
if (startupContext)
|
||
{
|
||
lblHeadline.Text = "Für PolyTrader wird eine gültige Lizenz benötigt.";
|
||
btnLimited.Text = "Ohne Lizenz starten (eingeschränkt)";
|
||
}
|
||
else
|
||
{
|
||
lblHeadline.Text = "Lizenz prüfen / hinterlegen";
|
||
btnLimited.Text = "Schließen";
|
||
}
|
||
|
||
btnValidate.Click += (_, _) => ValidateKey();
|
||
btnCopyHwId.Click += (_, _) => CopyHardwareId();
|
||
btnLimited.Click += (_, _) => { DialogResult = DialogResult.Cancel; Close(); };
|
||
AcceptButton = btnValidate;
|
||
}
|
||
|
||
private static string SafeHardwareId(LicenseClient client)
|
||
{
|
||
try { return client.GetHardwareId(); }
|
||
catch { return "(nicht ermittelbar)"; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// Prüft den eingegebenen Schlüssel gegen den Lizenzserver. Bewusst NICHT „Validate" genannt:
|
||
/// das würde <see cref="ContainerControl.Validate()"/> verdecken (CS0108), sodass ein Aufruf
|
||
/// über eine <see cref="Form"/>-Referenz statt der Control-Validierung einen Netzwerk-Call
|
||
/// auslösen würde.
|
||
/// </summary>
|
||
private void ValidateKey()
|
||
{
|
||
string key = txtKey.Text.Trim();
|
||
if (string.IsNullOrWhiteSpace(key))
|
||
{
|
||
lblResult.ForeColor = Color.Firebrick;
|
||
lblResult.Text = "Bitte einen Lizenzschlüssel eingeben.";
|
||
return;
|
||
}
|
||
|
||
btnValidate.Enabled = false;
|
||
Cursor = Cursors.WaitCursor;
|
||
lblResult.ForeColor = SystemColors.ControlText;
|
||
lblResult.Text = "Prüfe Lizenz …";
|
||
lblResult.Refresh();
|
||
|
||
try
|
||
{
|
||
// Blockierend: das SDK nutzt durchgängig ConfigureAwait(false) → kein Deadlock auf dem
|
||
// UI-Thread; der kurze Freeze (max. HTTP-Timeout) ist im modalen Startdialog vertretbar
|
||
// und vermeidet jede Abhängigkeit vom WinForms-SynchronizationContext vor Application.Run.
|
||
LicenseResult result = _client.ValidateAsync(key).GetAwaiter().GetResult();
|
||
|
||
if (result.IsUsable)
|
||
{
|
||
ValidatedResult = result;
|
||
ValidatedKey = key;
|
||
lblResult.ForeColor = Color.Green;
|
||
lblResult.Text = "Lizenz gültig – PolyTrader startet.";
|
||
DialogResult = DialogResult.OK;
|
||
Close();
|
||
}
|
||
else
|
||
{
|
||
lblResult.ForeColor = Color.Firebrick;
|
||
string extra = string.IsNullOrEmpty(result.Message) ? string.Empty : $" ({result.Message})";
|
||
lblResult.Text = "Nicht gültig: " + LicenseGate.Describe(result) + extra;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
lblResult.ForeColor = Color.Firebrick;
|
||
lblResult.Text = "Fehler bei der Prüfung: " + ex.Message;
|
||
}
|
||
finally
|
||
{
|
||
Cursor = Cursors.Default;
|
||
btnValidate.Enabled = true;
|
||
}
|
||
}
|
||
|
||
private void CopyHardwareId()
|
||
{
|
||
try { Clipboard.SetText(txtHardwareId.Text); }
|
||
catch { /* Zwischenablage kann kurzzeitig belegt sein – kein harter Fehler */ }
|
||
}
|
||
}
|
||
}
|