Quickstart: .NET (C# / VB.NET)
This quickstart takes about ten minutes. You activate a license from .NET, persist it
to disk, confirm it validates with no network connection, and release the seat again.
C# and VB.NET consume the same assembly - SWS.dll, imported as SWS.Licensing - and every
example on this page is shown in both languages.
Before you start
From Getting started you should have your vendor id (console → Settings), a license key (issued by an order, Orders → Create, and emailed to the customer), and the NuGet package:
dotnet add package SoftActivate.Sdk
The whole program
This is a complete, runnable integration: the same loop your application runs on every start.
C#
using System;
using System.IO;
using System.Threading.Tasks;
using SWS.Licensing;
internal static class Program
{
private static async Task<int> Main()
{
using var client = new LicensingClient("https://lm.sws.softactivate.com")
{
// your tenant coordinates - vendor id from the console (Settings),
// license key from your order's delivery email
VendorId = "YOUR-VENDOR-ID",
LicenseKey = "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX",
TrustedDomain = "sws.softactivate.com"
};
// your product's Reference Id (console -> Products): a key for a
// different product is refused - see the product binding guide
client.SetProductId("YOUR_PRODUCT_ID");
// after the first (online) activation, every later start validates the
// stored license file with no network access at all
License? existing = File.Exists("license.dat")
? License.Parse(File.ReadAllText("license.dat"))
: null;
// with no stored license this activates the device (online, once); with one
// it verifies the chain, the device fingerprint and the dates locally -
// and renews online only when needed
LicenseValidationResult result = await client.ValidateLicenseAsync(existing);
if (!result.IsValid)
{
// the precise status says WHY - expired, revoked, bound to another
// machine, subscription lapsed... each deserves different UX; see the
// statuses reference: /licensing/docs/status-codes/
Console.Error.WriteLine($"License is not valid: {result.Status}");
// when an activation failed, Error says why (HTTP status, error code,
// message) - log it, it is what support needs
if (result.Error != null)
Console.Error.WriteLine($"Activation error: {result.Error}");
return 1;
}
// persist the new or renewed certificate whenever one is issued
if (result.UpdatedLicense != null)
File.WriteAllText("license.dat", result.UpdatedLicense.Serialize());
License current = result.UpdatedLicense ?? existing!;
Console.WriteLine(current.ExpirationDate == License.NoExpiration
? "License is valid, with no expiration."
: $"License is valid until {current.ExpirationDate:u}.");
return 0;
}
}
VB.NET
Imports System
Imports System.IO
Imports System.Threading.Tasks
Imports SWS.Licensing
Module Program
Function Main() As Integer
Return MainAsync().GetAwaiter().GetResult()
End Function
Private Async Function MainAsync() As Task(Of Integer)
' your tenant coordinates - vendor id from the console (Settings),
' license key from your order's delivery email
Using client As New LicensingClient("https://lm.sws.softactivate.com") With {
.VendorId = "YOUR-VENDOR-ID",
.LicenseKey = "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX",
.TrustedDomain = "sws.softactivate.com"
}
' your product's Reference Id (console -> Products): a key for a
' different product is refused - see the product binding guide
client.SetProductId("YOUR_PRODUCT_ID")
' after the first (online) activation, every later start validates the
' stored license file with no network access at all
Dim existing As License = Nothing
If File.Exists("license.dat") Then
existing = License.Parse(File.ReadAllText("license.dat"))
End If
' with no stored license this activates the device (online, once); with
' one it verifies the chain, the device fingerprint and the dates
' locally - and renews online only when needed
Dim result As LicenseValidationResult = Await client.ValidateLicenseAsync(existing)
If Not result.IsValid Then
' the precise status says WHY - expired, revoked, bound to another
' machine, subscription lapsed... each deserves different UX; see
' the statuses reference: /licensing/docs/status-codes/
Console.Error.WriteLine($"License is not valid: {result.Status}")
' when an activation failed, Error says why (HTTP status, error
' code, message) - log it, it is what support needs
If result.Error IsNot Nothing Then
Console.Error.WriteLine($"Activation error: {result.Error}")
End If
Return 1
End If
' persist the new or renewed certificate whenever one is issued
If result.UpdatedLicense IsNot Nothing Then
File.WriteAllText("license.dat", result.UpdatedLicense.Serialize())
End If
Dim current As License = If(result.UpdatedLicense, existing)
If current.ExpirationDate = License.NoExpiration Then
Console.WriteLine("License is valid, with no expiration.")
Else
Console.WriteLine($"License is valid until {current.ExpirationDate:u}.")
End If
Return 0
End Using
End Function
End Module
Replace the vendor id and license key with yours, put deployment-root.crt next to the
binary (in your real product, embed it as a resource or install it with your
application), and run it.
Run it three times
- First run (online): no
license.datexists, soValidateLicenseAsyncactivates the device with the service. The SDK receives the license and the program saves it tolicense.dat. - Second run (disconnect the network if you like): the stored license is verified entirely on the machine, and no network access happens. This is the everyday path your customers' machines take on every start.
- Moving on: when you are done with this machine, release its seat so the key can activate elsewhere:
await client.DeactivateLicenseAsync();
File.Delete("license.dat"); // the local file is now meaningless
Await client.DeactivateLicenseAsync()
File.Delete("license.dat") ' the local file is now meaningless
When validation says no
A false result.IsValid can mean several different things. result.Status tells you
whether the license is expired, revoked, bound to a different device (BadContext), or
waiting on a lapsed subscription (PaymentRequired, with the subscription state in
result.StatusMessage), and each case deserves different UX. When the online activation
itself failed, result.Error says why: the HTTP status, the service's error code (for
example license_not_found or vendor_mismatch) and its message. Log it. The
statuses and errors reference maps every status to a
recommended action.
Publishing your app
Every way of publishing a .NET 8 app works with the SDK as of version 1.1.0, and with SDK 1.2.0 and later a Native AOT app recognizes the machine as reliably as a regular build:
- Framework-dependent, self-contained, and single file need nothing special.
- Trimmed (
PublishTrimmed): the NuGet package configures the trimmer for you, so device identification works fully. If you referenceSWS.dlldirectly instead of the package, add the same settings to your project file yourself:
<PropertyGroup>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
</PropertyGroup>
<ItemGroup>
<TrimmerRootAssembly Include="System.Management" />
</ItemGroup>
- Native AOT (
PublishAot): supported, with a slightly weaker device binding on Windows, which the build notes with warningSWS0001. Since SDK 1.2.0, the same machine has the same identity in a Native AOT build and in a regular build. SDK 1.1.0 cannot identify some Windows machines reliably under Native AOT, and their activation is refused (device_unidentifiable, reported asNotFound; see hardware IDs). Upgrade to SDK 1.2.0 or later, or publish trial builds without Native AOT.
SDK 1.0.0 cannot activate from a trimmed or Native AOT app: it fails before the
request is sent and reports NotFound. Upgrade to 1.1.0. If you can't upgrade yet,
publish without trimming, or keep trimming and add all four settings, then rebuild from
clean:
<PropertyGroup>
<JsonSerializerIsReflectionEnabledByDefault>true</JsonSerializerIsReflectionEnabledByDefault>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
</PropertyGroup>
<ItemGroup>
<TrimmerRootAssembly Include="SWS" />
<TrimmerRootAssembly Include="System.Management" />
</ItemGroup>
Next: the C++ quickstart, or the statuses and errors reference.