C#手写UserPref存档

本文介绍了一种基于C#的自定义存档系统,名为UserPref,用于替代Unity中的PlayerPrefs。该系统使用DES加密算法保护数据,通过文件而非注册表进行存储,提供了一种更安全的数据持久化解决方案。

C#手写UserPref存档

这是我的第一篇博客,代码可能不够高效简洁,希望大家多多指点!

用过unity的肯定都知道PlayerPrefs类:

PlayerPrefs.GetInt("key_i");
PlayerPrefs.GetFloat("key_f");
PlayerPrefs.GetString("key_s");
PlayerPrefs.SetInt("key_i",0);
PlayerPrefs.SetFloat("key_f",0f);
PlayerPrefs.SetString("key_s","hello world!");

这是unity里自带的存档系统,通过修改注册表数值存档,相当简洁而好用。
诶?这么好用,我们为什么不把它从unity里搬出来,给其他C#程序用呢?(别告诉我从Dnspy里扒拉)

但是PlayerPrefs有个问题。

那就是它使用的是注册表。
而众所周知,有个东西叫Regedit。(win+r,然后输入“Regedit”回车)它可以编辑注册表,也就是说如果你用PlayerPrefs不加密保存数据,只要用户懂点技术,就可以修改存档。

所以,

1.我们为什么不用文件存档?
2.为什么不自带加密系统呢?

于是,

我做了一个类似PlayerPref的类-UserPref!(又不一定是做游戏用)

原理:

1. 存档地址:C:\Users\Administrator\AppData\Roaming\程序名\prefs.dat
2. 加密算法:DES
3. 存储格式:

(解密后的prefs.dat)
/数值名/
/数值类型,默认是string/
/以string保存的数据/
/数值名/
/数值类型,默认是string/
/以string保存的数据/
/数值名/
/数值类型,默认是string/
/以string保存的数据/
……

瞎BB 说 了,直接上代码。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace UserPreference
{
	public class UserPrefs
	{
		/// <summary>
		/// 应用程序的名称
		/// </summary>
		public static string AppName;

		/// <summary>
		/// 存储加密密码,必须是八位。
		/// </summary>
		public static string DataKey;

		/// <summary>
		/// "C:/"
		/// </summary>
		private static string SystemDiskCode
		{
			get
			{
				return Environment.GetEnvironmentVariable("systemdrive") + '\\';
			}
		}

		/// <summary>
		/// @"C:\Users\Administrator\AppData\Roaming\"+程序名
		/// </summary>
		private static string DataFolderPath
		{
			get
			{
				return Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\" + AppName;
			}
		}

		/// <summary>
		/// 清除文件里的数据
		/// </summary>
		private static void ClearDataFileText()
		{
			File.WriteAllText(DataPath, "");
		}

		public static string DataPath
		{
			get
			{
				return DataFolderPath + @"\prefs.dat";
			}
		}

		/// <summary>
		/// 逐行读取文件,并解密数据
		/// </summary>
		/// <returns></returns>
		private static string[] ReadFileDataInLines()
		{
			try
			{
				if (File.Exists(DataPath))
				{
					string[] num = File.ReadAllLines(DataPath);
					for (int i = 0; i < num.Length; i++)
					{
						num[i] = SystemSafety.StringDecryptByDES(num[i], DataKey);

					}
					return num;
				}
				else
				{
					Directory.CreateDirectory(DataFolderPath);
					File.WriteAllText(DataPath, "");
					return new string[0];
				}
			}
			catch (Exception)
			{
				Directory.CreateDirectory(DataFolderPath);
				File.WriteAllText(DataPath, "");
				return new string[0];
			}
		}

		/// <summary>
		/// 读取数据。
		/// </summary>
		/// <returns>读取到的数据。</returns>
		private static Data[] ReadData()
		{
			string[] list = ReadFileDataInLines();
			if (list.Length % 3 != 0)
			{
				ClearDataFileText();
				return new Data[0];
			}
			else
			{
				Data[] data = new Data[list.Length / 3];
				for (int i = 0; i < list.Length; i += 3)
				{
					data[i / 3].DataName = list[i];
					switch (list[i + 1])
					{
						case "int":
							data[i / 3].dataType = Data.DataType.Integer;
							break;
						case "str":
							data[i / 3].dataType = Data.DataType.String;
							break;
						case "flt":
							data[i / 3].dataType = Data.DataType.Float;
							break;
						default:
							data[i / 3].dataType = Data.DataType.String;
							break;
					}
					data[i / 3].DataValue = list[i + 2];
				}
				return data;
			}
		}

		/// <summary>
		/// 将数据加密并保存。
		/// </summary>
		/// <param name="datas">数据</param>
		private static void SaveData(Data[] datas)
		{
			string[] texts = new string[datas.Length * 3];
			for (int i = 0; i < datas.Length; i++)
			{
				texts[i * 3] = datas[i].DataName;
				switch (datas[i].dataType)
				{
					case Data.DataType.Integer:
						texts[i * 3 + 1] = "int";
						break;
					case Data.DataType.String:
						texts[i * 3 + 1] = "str";
						break;
					case Data.DataType.Float:
						texts[i * 3 + 1] = "flt";
						break;
				}
				texts[i * 3 + 2] = datas[i].DataValue;
			}
			for (int i = 0; i < texts.Length; i++)
			{
				texts[i] = SystemSafety.StringEncryptByDES(texts[i], DataKey);
			}
			File.WriteAllLines(DataPath, texts);
		}

		/// <summary>
		/// 读取指定名字的整数数值
		/// </summary>
		/// <param name="key">整数的名字</param>
		/// <param name="defaultValue">默认返回值,当该数值不存在或读取出现问题时返回。</param>
		/// <returns>获取到的整数</returns>
		public static int GetInt(string key, int defaultValue = 0)
		{
			foreach (var r in ReadData())
			{
				if (r.DataName == key && r.dataType == Data.DataType.Integer)
				{
					int o;
					try
					{
						o = int.Parse(r.DataValue);
					}
					catch (FormatException)
					{
						SetInt(key, defaultValue);
						return defaultValue;
					}
					return o;
				}
			}
			return defaultValue;
		}

		/// <summary>
		/// 读取指定名字的浮点数数值
		/// </summary>
		/// <param name="key">浮点数的名字</param>
		/// <param name="defaultValue">默认返回值,当该数值不存在或读取出现问题时返回。</param>
		/// <returns>获取到的浮点数</returns>
		public static float GetFloat(string key, float defaultValue = 0)
		{
			foreach (var r in ReadData())
			{
				if (r.DataName == key && r.dataType == Data.DataType.Float)
				{
					float o;
					try
					{
						o = float.Parse(r.DataValue);
					}
					catch (FormatException)
					{
						SetFloat(key, defaultValue);
						return defaultValue;
					}
					return o;
				}
			}
			return defaultValue;
		}

		/// <summary>
		/// 读取指定名字的字符串数值
		/// </summary>
		/// <param name="key">字符串的名字</param>
		/// <param name="defaultValue">默认返回值,当该数值不存在或读取出现问题时返回。</param>
		/// <returns>获取到的字符串</returns>
		public static string GetString(string key, string defaultValue = "")
		{
			foreach (var r in ReadData())
			{
				if (r.DataName == key && r.dataType == Data.DataType.Integer)
				{
					return r.DataValue;
				}
			}
			return defaultValue;
		}

		/// <summary>
		/// 写入一个整数,如果该数值已存在,则修改该数值。
		/// </summary>
		/// <param name="key">数值的名字</param>
		/// <param name="value">需要存入的整数</param>
		public static void SetInt(string key, int value)
		{
			Data[] datas = ReadData();
			int x = GetKeyIndex(key, Data.DataType.Integer, ReadData());
			if (x == -1)
			{
				Array.Resize<Data>(ref datas, datas.Length + 1);
				datas[datas.Length - 1].DataName = key;
				datas[datas.Length - 1].DataValue = value.ToString();
				datas[datas.Length - 1].dataType = Data.DataType.Integer;
			}
			else
			{
				datas[x].DataName = key;
				datas[x].DataValue = value.ToString();
				datas[x].dataType = Data.DataType.Integer;
			}
			SaveData(datas);
		}

		/// <summary>
		/// 将数据中的所有数值都删除。
		/// </summary>
		public static void DeleteAllKeys()
		{
			ClearDataFileText();
		}

		/// <summary>
		/// 指定一个数值,如果该数值存在,就将其删除。
		/// </summary>
		/// <param name="key"></param>
		/// <param name="type"></param>
		public static void DeleteKey(string key, Data.DataType type)
		{
			Data[] d = ReadData();
			int x = GetKeyIndex(key, type, d);
			if (x != -1)
			{
				List<Data> l = d.ToList();
				l.RemoveAt(x);
				SaveData(l.ToArray());
			}
		}

		/// <summary>
		/// 写入一个浮点数,如果该数值已存在,则修改该数值。
		/// </summary>
		/// <param name="key">数值的名字</param>
		/// <param name="value">需要存入的浮点数</param>
		public static void SetFloat(string key, float value)
		{
			Data[] datas = ReadData();
			int x = GetKeyIndex(key, Data.DataType.Float, ReadData());
			if (x == -1)
			{
				Array.Resize<Data>(ref datas, datas.Length + 1);
				datas[datas.Length - 1].DataName = key;
				datas[datas.Length - 1].DataValue = value.ToString();
				datas[datas.Length - 1].dataType = Data.DataType.Float;
			}
			else
			{
				datas[x].DataName = key;
				datas[x].DataValue = value.ToString();
				datas[x].dataType = Data.DataType.Float;
			}
			SaveData(datas);
		}

		/// <summary>
		/// 写入一个字符串值,如果该数值已存在,则修改该数值。
		/// </summary>
		/// <param name="key">数值的名字</param>
		/// <param name="value">需要存入的字符串</param>
		public static void SetString(string key, string value)
		{
			Data[] datas = ReadData();
			int x = GetKeyIndex(key, Data.DataType.String, ReadData());
			if (x == -1)
			{
				Array.Resize<Data>(ref datas, datas.Length + 1);
				datas[datas.Length - 1].DataName = key;
				datas[datas.Length - 1].DataValue = value;
				datas[datas.Length - 1].dataType = Data.DataType.String;
			}
			else
			{
				datas[x].DataName = key;
				datas[x].DataValue = value;
				datas[x].dataType = Data.DataType.String;
			}
			SaveData(datas);
		}

		/// <summary>
		/// 检查指定数值是否存在。
		/// </summary>
		/// <param name="key">指定数值名字</param>
		/// <param name="type">指定数值类型</param>
		/// <returns>是否存在。</returns>
		public static bool HasKey(string key, Data.DataType type)
		{
			foreach (var r in ReadData())
			{
				if (r.DataName == key && r.dataType == type)
				{
					return true;
				}
			}
			return false;
		}

		/// <summary>
		/// 获取指定数据在数组里的索引,如果不存在则输出-1。
		/// </summary>
		/// <param name="key">指定数值名字</param>
		/// <param name="type">指定数值类型</param>
		/// <param name="datas">数据的数组</param>
		/// <returns>指定数据在数组里的索引,如果不存在则为-1</returns>
		private static int GetKeyIndex(string key, Data.DataType type, Data[] datas)
		{
			for (int i = 0; i < datas.Length; i++)
			{
				if (datas[i].DataName == key && datas[i].dataType == type)
				{
					return i;
				}
			}
			return -1;
		}
	}

	/// <summary>
	/// 一个数据值的结构体。
	/// </summary>
	public struct Data
	{
		public enum DataType
		{
			Integer,
			Float,
			String
		}
		public DataType dataType;//类型
		public string DataName;//数据的名字
		public string DataValue;//数据的值(string)
	}

	/// <summary>
	/// 这个用来加解密的类是我在破解滚动的天空时从文件里翻出来的,改了改。(无耻)(滑稽)
	/// </summary>
	public class SystemSafety
	{
		public static string StringEncryptByDES(string encryptInfo, string key, string iv = "12345678")
		{
			string result;
			try
			{
				byte[] bytes = Encoding.UTF8.GetBytes(key);
				byte[] bytes2 = Encoding.UTF8.GetBytes(iv);
				DESCryptoServiceProvider descryptoServiceProvider = new DESCryptoServiceProvider();
				using (MemoryStream memoryStream = new MemoryStream())
				{
					byte[] bytes3 = Encoding.UTF8.GetBytes(encryptInfo);
					try
					{
						using (CryptoStream cryptoStream = new CryptoStream(memoryStream, descryptoServiceProvider.CreateEncryptor(bytes, bytes2), CryptoStreamMode.Write))
						{
							cryptoStream.Write(bytes3, 0, bytes3.Length);
							cryptoStream.FlushFinalBlock();
						}
						result = Convert.ToBase64String(memoryStream.ToArray());
					}
					catch
					{
						result = encryptInfo;
					}
				}
			}
			catch
			{
				result = "DES加密出错";
			}
			return result;
		}

		public static string StringDecryptByDES(string encryptedString, string key, string iv = "12345678")
		{
			byte[] bytes = Encoding.UTF8.GetBytes(key);
			byte[] bytes2 = Encoding.UTF8.GetBytes(iv);
			DESCryptoServiceProvider descryptoServiceProvider = new DESCryptoServiceProvider();
			string result;
			using (MemoryStream memoryStream = new MemoryStream())
			{
				byte[] array = Convert.FromBase64String(encryptedString);
				try
				{
					using (CryptoStream cryptoStream = new CryptoStream(memoryStream, descryptoServiceProvider.CreateDecryptor(bytes, bytes2), CryptoStreamMode.Write))
					{
						cryptoStream.Write(array, 0, array.Length);
						cryptoStream.FlushFinalBlock();
					}
					result = Encoding.UTF8.GetString(memoryStream.ToArray());
				}
				catch
				{
					result = encryptedString;
				}
			}
			return result;
		}
	}
}

使用方法(非常建议你认真看一看)

1. UserPrefs.AppName 必须设置!写上你的程序名就行,不写会报错。
2. UserPrefs.DataKey 必须设置!这是数据加解密的密码,且必须有8个字符。
3. 可能还有Bug,不过经测试目前没有问题,我也会以后再更新。

欢迎指出不足,谢谢各位大佬们!

<think>好的,我现在需要帮助用户解决Keil编译过程中出现的LICENSE ERROR和REGISTRY READ ERROR,以及链接器相关问题。首先,用户提到了BL51 linker的许可证错误R230和R207,还有注册表读取错误。根据用户提供的引用[1]和[2],我需要结合这些信息来找出解决方案。 首先,LICENSE ERROR通常与许可证未正确安装或配置有关。引用[1]中提到,添加新的编译器版本可能解决许可证检查失败的问题。这可能是因为用户安装的Keil版本与当前许可证不兼容,或者许可证管理器未能正确识别。需要指导用户检查许可证安装,并确保使用的是支持BL51的许可证,因为BL51是传统8051的链接器,可能需要特定的许可证。 接下来,REGISTRY READ ERROR可能与注册表项损坏或权限问题有关。Keil在安装时会在注册表中写入信息,如果用户权限不足或注册表项被损坏,可能导致此错误。需要建议用户以管理员身份运行Keil,或者修复注册表权限,甚至重新安装Keil。 关于链接器问题,比如BL51的许可证错误R230和R207,这通常表示许可证无效或未找到。用户可能需要检查许可证是否包含BL51的授权,或者许可证文件是否损坏。此外,确保Keil的许可证管理器(如LMAN)服务正在运行也很重要。 另外,用户可能在项目配置中选择了错误的编译器或链接器版本。引用[1]中提到在Options for Target的Folders/Extensions选项卡中可以调整编译器版本,可能需要用户确认是否选择了正确的工具链,比如ARM编译器还是传统的C51工具链。 用户可能混淆了不同产品线的许可证,比如MDK-ARM和C51的许可证是分开的。如果用户同时使用多个工具链,需要确保所有许可证都正确安装。此外,防火墙或杀毒软件有时会阻止许可证管理器访问网络,导致许可证检查失败,暂时禁用这些软件可能有帮助。 最后,如果以上方法都无效,可能需要联系Keil的技术支持,提供具体的错误代码和日志,以便进一步诊断。需要提醒用户在操作前备份项目和设置,避免数据丢失。 总结步骤:检查许可证安装、以管理员身份运行、修复注册表、重新安装Keil、调整编译器设置、检查防火墙设置、联系支持。需要分点说明,确保用户能逐步操作。</think>### Keil编译错误解决方案分步说明 #### 一、LICENSE ERROR(R230/R207)处理步骤 1. **许可证类型验证** - BL51链接器属于Keil C51开发工具链,需确认安装的是C51产品许可证[^1] - 打开License Management查看授权状态:`File > License Management` - 有效授权应包含类似`LIC0=XXXXXXXX (C51)`的条目 2. **许可证修复操作** ```regedit HKEY_CURRENT_USER\Software\Keil\PKM HKEY_LOCAL_MACHINE\SOFTWARE\Keil\PKM ``` - 右键选择权限 > 完全控制 > 应用(需管理员权限) - 删除旧许可证文件:`C:\Keil_v5\TOOLS.ini`中多余条目 3. **环境重置方法** - 运行Keil自带的注册表清理工具: $$ \text{开始菜单 > Keil uVision5 > Clear Registry Entries} $$ - 重新导入许可证文件(.LIC格式) #### 二、REGISTRY READ ERROR解决方案 1. **权限提升操作** - 右键uVision图标 > 属性 > 兼容性 > 勾选"以管理员身份运行此程序" - 对安装目录(如C:\Keil_v5)右键 > 属性 > 安全 > 添加完全控制权限 2. **注册表修复示范** ```powershell # 以管理员身份运行PowerShell reg add "HKEY_CURRENT_USER\Software\Keil" /f reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Keil" /f ``` #### 三、链接器配置检查 1. **工具链版本验证** - 打开`Options for Target > Folders/Extensions` - 确认"Use default compiler version"处于激活状态 - 检查BL51 Loc字段路径是否正确(默认应为`C:\Keil_v5\C51\BIN\BL51.EXE`) 2. **项目配置对比** | 正确配置项 | 错误配置表现 | |------------|--------------| | Target页选择C51 Device | 显示ARM Compiler选项 | | Output页生成.hex文件 | 出现ARM链接错误 | | Debug页使用C51 Debugger | 显示ULINK2调试器 | #### 四、进阶排查方案 1. **网络许可证诊断** - 检查1700端口状态:`netstat -ano | findstr :1700` - 重启许可证服务:`services.msc > Keil License Manager` 2. **编译环境隔离测试** - 新建空白工程复现错误 - 尝试在虚拟机环境安装纯净系统测试
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值