跳到正文
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 用途不同,不要在每次退出时解除设备绑定。