You add a 14-day trial to your desktop tool and downloads go up. Sales don't. A month later you find a forum thread explaining that deleting one registry key restarts the trial, and a small script that does it on every boot. Some of your most active users have been on day one of their trial since spring.
This guide covers how to add a free trial to desktop software that holds up: the three kinds of trial and when each fits, why trials enforced on the customer's machine get reset, how server-side trial keys bound to a hardware ID work, and how to move a trial user to a paid license without friction. It ends with an illustrative C++ example you can adapt to whichever licensing system you use.
How to add a free trial to desktop software
The short answer: treat the trial as a license, not as a timer. When a new user starts the app, it asks your licensing server for a trial. The server checks whether this device has had a trial before, and if not, issues a time-limited license key bound to the device's hardware ID. Every launch then validates that key on the server, exactly like a paid key. When the trial ends, the server stops accepting it, and the user enters a paid key on the same machine.
Everything else in this guide is detail on top of that model: which limits to apply, how to identify a device, what to do when the network is down and how to turn the last days of a trial into a sale.
01Request trial + device fingerprint
First launch, no key on file
02Has this device had a trial?
Lookup by hashed hardware ID
03No: issue trial key, 14 days
Yes: refuse and offer to buy
04Every launch: key + fingerprint
05Valid until expiry, then refused
Expiry is server time, not PC time
Time-limited, feature-limited and usage-limited trials
There are three common ways to limit a trial, and many products combine two of them. The right choice depends on how quickly a new user reaches the moment where your software proves its value.
| Trial type | How it limits | Fits best when | Typical abuse |
|---|---|---|---|
| Time-limited | Full features for a fixed number of days | Value shows up within a few sessions | Resetting the timer |
| Feature-limited | Core features free, premium features locked | Users need weeks to adopt the tool | Patching the feature flag |
| Usage-limited | A set number of exports, runs or projects | Each use has clear, countable value | Resetting the counter |
A time-limited trial is the most familiar: 7, 14 or 30 days with everything enabled. It creates a natural deadline, which helps conversion, but it is the type most often reset. A feature-limited trial, sometimes called freemium, never expires but keeps the valuable features behind a paid key. It is gentler on users who take a long time to evaluate, and its weak point is the flag that decides whether a feature is enabled. A usage-limited trial counts actions, such as ten exports or three rendered projects, and suits tools where each action has obvious value.
Whichever you pick, the same rule applies: the limit has to be enforced somewhere the user can't edit. For a time limit that means the server's clock. For a feature limit it means the premium logic or data comes from the server only for paid keys. For a usage limit it means the counter lives on the server.
Why local trial timers get reset
Most trials that leak were implemented the same way: on first launch the application writes an install date somewhere on the PC, and on every later launch it compares that date with the system clock. Every part of that design is under the user's control.
- Registry and file markers. The install date sits in a registry key, an AppData file or a hidden file. Once someone finds it with a file or registry monitor, deleting it starts a new trial. Obfuscating the name only delays this.
- Reinstalling. An uninstaller that removes your markers also removes the trial history. One that leaves them behind gets you complaints about leftover files, and the markers can still be found and removed.
- Clock changes. If the trial compares against the system clock, setting the date back extends it. Detecting backward clock jumps helps a little, but the user controls the clock from before your app ever runs.
- Virtual machines and snapshots. Install the app in a VM, take a snapshot, and restore it when the trial ends. Every restore is a fresh first launch, and nothing on the machine remembers otherwise.
- Patching the check. Even with a perfect marker, the comparison runs in your binary. A cracker changes one conditional jump and the trial never ends.
Trial stored on the PC
- Install date in registry or file
- Compared with the system clock
- Reset by reinstall or VM snapshot
- Decision made in the binary
Trial issued by the server
- Trial key recorded per device
- Expiry checked against server time
- Reinstall finds the same device
- Decision made on the server
None of this means local markers are useless. They are a reasonable first hint, and they let the app show a sensible message before it reaches the network. They just can't be the thing that decides whether the trial is still valid. The same argument applies to licensing in general, and client-side protection vs server-side secrets covers it in more depth.
Server-side trial keys bound to a hardware ID
A server-side trial replaces the local timer with a real license key that happens to be short-lived. The key is created by your server, its expiry is stored on your server, and it is bound to the hardware fingerprint of the device that requested it. This is the same mechanism described in what HWID locking is and how it works, applied to a key that lasts days instead of years.
- 01
Fingerprint the device
On first launch the app computes a hash of stable hardware components, such as motherboard and processor identifiers. Send the hash, never the raw values.
- 02
Request a trial
The app sends the fingerprint to your licensing server over an encrypted, signed channel and asks for a trial.
- 03
Check trial history
The server looks up the fingerprint. If this device has never had a trial, it creates a trial key with a fixed duration and binds it to the fingerprint. If it has, it refuses.
- 04
Validate every launch
The app sends the trial key and fingerprint on each start. The server compares the expiry with its own clock, so changing the date on the PC has no effect.
- 05
Release what the app needs
Only a valid session receives configuration, strings or files the app depends on. A patched check then leaves a program with nothing to run.
Because the history is keyed by the device rather than by anything stored on it, reinstalling the app, wiping AppData or deleting registry entries all lead back to the same answer from the server. The last step is what stops the patched jump: if the trial build depends on data that only an authenticated session receives, removing the check removes the data too.
One trial per device: handling the edge cases
"One trial per device" sounds simple until real users arrive. A few cases come up in almost every product, and each needs a decision before launch rather than a support ticket afterwards.
- Virtual machines. A fresh VM produces a fresh fingerprint, which is exactly how VM snapshot abuse works. Detecting virtualized environments and refusing trials there, or requiring an email for them, closes most of the gap. Be aware that some legitimate users work in VMs, so decide whether to refuse or simply ask for more.
- Hardware changes. A user who replaces a system drive mid-trial may produce a new fingerprint. Building the fingerprint from several stable components, and tolerating a partial match, keeps them on the same trial.
- Shared and lab machines. A classroom PC may have had a trial from a previous student. Give your support team a way to grant a second trial manually.
- Second evaluations. Someone who tried version 2 may deserve a fresh look at version 4. A per-major-version trial policy, or a manual extension, handles this without opening a loophole.
- Accounts as well as devices. Asking for an email when a trial starts adds a second identity to check and gives you a way to reach the user. It also adds friction, so test whether it costs more signups than it saves.
A trial flow in C++, step by step
The example below shows where trial logic sits in a C++ application. It uses placeholder names for a generic licensing client so the structure is clear without tying it to one SDK. The pattern is the same in C#: request or load a key, validate it on the server, and fail closed when validation doesn't succeed.
// Illustrative only. Licensing::*, LoadStoredKey, SaveKey and the UI
// functions are placeholders for your licensing SDK and your own code,
// not qPapel Auth API names.
int main()
{
Licensing::Client lic; // encrypted, signed channel
std::string fingerprint = lic.HardwareId(); // hashed on the device
std::string key = LoadStoredKey(); // paid or trial key, if any
if (key.empty())
{
auto trial = lic.RequestTrial(fingerprint);
if (!trial.granted)
{
ShowBuyScreen("A trial was already used on this device.");
return 0;
}
key = trial.key; // short-lived, HWID-bound
SaveKey(key); // a hint, not the source of truth
}
auto session = lic.Validate(key, fingerprint); // expiry checked on server
if (!session.valid)
{
ShowBuyScreen(session.reason); // expired, banned, wrong device
return 0; // fail closed
}
if (session.isTrial && session.daysLeft <= 3)
ShowUpgradeBanner(session.daysLeft); // nudge before it ends
auto config = session.FetchConfig(); // only for valid sessions
RunApp(config, session.isTrial);
}Three details matter more than the rest. The key saved on disk is only a convenience, so deleting it leads to a new trial request, which the server refuses for a known device. The expiry is compared on the server, so the PC clock is irrelevant. And the configuration the app needs arrives only with a valid session, which is what makes patching session.valid pointless. The C++ authentication guide and its C# counterpart cover the session itself in more detail.
Converting a trial into a paid license
Stopping abuse protects revenue you would otherwise lose. Conversion is where the revenue comes from. The mechanics are simple: the user buys a paid key, enters it on the same machine, and the app validates it in place of the trial key. The details decide how many users actually do it.
- Let users buy from inside the app. A "Buy now" button that opens your checkout with the right product selected removes a search step. Deliver the key instantly after payment so the user can paste it while the app is still open.
- Keep their work. Whatever the user created during the trial should still be there after they upgrade. Losing a project at the moment of purchase is a refund waiting to happen.
- Bind the paid key to the same device. The trial already proved which machine they use, so the paid key can bind on first validation with no extra step.
- Offer a reason to decide now. A coupon that is valid for the last days of the trial, or for a week after it ends, gives the deadline a concrete benefit.
- Handle expired trials gracefully. When the trial ends, show what they will keep by buying and a clear path to do it, rather than a bare error.
If you sell subscriptions rather than perpetual licenses, the trial becomes the first period of the plan instead of a separate key. Subscription licensing for software explains how renewals and expiry interact with key validation.
Emails and in-app nudges that convert
Most trial users don't decide against your product. They forget about it. A small number of well-timed reminders, in the app and by email if you collected an address, recovers a share of them.
Day 0
Welcome
One link to the task most users come for
Day 3
First result
Tip for users who haven't finished a task yet
Day 11
Three days left
In-app banner and email with the price
Day 14
Trial ends
Clear upgrade path, work kept
Day 17
Last reminder
Optional time-limited coupon
Base the messages on what the user did, not only on the calendar. Someone who has exported five projects needs a price and a buy button. Someone who never finished setup needs help, not a countdown. Since the server sees every validation, you already know who is active, which is more useful than any open rate. Keep the number of emails small and always include a way to opt out.
Running trials with PapelShip
qPapel Auth gives you the server-side pieces this guide relies on for C, C++ and C# software: license keys checked on the server, HWID locking with reset cooldowns, encrypted and signed sessions, and server-side strings and files that are delivered only after a valid check. Its anti-VM protection helps with the snapshot problem, and remote settings let you adjust behaviour without shipping a new build, as described in updating protection remotely.
On the selling side, the PapelShip storefront delivers license keys instantly after payment, supports coupons for end-of-trial offers and takes card and crypto payments in one checkout. Plans start free; see pricing for fees, or the software licensing overview for how the pieces fit together. If you are still working out how to sell keys at all, how to sell software licenses online is a good place to start.
Frequently asked questions
Trials that end when they should
A trial is a promise to the user and to yourself: full access for a while, then a decision. Enforcing it on the server, binding it to the device and making the upgrade a single step keeps that promise in both directions. Honest users get a fair evaluation, and the ones looking for a reset script find that there is nothing on their machine to reset.
qPapel Security Team
Licensing and software protection