跳到正文
LICENTIVO / INTEGRATION

C# 軟體授權:.NET 與 WinForms 整合教學

為 .NET 8 或 WinForms 應用程式加入授權碼啟用。透過 NuGet 安裝 SDK、控管功能權限,並驗證重新啟動與拒絕存取的處理。

.NET 8+ · 主控台與 WinForms更新日期

1. 準備產品設定與測試授權

為自己的 C#/.NET 或 WinForms 應用程式加入客戶授權碼與功能權限。本教學說明商業軟體的使用授權;為原始碼選擇開源授權條款則是另一項決定。先使用裝置綁定授權與不計量的匯出功能,確認基本流程可正常運作。

裝置額度按目前啟用裝置計算,釋放後返還;一般授權離線仍占用,浮動授權租約到期後自動返還。

  1. 在控制台「產品」複製產品 ID,點擊同一產品的「簽章公鑰」,將每一組金鑰 ID、公鑰填入初始化程式碼,取代 YOUR_PRODUCT_ID、YOUR_KEY_ID 和 YOUR_PUBLIC_KEY。快速接入也可直接複製已填好的程式碼。無需設定檔;SDK 預設連線至 https://www.licentivo.com。
  2. 安裝 .NET 8 或更新版本的 SDK。在 Licentivo 快速整合中建立或選擇產品,於策略與授權中允許 export 功能。

2. 安裝固定版本的 .NET 套件

在自己的應用程式專案中執行下方安裝命令或加入建置依賴,安裝 SDK 0.11.6。套件管理器負責取得 SDK,無需下載解壓原始碼套件或設定本機原始碼路徑。

Terminal
dotnet add package Licentivo.SDK --version 0.11.6
Terminal · new console project
dotnet new console --name LicensedApp --framework net8.0
cd LicensedApp
dotnet add package Licentivo.SDK --version 0.11.6

3. 啟用並保護一個實際操作

將 SDK 初始化接到自己軟體的啟動或啟用入口,從啟用視窗讀取客戶授權碼,傳入產品 ID、信任公鑰並保持用戶端存活。將 RunFeature 放在實際業務入口,以自己的功能取代匯出範例函式。

C# · 完整 Program.cs
using Licentivo;

// BEGIN LICENTIVO PRODUCT
// Copy Product ID and all key ID/public key pairs from Console > Products > Signing public keys.
const string ProductId = "YOUR_PRODUCT_ID";
var TrustedKeys = new Dictionary<string,string> { ["YOUR_KEY_ID"] = "YOUR_PUBLIC_KEY" };
// END LICENTIVO PRODUCT

static string Setting(string name, string fallback) => Environment.GetEnvironmentVariable(name) ?? fallback;

// Call at the business entry point, including shortcuts and alternate menu actions.
static async Task ExportReport(string path)
{
    // Replace this write with your existing, non-metered export operation.
    await File.WriteAllTextAsync(path, "A report generated by the licensed application.\n");
}

try
{
    // In a GUI, read the activation input. Later starts pass an empty key.
    await using var client = await LicentivoClient.OpenProduct(
        ProductId, TrustedKeys, Setting("LICENTIVO_LICENSE_KEY", ""),
        error => Console.Error.WriteLine($"Background verification: {error.Message}"));
    var feature = Setting("LICENTIVO_FEATURE", "export");
    var path = Setting("LICENTIVO_OUTPUT", "licensed-report.txt");
    if (Setting("LICENTIVO_METERED", "") == "1")
        await client.RunMeteredFeature(feature, 1, Setting("LICENTIVO_OPERATION_ID", ""), () => ExportReport(path));
    else
        await client.RunFeature(feature, () => ExportReport(path));
    Console.WriteLine($"License OK: {feature} is available\nExport completed: {path}");
    // Keep client alive in your real app; disposal belongs in app shutdown.
}
catch (Exception error)
{
    Console.Error.WriteLine(error.Message);
    Environment.ExitCode = 2;
}
Windows · PowerShell
$env:LICENTIVO_LICENSE_KEY = 'YOUR_TEST_LICENSE_KEY'
dotnet run
# On later starts, restore the saved device credential:
Remove-Item Env:LICENTIVO_LICENSE_KEY
dotnet run
Linux / macOS · Bash
export LICENTIVO_LICENSE_KEY='YOUR_TEST_LICENSE_KEY'
dotnet run
# On later starts, restore the saved device credential:
unset LICENTIVO_LICENSE_KEY
dotnet run

4. 整合 WinForms 啟用視窗與匯出按鈕

每個匯出入口都 await client.RunFeature("export", ExportReport),包含選單與快捷鍵。操作期間停用對應按鈕,捕捉錯誤並顯示授權拒絕原因,再允許下一次操作。

視窗開啟期間保持用戶端存活,由 SDK 心跳更新授權。背景錯誤提示透過 Invoke 或 BeginInvoke 回到介面執行緒;結束時 await DisposeAsync。正常釋放保留裝置綁定紀錄,並嘗試歸還浮動席位。

獨立桌面範例可透過下方文件連結查看。WinForms 需要 Windows;上方的主控台範例也可在 Linux 與 macOS 執行。

Windows · new WinForms project
dotnet new winforms --name LicensedWinForms --framework net8.0
cd LicensedWinForms
dotnet add package Licentivo.SDK --version 0.11.6
C# · replace Program.cs
using Licentivo;
namespace LicensedWinForms;
sealed class DemoForm : Form {
    // BEGIN LICENTIVO PRODUCT
    // Console > Products > Signing public keys: paste Product ID and every key ID/public key pair.
    const string ProductId = "YOUR_PRODUCT_ID";
    static readonly Dictionary<string,string> TrustedKeys = new() { ["YOUR_KEY_ID"] = "YOUR_PUBLIC_KEY" };
    // END LICENTIVO PRODUCT
    readonly TextBox key = new() { Width = 380, UseSystemPasswordChar = true };
    readonly Label status = new() { AutoSize = true, MaximumSize = new Size(550, 0), Text = "Enter a key, or leave empty to restore this device." };
    readonly CancellationTokenSource lifetime = new();
    LicentivoClient? client;
    bool busy;
    [STAThread] static void Main() { ApplicationConfiguration.Initialize(); Application.Run(new DemoForm()); }
    DemoForm() {
        Text = "Licentivo · Report export"; ClientSize = new Size(600, 220);
        var panel = new FlowLayoutPanel { Dock = DockStyle.Fill, Padding = new Padding(20), FlowDirection = FlowDirection.TopDown, WrapContents = false, AutoScroll = true };
        Controls.Add(panel); panel.Controls.Add(new Label { Text = "Customer license key", AutoSize = true }); panel.Controls.Add(key);
        Add(panel, "Activate / restore", async () => {
            if (client is not null) { await client.DisposeAsync(); client = null; }
            client = await LicentivoClient.OpenProduct(ProductId, TrustedKeys, key.Text, ct: lifetime.Token);
            key.Clear(); status.Text = client.Status().State;
        });
        var buttons = new FlowLayoutPanel { AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink, WrapContents = false }; panel.Controls.Add(buttons);
        Add(buttons, "Export CSV", () => Export("export", "report.csv"));
        Add(buttons, "Pro export", () => Export("pro", "pro-report.csv"));
        Add(buttons, "Refresh license", async () => { await Require().RefreshOnline(lifetime.Token); status.Text = Require().Status().State; });
        panel.Controls.Add(status);
        FormClosed += async (_, _) => { lifetime.Cancel(); if (client is not null) await client.DisposeAsync(); };
    }
    LicentivoClient Require() => client ?? throw new InvalidOperationException("Activate the application first.");
    async Task Export(string feature, string filename) {
        using var save = new SaveFileDialog { FileName = filename, Filter = "CSV report|*.csv" };
        await Require().RunFeature(feature, async () => {
            if (save.ShowDialog(this) != DialogResult.OK) return;
            await File.WriteAllTextAsync(save.FileName, "Item,Quantity,Price\nNotebook,2,12.50\nPen,5,3.00\n");
            status.Text = "Saved: " + save.FileName;
        });
    }
    void Add(Control panel, string title, Func<Task> action) {
        var button = new Button { Text = title, AutoSize = true }; panel.Controls.Add(button);
        button.Click += async (_, _) => {
            if (busy) return; busy = true; status.Text = "Working…";
            try { await action(); } catch (Exception error) { if (!IsDisposed) status.Text = error.Message; }
            finally { busy = false; }
        };
    }
}
Windows · PowerShell
dotnet run

5. 驗證啟用、重新啟動及權限拒絕

  1. 以測試金鑰執行,確認出現 License OK 與 Export completed,並查看匯出檔內容。
  2. 移除金鑰環境變數,以相同作業系統使用者重新啟動。應恢復既有憑證,不新增註冊裝置。
  3. 將 LICENTIVO_FEATURE 設為授權中不存在的功能,LICENTIVO_OUTPUT 設為尚不存在的檔名。確認結束碼非零,且沒有產生新檔案。
  4. 只有策略允許且簽章快取仍有效時才測試離線啟動。浮動授權與必須連線的啟動需要伺服器核准。

排查授權驗證問題

檢查產品 ID、每組金鑰 ID 和公鑰、客戶輸入的授權碼。公鑰屬於產品;客戶授權與本機憑證分別管理。開發測試授權碼及廠商管理 API Key 不隨客戶安裝包發送。

授權到期、裝置上限與功能缺失屬於授權拒絕。顯示錯誤及請求編號,在主控台解決授權或策略問題,不要略過 RunFeature。

每月計次使用 RunMeteredFeature 時保留穩定工作編號與持久業務結果。正常結束與客戶主動 Deactivate 用途不同,不要在每次結束時解除裝置綁定。