试用期限制需持久化首次运行时间:Windows用注册表HKEY_CURRENT_USER\Software\YourApp存InstallDate(DateTime.UtcNow.ToString("O")),跨平台写JSON/文本到ApplicationData;校验时用(DateTime.UtcNow - installUtc).TotalDays防系统时间篡改,加校验值防删除重置,结束时弹窗明确提示购买路径。

如何用 DateTime 和注册表/文件存储首次运行时间
试用期限制本质是「记住用户第一次打开软件的时间」,后续每次启动都比对当前时间与该时间的差值。C# 没有内置“试用期 API”,必须自己持久化这个时间点。
最轻量、兼容性最好的方式是写入注册表(Windows)或配置文件(跨平台)。不推荐用环境变量或内存缓存——关机就丢。
- Windows 下优先用
Microsoft.Win32.Registry写入HKEY_CURRENT_USER\Software\YourApp,键名如InstallDate,值为DateTime.Now.ToString("O") - 跨平台场景改用
System.IO.File.WriteAllText存到Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)下的 JSON 或纯文本文件 - 务必加异常捕获:注册表被策略禁用、目录无写权限、磁盘满等情况都会导致写入失败,此时应视为“无法验证”,可降级为仅提示而非直接拒绝启动
判断是否过期时为什么不能只比对 DateTime.Today
用 DateTime.Today 会丢失时分秒精度,导致“刚安装就过期”或“多试用一天”。更严重的是,如果用户手动调快系统时间,DateTime.Today 会立刻跳变,绕过限制。
正确做法是记录完整 DateTime.UtcNow(避免本地时区切换干扰),再用 (DateTime.UtcNow - installUtc).TotalDays 计算天数。
- 示例逻辑:
var installUtc = DateTime.Parse(installString); var daysUsed = (DateTime.UtcNow - installUtc).TotalDays; if (daysUsed > 30.0) { /* 拒绝启动 */ } - 注意:用
TotalDays而非Days——后者只取整数部分,30 天零 23 小时会被误判为未超期 - 不要用
DateTime.Now做计算:用户若把系统时间调慢,Now会倒退,daysUsed可能变成负数,逻辑崩溃
如何防止用户删注册表/配置文件重置试用期
纯删文件就能重置,说明防护太弱。这不是“能不能防住黑客”的问题,而是“让普通用户不会无意或轻易绕过”的平衡点。
- 在首次写入后,额外生成一个简单校验值,比如
installUtc.Ticks ^ 0xABCDEF12,存为另一键(如InstallCheck),启动时校验匹配才认可该时间有效 - 把安装时间拆成两部分存储:一部分放注册表,一部分放程序集自定义属性(
AssemblyMetadata),启动时两者拼合还原——重装软件才会重置,删注册表无效 - 不推荐加密或强签名:密钥硬编码在程序里毫无意义,且增加调试成本;混淆代码即可提高删除后重新伪造的门槛
- 真正难防的是替换整个 exe 文件——这已超出试用期逻辑范畴,属于分发和签名问题
试用期结束时该弹窗还是静默退出
直接 Environment.Exit(0) 或关闭主窗体,用户会困惑“程序崩了?”。必须明确传达“试用已结束”,并提供购买路径。
- 弹窗内容必须包含:已使用天数、总试用天数、购买链接(URL 或邮件)、客服联系方式;避免模糊表述如“授权异常”
- 按钮至少要有「立即购买」和「稍后再说」——后者不应关闭程序,而应最小化主窗口或进入只读模式(如禁用保存、导出)
- 如果应用有网络能力,可在弹窗前尝试请求一次授权服务器(哪怕只是检查域名是否可连),避免用户因断网误以为“被误封”
- 切勿记录用户点击“稍后再说”的次数并自动升级为强制退出——这会激化反感,也违反多数地区消费者提示规范
试用期逻辑本身很简单,复杂点全在边界上:系统时间被篡改、多设备同步、用户反复重装、权限受限环境。别堆砌技术,先确保首次写入可靠、时间计算鲁棒、提示信息清晰——剩下的,交给购买动机和产品价值。


















