Qt下对软件试用期以及使用次数设置的一次探索

利用注册表和配置文件限制用户对软件的使用次数和天数。

XML配置文件字段如下:

<?xml version="1.0" encoding="UTF-8"?>
<UsageConfig description="用户使用权限配置">
	<IsPermanent>0</IsPermanent>
	<CDKey>Kv3cEciR3L</CDKey>
	<fisrtUsageDate>1995.09.02</fisrtUsageDate>
	<viableDays>4</viableDays>
	<viableTimes>3</viableTimes>
</UsageConfig>

字段代表的意思分别为:

1、是否被设为永久使用

字段为1,则说明该用户有永久使用权,不必再进行天数和次数的计算和判断;字段为0,则说明该用户为试用用户,要继续判断是否过期。

2 、配置文件序列号

每一次生成新的配置文件,序列号都不一致。软件第一次运行时,因注册表中不含该序列号,程序会判定为首次运行。

这防止了用户复制初始配置文件,并循环使用的问题。

之后每次运行都会将配置文件里序列号与注册表里的做判断。如果序列号不一致,则判定配置文件是新生成的,接下来按首次运行处理;否则则判定是同一配置文件,进行试用期的计算。

3 、首次启动日期

用来与当前日期作差,更新可使用次数。

另外,当发现本地日期小于首次启动日期时,则判定为用户修改了本地日期,终止使用。

4 、剩余使用天数

5、 剩余使用次数

注册表信息如下:

另外对配置文件采用加密处理,增强安全性。

 这里贴出几个重要代码:
1 生成任意长度字符串作为序列号

QString Trialtool::getRandomString(int length)
{
	qsrand(QDateTime::currentMSecsSinceEpoch());

	const char chrs[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	int chrs_size = sizeof(chrs);

	char* ch = new char[length + 1];
	memset(ch, 0, length + 1);
	int randomx = 0;
	for (int i = 0; i < length; ++i)
	{
		randomx= rand() % (chrs_size - 1);
		ch[i] = chrs[randomx];
	}

	QString ret(ch);
	delete[] ch;
	return ret;
}

2 获取网络时间

QString QVerification::getNetTime()
{
	QStringList urls;
	urls<<"time-b-g.nist.gov"<<"time-c-g.nist.gov"<<"time-d-g.nist.gov"
		<<"time-e-g.nist.gov"<<"time-a-wwv.nist.gov"<<"time-b-wwv.nist.gov"
		<<"time-c-wwv.nist.gov"<<"time-d-wwv.nist.gov"<<"time-e-wwv.nist.gov"
		<<"time-a-b.nist.gov"<<"time-b-b.nist.gov"<<"time-c-b.nist.gov"
		<<"time-d-b.nist.gov"<<"time-e-b.nist.gov"<<"time.nist.gov"
		<<"utcnist.colorado.edu"<<"utcnist2.colorado.edu";

	QString netTime;
	QTcpSocket *socket = new QTcpSocket();
	for (int i=0; i<urls.size(); i++)
	{
		socket->connectToHost(urls.at(i), 13);
		//QCoreApplication::processEvents();
		if (socket->waitForConnected())
		{
			if (socket->waitForReadyRead())
			{
				QString str(socket->readAll());
				netTime = str.trimmed();
				netTime = str.section(" ", 1, 2);
				qDebug()<<netTime;
				break;
			}
		}
		socket->close();
	}

	delete socket;
	return netTime;
}

3 计算日期间隔

int mon[12] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
int cal(int y, int m, int d)
{
	return y * 365 + y / 4 - y / 100 + y / 400 +
		mon[m - 1] + d - 1 +
		(((y % 100 != 0 && y % 4 == 0) || y % 400 == 0) && m > 2);
}

//...
int iDateBefore = cal(iyearBefor, imonthBefor, idayBefor);
int iDateAfter = cal(iYear, iMonth, iDay);

//检测时间是否被修改
if (iDateBefore > iDateAfter)
{
	//...			
}

试用期判定类的代码如下:

/**
	试用期校验类
**/
#ifndef QVERIFICATION_H
#define QVERIFICATION_H

#include <QObject>

class QVerification : public QObject
{
	Q_OBJECT

public:
	QVerification(QObject *parent = NULL);
	~QVerification();

	//1:永久使用
	//0:可以使用
	//-1:找不到xml文件
	//-2:复制了XML
	//-3: 时间被修改
	//-4:试用结束
	//-5:文件解密失败
	//-6:文件加密失败

	int verification();

private:
	void writeRegedit(QString key, QString val);
	QString getRegedit(QString key);
	void removeRegedit(QString key);

	QString getNetTime();
	void firstRun(QString CDKey, int iDays, int iTimes, QString date);
};

#endif // QVERIFICATION_H
#include "qverification.h"
#include "XmlTools.h"
#include <QMessageBox>
#include <QSettings>
#include <QTcpSocket>
#include <QDate>
#include <QCoreApplication>
#include "base64.h"

int mon[12] = { 0,31,59,90,120,151,181,212,243,273,304,334 };
int cal(int y, int m, int d)
{
	return y * 365 + y / 4 - y / 100 + y / 400 +
		mon[m - 1] + d - 1 +
		(((y % 100 != 0 && y % 4 == 0) || y % 400 == 0) && m > 2);
}

QVerification::QVerification(QObject *parent)
	: QObject(parent)
{
	QCoreApplication::setOrganizationName("Zhangzhihao");
	QCoreApplication::setApplicationName("testTrailTool");
}

QVerification::~QVerification()
{
	qDebug()<<"jkl";
}

void QVerification::writeRegedit(QString key, QString val)
{
	QSettings Setting(QSettings::NativeFormat, QSettings::SystemScope, QCoreApplication::organizationName(), QCoreApplication::applicationName());
	Setting.setValue(key, val);
}

QString QVerification::getRegedit(QString key)
{
	QSettings Setting(QSettings::NativeFormat, QSettings::SystemScope, QCoreApplication::organizationName(), QCoreApplication::applicationName());
	QString val = Setting.value(key).toString();
	return val;
}

void QVerification::removeRegedit(QString key)
{
	QSettings Setting(QSettings::NativeFormat, QSettings::SystemScope, QCoreApplication::organizationName(), QCoreApplication::applicationName());
	Setting.remove(key);
}

QString QVerification::getNetTime()
{
	QStringList urls;
	urls<<"time-b-g.nist.gov"<<"time-c-g.nist.gov"<<"time-d-g.nist.gov"
		<<"time-e-g.nist.gov"<<"time-a-wwv.nist.gov"<<"time-b-wwv.nist.gov"
		<<"time-c-wwv.nist.gov"<<"time-d-wwv.nist.gov"<<"time-e-wwv.nist.gov"
		<<"time-a-b.nist.gov"<<"time-b-b.nist.gov"<<"time-c-b.nist.gov"
		<<"time-d-b.nist.gov"<<"time-e-b.nist.gov"<<"time.nist.gov"
		<<"utcnist.colorado.edu"<<"utcnist2.colorado.edu";

	QString netTime;
	QTcpSocket *socket = new QTcpSocket();
	for (int i=0; i<urls.size(); i++)
	{
		socket->connectToHost(urls.at(i), 13);
		//QCoreApplication::processEvents();
		if (socket->waitForConnected())
		{
			if (socket->waitForReadyRead())
			{
				QString str(socket->readAll());
				netTime = str.trimmed();
				netTime = str.section(" ", 1, 2);
				qDebug()<<netTime;
				break;
			}
		}
		socket->close();
	}

	delete socket;
	return netTime;
}

int QVerification::verification()
{
	QString msg;
	Base64 base64;
	QString filePath = QCoreApplication::applicationDirPath().append("/").append("usage.xml");
	//Base64解密
	if (!base64.Decrypt(filePath))
	{
		QMessageBox::critical(0, QString::fromLocal8Bit("错误"), QString::fromLocal8Bit("读配置文件,解密失败!"));
		qDebug() << QString::fromLocal8Bit("读配置文件,解密失败!");
		return -5;
	}

	bool bRet = XmlTools::GetInstance()->LoadXmlFile(filePath.toLocal8Bit().constData());
	//找不到配置文件时失败
	if (!bRet)
	{
		QMessageBox::critical(0, QString::fromLocal8Bit("错误"), QString::fromLocal8Bit("加载秘钥文件失败!"));

		if (!base64.Encrypt(filePath))
		{
			qDebug() << QStringLiteral("读配置文件,加密失败!");
			return -5;
		}
		return -1;
	}

	//获取xml信息
	bool bIsPermanent = QString(XmlTools::GetInstance()->GetValue("UsageConfig/IsPermanent")).trimmed().toInt();
	QString CDKey = QString(XmlTools::GetInstance()->GetValue("UsageConfig/CDKey")).trimmed();
	QString fisrtUsageDate = QString(XmlTools::GetInstance()->GetValue("UsageConfig/fisrtUsageDate")).trimmed();
	int iViableDays = QString(XmlTools::GetInstance()->GetValue("UsageConfig/viableDays")).trimmed().toInt();
	int iViableTimes = QString(XmlTools::GetInstance()->GetValue("UsageConfig/viableTimes")).trimmed().toInt();

	//永久使用
	if (bIsPermanent)
	{
		QMessageBox::critical(0, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("你已获得永久使用权!"));
		if (!base64.Encrypt(filePath))
		{
			qDebug() << QStringLiteral("读配置文件,加密失败!");
			return -5;
		}
		return 1;
	}

	//获取网络时间,失败则试用本地时间
	QString timeNet = getNetTime();
	QDateTime currentime = QDateTime::currentDateTime(); 
	if (timeNet.isNull())
	{
		timeNet = currentime.toString("yyyy-MM-dd hh:mm:ss"); 
	}
	else
	{
		timeNet = QString("20").append(timeNet);
	}

	//拆分日期
	int iYear, iMonth, iDay;
	QStringList temp = timeNet.split(" ").at(0).split("-");
	iYear = temp.at(0).toInt();
	iMonth = temp.at(1).toInt();
	iDay = temp.at(2).toInt();

	//次数减一
	iViableTimes--;
	QStringList dateList;
	dateList<<QString::number(iYear)<<QString::number(iMonth)<<QString::number(iDay);
	QString date = dateList.join(".");

	//先获取序列号
	QString cdKeyReg = getRegedit("cdKey");

	//如果不存在则是首次启动
	if (cdKeyReg.isNull())
	{
		//写入首次启动日期,更新xml日期及次数
		msg = QString::fromLocal8Bit("%1日后的24:00时或启动%2次后试用结束!").arg(iViableDays).arg(iViableTimes);
		QMessageBox::critical(0, QString::fromLocal8Bit("提示"), msg);
		firstRun(CDKey, iViableDays, iViableTimes, date);

		if (!base64.Encrypt(filePath))
		{
			qDebug() << QStringLiteral("读配置文件,加密失败!");
			return -5;
		}
		return 0;
	}
	//否则如果是同一个秘钥文件,则判断首次启动日期
	else if(cdKeyReg == CDKey)
	{
		//如果日期一致,则判定为复制了初始XML
		if (fisrtUsageDate == "1995.09.02")
		{
			QMessageBox::critical(0, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("检测到可能的秘钥复制行为!"));
			if (!base64.Encrypt(filePath))
			{
				qDebug() << QStringLiteral("读配置文件,加密失败!");
				return -5;
			}
			return -2;
		}
		else//如果不一致
		{
			if (iViableDays > 0 && iViableTimes >= 0)
			{
				//计算时间间隔
				QStringList temp = fisrtUsageDate.split(".");
				int iyearBefor = temp.at(0).toInt();
				int imonthBefor = temp.at(1).toInt();
				int idayBefor = temp.at(2).toInt();

				int iDateBefore = cal(iyearBefor, imonthBefor, idayBefor);
				int iDateAfter = cal(iYear, iMonth, iDay);

				//检测时间是否被修改
				if (iDateBefore > iDateAfter)
				{
					QMessageBox::critical(0, QString::fromLocal8Bit("提示"), QString::fromLocal8Bit("检测到系统时间被改变!"));
					if (!base64.Encrypt(filePath))
					{
						qDebug() << QStringLiteral("读配置文件,加密失败!");
						return -5;
					}
					return -3;
				}

				//次数更新
				int iGap = iDateAfter - iDateBefore;
				iViableDays = iViableDays - iGap;
				if (iViableDays < 0)
				{
					msg = QString::fromLocal8Bit("软件试用已结束,请联系供应商注册!");
					QMessageBox::critical(0, QString::fromLocal8Bit("提示"), msg);

					if (!base64.Encrypt(filePath))
					{
						qDebug() << QStringLiteral("读配置文件,加密失败!");
						return -5;
					}
					return -4;
				}

				XmlTools::GetInstance()->SetValue("UsageConfig/viableDays", QString::number(iViableDays).toStdString().c_str());
				XmlTools::GetInstance()->SetValue("UsageConfig/viableTimes", QString::number(iViableTimes).toStdString().c_str());

				msg = QString::fromLocal8Bit("%1日后的24:00时或启动%2次后试用结束!").arg(iViableDays).arg(iViableTimes);
				QMessageBox::critical(0, QString::fromLocal8Bit("提示"), msg);

				if (!base64.Encrypt(filePath))
				{
					qDebug() << QStringLiteral("读配置文件,加密失败!");
					return -5;
				}
				return 0;
			}
			else
			{
				msg = QString::fromLocal8Bit("软件试用已结束,请联系供应商注册!");
				QMessageBox::critical(0, QString::fromLocal8Bit("提示"), msg);

				if (!base64.Encrypt(filePath))
				{
					qDebug() << QStringLiteral("读配置文件,加密失败!");
					return -5;
				}
				return -4;
			}
		}
	}
	//否则如果不是同一个,则是新的秘钥文件要先清除注册表,当首次运行处理
	else
	{
		//写入首次启动日期,更新xml日期及次数
		msg = QString::fromLocal8Bit("%1日后的24:00时或启动%2次后试用结束!").arg(iViableDays).arg(iViableTimes);
		QMessageBox::critical(0, QString::fromLocal8Bit("提示"), msg);
		firstRun(CDKey, iViableDays, iViableTimes, date);

		if (!base64.Encrypt(filePath))
		{
			qDebug() << QStringLiteral("读配置文件,加密失败!");
			return -5;
		}
		return 0;
	}
}

void QVerification::firstRun(QString CDKey, int iDays, int iTimes, QString date)
{
	//删除历史信息
	removeRegedit("cdKey");
	//removeRegedit("firstRunDate");

	XmlTools::GetInstance()->SetValue("UsageConfig/fisrtUsageDate", date.toLocal8Bit().constData());
	XmlTools::GetInstance()->SetValue("UsageConfig/viableDays", QString::number(iDays).toStdString().c_str());
	XmlTools::GetInstance()->SetValue("UsageConfig/viableTimes", QString::number(iTimes).toStdString().c_str());

	//32位程序运行在WOW64模式下,setting被存储在 HKEY_LOCAL_MACHINE\Software\WOW6432Node上
	//writeRegedit("firstRunDate", date);
	writeRegedit("cdKey", CDKey);
}

试用期设置界面如下:

完整源码以及测试环境连接如下:

https://download.csdn.net/download/qq_24282081/11431425

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值