How to Create a Trial Version for a C# Windows Forms application

Written by Renato Ivanescu · April 19th, 2024 · 6min read

Offering trial versions of applications is a common strategy to attract users by allowing them to evaluate the software before purchasing. However, designing a secure and user-friendly trial functionality can be challenging.

In this article, I will guide you through setting up a 30-day trial period for a C# Windows Forms application. This approach ensures a secure experience by storing encrypted trial information and linking the trial to an encrypted hardware identifier.

Generate a Hardware ID

The first step is to generate a unique identifier based on the user's hardware configuration.

This process involves gathering the IDs from the processor and the main hard drive, which are then merged into a single string.

The string is then encrypted to securely store the unique hardware ID, making it difficult to replicate or tamper with.

public static class HardwareId
{
    public static string GetHardwareId()
    {
        return GetProcessorId() + "-" + GetDiskId();
    }
    private static string GetProcessorId()
    {
        string processorId = string.Empty;
        var searcher = new ManagementObjectSearcher("SELECT ProcessorId FROM Win32_Processor");
        foreach (var item in searcher.Get())
        {
            processorId = item["ProcessorId"].ToString();
            break; 
        }
        return processorId;
    }
    private static string GetDiskId()
    {
        string diskId = string.Empty;
        var searcher = new ManagementObjectSearcher("SELECT SerialNumber FROM Win32_DiskDrive WHERE MediaType='Fixed hard disk media'");
        foreach (var item in searcher.Get())
        {
            diskId = item["SerialNumber"].ToString().Trim(); 
            break; 
        }
        return diskId;
    }
}

Encrypt the Trial Information

After creating the unique ID, the next step is to encrypt it using the Advanced Encryption Standard method for its balance of security and performance.

Once encrypted, the data is then stored in the Windows Registry.

public static class EncryptionService
{
    private static readonly string encryptionKey = "EncryptionKey123"; 
    public static string EncryptString(string plainText)
    {
        byte[] initVector = new byte[16];
        byte[] array;
        using (Aes aes = Aes.Create())
        {
            aes.Key = Encoding.UTF8.GetBytes(encryptionKey);
            aes.IV = initVector;
            ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
            using (MemoryStream memoryStream = new MemoryStream())
            {
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter streamWriter = new StreamWriter(cryptoStream))
                    {
                        streamWriter.Write(plainText);
                    }
                    array = memoryStream.ToArray();
                }
            }
        }
        return Convert.ToBase64String(array);
    }
    public static string DecryptString(string cipherText)
    {
        byte[] initVector = new byte[16];
        byte[] buffer = Convert.FromBase64String(cipherText);
        using (Aes aes = Aes.Create())
        {
            aes.Key = Encoding.UTF8.GetBytes(encryptionKey);
            aes.IV = initVector;
            ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
            using (MemoryStream memoryStream = new MemoryStream(buffer))
            {
                using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader streamReader = new StreamReader(cryptoStream))
                    {
                        return streamReader.ReadToEnd();
                    }
                }
            }
        }
    }
}

Manage the Trial Logic

In our scenario, the trial management is based on two processes:

  • setting up the trial
  • and monitoring its status.

During the first launch, the application stores the starting date of the trial and the hardware ID, both in encrypted form.

In later sessions, it verifies the present hardware ID with the one saved earlier and determines how many days are left in the trial by comparing today’s date to the encrypted starting date. If the hardware IDs match and the trial hasn’t reached its end, the application continues to function as intended. Otherwise, you should provide options for the user to purchase or extend the trial.

public static class TrialManager
{
    private const string RegistryPath = @"SOFTWARE\YourCompany\YourProduct";
    private const string StartDateValueName = "StartDate";
    private const string HardwareIdValueName = "HardwareId";
    public static void InitializeTrial()
    {
        using (var key = Registry.CurrentUser.CreateSubKey(RegistryPath))
        {
            if (key.GetValue(StartDateValueName) == null || key.GetValue(HardwareIdValueName) == null)
            {
                string startDate = EncryptString(DateTime.UtcNow.ToString("o")); // Data de start a perioadei de încercare
                string hardwareId = EncryptString(HardwareId.GetHardwareId()); // ID-ul hardware criptat
                key.SetValue(StartDateValueName, startDate);
                key.SetValue(HardwareIdValueName, hardwareId);
            }
        }
    }
    public static bool IsTrialValid()
    {
        using (var key = Registry.CurrentUser.OpenSubKey(RegistryPath))
        {
            if (key != null)
            {
                string encryptedStartDate = key.GetValue(StartDateValueName)?.ToString();
                string encryptedHardwareId = key.GetValue(HardwareIdValueName)?.ToString();
                if (!string.IsNullOrEmpty(encryptedStartDate) && !string.IsNullOrEmpty(encryptedHardwareId))
                {
                    string decryptedStartDate = DecryptString(encryptedStartDate);
                    string decryptedHardwareId = DecryptString(encryptedHardwareId);
                    if (decryptedHardwareId == HardwareId.GetHardwareId())
                    {
                        DateTime startDate = DateTime.Parse(decryptedStartDate);
                        return DateTime.UtcNow <= startDate.AddDays(30); 
                    }
                }
            }
        }
        return false;
    }
    public static int DaysRemainingInTrial()
    {
        using (var key = Registry.CurrentUser.OpenSubKey(RegistryPath))
        {
            if (key != null)
            {
                string encryptedStartDate = key.GetValue(StartDateValueName)?.ToString();
                if (!string.IsNullOrEmpty(encryptedStartDate))
                {
                    string decryptedStartDate = EncryptionService.DecryptString(encryptedStartDate);
                    DateTime startDate = DateTime.Parse(decryptedStartDate);
                    TimeSpan timeSpan = startDate.AddDays(30) - DateTime.UtcNow;
                    return (int)timeSpan.TotalDays;
                }
            }
        }
        return -1; 
    }
    private static string EncryptString(string input) => EncryptionService.EncryptString(input);
    private static string DecryptString(string input) => EncryptionService.DecryptString(input);
}

Integrate the Trial Manager

The last step is to set up the Trial Manager in our application.

To keep the user informed about the trial status, display a message at startup, indicating whether the application is in trial mode and how many days are left.

If the trial has expired, notify the user and provide options to purchase the full version or extend the trial, enhancing the user experience and encouraging conversion to the full version.

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    // Initialize the trial period when the application runs for the first time
    TrialManager.InitializeTrial();
    int daysRemaining = TrialManager.DaysRemainingInTrial();
    if (daysRemaining >= 0)
    {
        MessageBox.Show($"You are in trial mode. You have {daysRemaining} days left in your trial period.", "Trial Mode", MessageBoxButtons.OK, MessageBoxIcon.Information);
        Application.Run(new Form1());
    }
    else
    {
        MessageBox.Show("Your trial period has expired. Please purchase the full version to continue using the application.", "Trial Expired", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        // Optional: Provide a way for the user to purchase or extend the trial
        // Application.Run(new PurchaseForm());
    }
}

Build and Install

Now, it’s time to create the installer package for your application and install it to test the trial functionality.

You can use the Advanced Installer extension for Visual Studio to create the package. For more details on how you can easily create the MSI installer for your C# application, check our dedicated article guide on this topic, or the video below:

Advanced Installer trial and licensing features

For a smooth integration of trial and licensing functionalities, you can try Advanced Installer.

Advanced Installer allows you to easily set up a trial period, manage license keys, and ensure users have a clear path for evaluating your software.

For detailed guidance on configuring Advanced Licensing features, explore our how-to articles: https://www.advancedinstaller.com/user-guide/qat-trial.html.

Conclusion

Implementing a secure and user-friendly trial period in a C# Windows Forms application can become a complex process.

The Advanced Installer tool offers a simpler and more secure alternative. Its dedicated feature for trial and licensing management minimizes the complexity and potential for error, ensuring a streamlined process for both developers and end-users.

Written by
See author's page
Renato Ivanescu

Renato is a technical writer for Advanced Installer and an assistant professor at the University of Craiova. He is currently a PhD. student, and computers and information technology are his areas of interest. He loves innovation and takes on big challenges.

Comments: