您的位置:首页 > 编程语言

.NET编程技术——实验07:Windows的注册表的基本使用方法,文件流保存为文本文件基础用法

2018-01-09 17:51 766 查看
阅读须知:纯粹是本人啊Jun作为初学者的笔记和个人对其中知识的复习,大神请绕道。


实验01:VS2015 对象浏览器的使用、简单调试和断点使用。

实验04:RS485串口调试、基本控件使用、CRC算法应用

实验05:RS485串口通讯,串口指令的收发应用,完成串口基础功能,github地址:实验05

实验06:窗体应用的最小化在Windows状态栏中显示托盘图标

实验07:Windows的注册表的基本使用方法,文件流保存为文本文件基础用法

实验08:利用GDI+技术生成简单验证码,类似excel的数据折线图、并保存为图片

实验09:Access数据库的创建、表创建、MD5加密,数据、图片保存在数据库的用法

实验10:将DataGridView表单数据保存为excel文件,表单的基本格式化

包含实验06~实验10的功能:github地址:实验10

来看一下实验07题目:

1、添加子菜单和相应的窗体,收发数据,译码,显示当前温度;

2、添加子菜单和相应的窗体,可设置通讯各参数,可以进行CRC校验值测试;

3、添加子菜单和相应的窗体,可进行用户密码的设置和
1605e
修改,可删除/添加用户,用户和密码信息保存在注册表中;

4、按照规则,如一天一个数据文件,或新建一个文件等,把测量到的温度值保存到文本文件中,要求最少有4列:序号、日期时间、用户名、温度值;

5、添加子菜单和相应的窗体,可以显示保存的数据文件的内容,但不能修改。

第 1 题的显示温度功能:在实验05中已经说过了,.NET编程技术——实验05,可以参考一下

第2
题的CRC功能:在实验04中也实现了, .NET编程技术——实验04,可以参考一下



3、添加子菜单和相应的窗体,可进行用户密码的设置和修改,可删除/添加用户,用户和密码信息保存在注册表中;

可以 Win+R ,输入regedit,便可以出现注册表了








先看一下效果图:

注册窗体,下为注册一个普通用户:现在还想表达的是,密码在注册表里进行RAS加密,原密码是123,看看加密后的密码是什么。











经过RAS加密后,密码就变成了如下的字符串





在讲注册表之前,先来普及一下知识,如下图所示,






结合上图和下面代码的注释,我觉得理解注册表已经没有什么问题了。

好了,那现在来开始讲一下注册表 和RAS 加密

注册表,我专门给注册表的操作整合出了一个 Redegit类 ,

RSA 加密,我专门给
RSA加密 整合出一个 RSA类

结合上面的 注册窗体 来说吧,

 注册窗体代码

public partial class RegisterForm : Form
{
private RSA ras;
private Regedit registry;  //注册表变量
private Regedit adminRegistry;
private Regedit userRegistry;

public RegisterForm()
{
InitializeComponent();
}

/// <summary>
/// 实现“注册表”的注册
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RegisterForm_Load(object sender, EventArgs e)
{
// primaryKey = Registry.CurrentUser;   //注册在当前用户里,如果使用管理员运行该软件才能注册在LocalMachine里。
//softWare= primaryKey.CreateSubKey(@"SOFTWARE\JUN-SerialPort");

userCombobox.Items.Add("管理员");
userCombobox.Items.Add("一般用户");
userCombobox.Text = userCombobox.Items[0].ToString();

registry = new Regedit(Registry.CurrentUser, @"SOFTWARE\JUN-SerialPort");  //定义一个注册表
adminRegistry = new Regedit(Registry.CurrentUser, @"SOFTWARE\JUN-SerialPort\admin");  //定义管理员注册表
userRegistry = new Regedit(Registry.CurrentUser, @"SOFTWARE\JUN-SerialPort\user");   //定义用户注册表
ras = new RSA();  //定义一个RAS对象,用于加密和解密
}

/// <summary>
/// "确定"按钮事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void okButton_Click(object sender, EventArgs e)
{
string userName = userNameTextBox.Text.Trim();  //去除字符串前后面的空格
string password = passwordTextBox.Text.Trim();

//if (String.IsNullOrEmpty(userNameTextBox.Text))
if (userName != String.Empty )
{
if (password != String.Empty)
{

if (String.Equals(password, passwordTextBox2.Text.Trim())) //两次密码相同
{
if (userCombobox.Text == "管理员")
{
//数据库和注册表同时操作
if (adminRegistry.isRegistryValueNameExist(userName))
{
MessageBox.Show("该管理员已存在!");
}
else
{
//下面要对密码进行加密再储存在注册表里面
adminRegistry.SetValue(userName, ras.Encryption(password), RegistryValueKind.String);

MessageBox.Show("恭喜管理员 “" + userName + "” 注册成功");
this.DialogResult = DialogResult.Yes;
//this.Close();
}
}
else
{
if (userRegistry.isRegistryValueNameExist(userName))
{
MessageBox.Show("该用户已存在!");
}
else
{
//下面要对密码进行加密再储存在注册表里面
userRegistry.SetValue(userName, ras.Encryption(password), RegistryValueKind.String);

MessageBox.Show("恭喜用户 “" + userName + "” 注册成功");
this.DialogResult = DialogResult.Yes;
//this.Close();
}
}
}
else
MessageBox.Show("两次输入密码不一致");
}
else
MessageBox.Show("输入密码不能为空");
}
else
MessageBox.Show("用户名不能为空");
}

private void cancelButton_Click(object sender, EventArgs e)
{
this.Close();
}
}


注册表: Redegit类

using Microsoft.Win32; //需要引用Win32,因为需要用到注册表类型: RegistryKey 类型
public class Regedit
{
private RegistryKey softWare; //注册表类型变量
private string[] sValueNameColl;  //键值表项
private string[] sKeyNameColl;  //注册表项

private RSA ras = new RSA();  // 这是我整合的 RSA 常用的方法的一个类

/// <summary>
/// 构造函数
/// </summary>
/// <param name="registry">判断哪里添加注册表</param>
/// <param name="subKey">注册表的软件名称</param>
public Regedit(RegistryKey registry, string subKey)
{
this.softWare = registry.CreateSubKey(subKey); //创建一个新子项或打开一个现有子项以进行写访问。
// this.softWare = registry.OpenSubKey(subKey,true);  //需要加上true才能对键值进行写操作,否则默认是读操作

sKeyNameColl = softWare.GetValueNames();
sValueNameColl = softWare.GetValueNames();  //获取JUN-SerialPort所有子项
}

/// <summary>
///  判断键值是否存在
/// </summary>
/// <param name="sValueName">注册表名 </param>
/// <param name="value">返回键值的值</param>
/// <returns></returns>
public bool isRegistryValueNameExist(string sValueName, ref string value)
{
foreach (string sName in sValueNameColl)
{
if (sName == sValueName)  //如果键匹配了
{
value = ras.Decrypt(Convert.ToString(softWare.GetValue(sName))); //那就将该键的值,即密码,进行RSA解密

return true;
}
}
return false;
}

/// <summary>
///  判断键值是否存在
/// </summary>
/// <param name="sValueName">注册表名 </param>
/// <returns></returns>
public bool isRegistryValueNameExist(string sValueName)
{
foreach (string sName in sValueNameColl)
{
if (sName == sValueName)
{
return true;
}
}
return false;
}

/// <summary>
/// 在注册表里添加键值
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>
/// <param name="valueKind"></param>
public void SetValue(string name,string value, Microsoft.Win32.RegistryValueKind valueKind)
{
try
{
softWare.SetValue(name, value, valueKind);
}
catch(Exception ex)
{
MessageBox.Show("出现异常:"+ex);
}
}

/// <summary>
/// 返回所有键值
/// </summary>
/// <returns></returns>
public string[] GetKeyNames()
{
return sValueNameColl;
}

/// <summary>
/// 删除注册表键值
/// </summary>
/// <param name="keyName">键值</param>
/// <returns></returns>
public bool DeleteKey(string keyName)
{
if (sValueNameColl.Length > 0)
{
softWare.DeleteValue(keyName); //只需要删除其中的键,那么键和值都会被删除
return true;
}
else
return false;
}

}


RSA加密类: RSA类
(用法)

using System.Security;  //RAS加密需要引用的包
using System.Security.Cryptography;
public class RSA
{

/// <summary>
/// 使用”RSA“加密
/// </summary>
/// <param name="express"></param>
/// <returns></returns>
public string Encryption(string express)
{
CspParameters param = new CspParameters(); //CspParameters: 包含传递给执行加密计算的加密服务提供程序(CSP) 的参数
param.KeyContainerName = "JunKey";  //密钥容器的名称,保持加密解密一致才能解密成功
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(param))
{
byte[] plainData = Encoding.Default.GetBytes(express);  //将要加密的字符串转换为字节数组
byte[] encryptData = rsa.Encrypt(plainData, false);//将加密后的字节数据转换为新的加密字节数组
return Convert.ToBase64String(encryptData); //将加密后的字节数组转换为字符串
}
}

/// <summary>
///  使用”RSA“解密
/// </summary>
/// <param name="ciphertext"></param>
/// <returns></returns>
public string Decrypt(string ciphertext)
{
CspParameters param = new CspParameters();
param.KeyContainerName = "JunKey";      //密钥容器的名称,保持加密解密一致才能解密成功
using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(param))
{
byte[] encrytData = Convert.FromBase64String(ciphertext);
byte[] decryptData = rsa.Decrypt(encrytData, false);
return Encoding.Default.GetString(decryptData);
}
}

}


ok,添加用户写好了,那就下面写一下 删除用户吧,删除用户的权限只有管理员才有的,





点击‘-’进行删除用户操作,combobox出现了注册里的“user”中两个一般用户,我想的是没有删除管理员的功能,目标为了管理员只有在注册表才能删除,为了安全。





这里加了一个小操作,现在的user已经不在combobox上了,由于暂时无法实现控件的自动更新,之前尝试了Control.Refresh() 或者Timer控件都无法实现控件更新,所以做了如下的小操作,满足自己强迫症.


 ,哈哈哈哈


下面附上删除用户的代码:

/// <summary>
/// 加载“注册表”的用户
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DeleteUser_Load(object sender, EventArgs e)
{
regedit = new Regedit(Registry.CurrentUser, @"SOFTWARE\JUN-SerialPort\user");
keyNames = regedit.GetKeyNames();

foreach (string name in keyNames)
userComboBox.Items.Add(name);
if (keyNames.Length > 0)
{
userComboBox.Text = userComboBox.Items[0].ToString();
}

}


/// <summary>
/// 点击“确定”删除
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void sureButton_Click(object sender, EventArgs e)
{
if (keyNames.Length == 0)
MessageBox.Show("对不起,用户为空,没有用户可以删除!");

else  //键值不为空
{
if (userComboBox.Items.Count == 1)
userComboBox.Text = userComboBox.Items[0].ToString();
else
userComboBox.Text = userComboBox.Items[userComboBox.SelectedIndex].ToString();
if (MessageBox.Show("你将要删除用户 “" + userComboBox.Text + "” 吗?", "删除用户", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
{
if (regedit.DeleteKey(userComboBox.Text) && access.deleteUser("一般用户", userComboBox.Text)) //在数据库和注册表内一起删除
{
MessageBox.Show("删除成功!");
userComboBox.Items.Clear();   //先将Combobox清空
DeleteUser_Load(sender, e);  //再重新加载
}
else
MessageBox.Show("删除失败!");
}
}
}


再到修改密码功能:







附上代码:

/// <summary>
/// "确认修改"按钮事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void changeButton_Click(object sender, EventArgs e)
{
string oldPwd = null;

if (LoginForm.userType == "管理员")
{
adminRegistry.isRegistryValueNameExist(LoginForm.userName, ref oldPwd);
}

else //用户类型是 “一般用户”
{
userRegistry.isRegistryValueNameExist(LoginForm.userName, ref oldPwd);
}

if (oldPWDTextbox.Text == oldPwd)  //如果旧密码输入正确才能修改密码
{
if (newPWDTextBox.Text.Trim() != String.Empty)
{

if (newPWDTextBox.Text.Trim() == newPWDTextBox2.Text.Trim())
{
if (LoginForm.userType == "管理员")  //“管理员修改密码”
{
//下面要对密码进行加密再储存在注册表里面
adminRegistry.SetValue(LoginForm.userName, ras.Encryption(newPWDTextBox.Text.Trim()), RegistryValueKind.String);

}
else  //“一般用户修改密码”
{
userRegistry.SetValue(LoginForm.userName, ras.Encryption(newPWDTextBox.Text.Trim()), RegistryValueKind.String);

}
MessageBox.Show("修改成功!");
}
else
MessageBox.Show("两次密码输入不一致");
}
else
MessageBox.Show("输入密码不能为空");
}
else
{
MessageBox.Show("原密码输入错误");
}

}


下面搞完第3 题了,妈呀,注册表的增删改查,,全做了,以后的实验中还有ACCESS的数据库的增删改查,想想都兴奋

4、按照规则,如一天一个数据文件,或新建一个文件等,把测量到的温度值保存到文本文件中,要求最少有4列:序号、日期时间、用户名、温度值;

下面终于到最关键的时刻了,就是保存txt文件



这里就涉及到文件流的操作了

private FileStream file;//格式:每天一个文件
private StreamWriter fileWriter;  //用于写文件
private StreamReader fileReader;  //用于读取文件的行数
private static int line = 0;  //静态变量,表示记录数


其实不算难,只要注意文件流的关闭和打开就Ok了,假如你的 StreamReader 读取完没有 close掉的话,就不能用 StreamWriter 写文件了。

if (File.Exists(temperatureFile + '\\' + fileName))  //判断记录温度的txt是否存在
{
System.Diagnostics.Debug.Write("文件已经存在");

fileReader = new StreamReader(temperatureFile + '\\' + fileName);
while (fileReader.ReadLine() != null)
line++;
System.Diagnostics.Debug.WriteLine("行数:" + line);
fileReader.Close();
}
else
{
file = File.Create(temperatureFile + '\\' + fileName);  //如果该文件不存在,就需要创建
file.Close();
}


在 .txt文件中写入数据时候,我放在了“串口数据接收事件”,serialPort_DataReceived,实验05 有提到,

private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
fileWriter.WriteLine("序号:" + ++line + "    日期:"+ DateTime.Now.ToString()+"    用户名:"+ LoginForm.userName+"    温度:"+ tempTextBox.Text + "℃");
当你写完数据的时候,也需要关闭 StreamWriter  ,否则下一次运行程序的时候,也是会出现错误,那我选择在“关闭串口”的时候,加上  fileWriter.Close(); 

既然讲到了文件操作,那我索性讲一下文件夹的操作吧。

if (!Directory.Exists(imageFile))  //判断文件夹是否存在
{
    Directory.CreateDirectory(imageFile);  //文件夹不存在,创建一个“温度折线图”文件夹
}
if (!Directory.Exists(temperatureFile))
{
    Directory.CreateDirectory(temperatureFile);    //文件夹不存在,创建一个“温度记录文件”文件夹
}


其实文件夹的操作并不难,但是我觉得有趣的是创建文件的 URL ,很多时候,我们习惯按顺序填文件的 URL,如下图



但是有一次用我的程序放在别人的电脑上用的时候,很多时候会出问题,原因是别人的电脑路径有别。突然想起了PHP老师曾经教过我们文件夹都是返回上一层这样才能正常使用的,所以我找到C#的文件退级的方法,如下。



我们运行的程序,其实是从Debug共返回了三层,





第 5 题了,啊,这次任务真多,胜利就在前方啊,同学们

5、添加子菜单和相应的窗体,可以显示保存的数据文件的内容,但不能修改。

题目意思其实就是查看刚刚保存的
.txt 文件,首先看到是只能看,不能修改,

那好办,我直接在
RichTextBox 控件中添加数据就好了,顺便改RichTextBox 的属性ReadOnly = true就好了



附上代码:

/// <summary>
/// 打开文件选择导入
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void fileButton_Click(object sender, EventArgs e)
{
OpenFileDialog file = new OpenFileDialog();
file.Filter = "文本文件(*.txt) | *.txt";  //文本文件(*.txt) | *.txt | 所有文件(*.*) | *.*
//file.ShowDialog();
//string name = file.FileName;   //返回路径名和文件名
//System.Diagnostics.Debug.Write(name);

DialogResult result = file.ShowDialog();
if (result == DialogResult.OK)
ReadTextFile(@file.FileName);

}

/// <summary>
/// 读取txt文件的数据
/// </summary>
/// <param name="fileName"></param>
private void ReadTextFile(string fileName)
{
readTextBox.Text = null;
string[] lines = File.ReadAllLines(fileName,System.Text.Encoding.Default);
foreach(string line in lines)
{
readTextBox.AppendText(line+Environment.NewLine);
}
}


其中一个文件对话框类:OpenFileDialog,确实很常用,建议大家可以找一下这方面的知识,以后的实验可能会讲到

OpenFileDialog file = new OpenFileDialog();
file.Filter = "文本文件(*.txt) | *.txt";  //文本文件(*.txt) | *.txt | 所有文件(*.*) | *.*
//file.ShowDialog();
//string name = file.FileName;   //返回路径名和文件名
//System.Diagnostics.Debug.Write(name);

DialogResult result = file.ShowDialog();
if (result == DialogResult.OK)
ReadTextFile(@file.FileName);


效果如下:





实验07完成

,如果有疑问或者在哪些地方有错误的,欢迎大家指出,共同学习,共同进步。

实验01:VS2015 对象浏览器的使用、简单调试和断点使用。

实验04:RS485串口调试、基本控件使用、CRC算法应用

实验05:RS485串口通讯,串口指令的收发应用,完成串口基础功能,github地址:实验05

实验06:窗体应用的最小化在Windows状态栏中显示托盘图标

实验07:Windows的注册表的基本使用方法,文件流保存为文本文件基础用法

实验08:利用GDI+技术生成简单验证码,类似excel的数据折线图、并保存为图片

实验09:Access数据库的创建、表创建、MD5加密,数据、图片保存在数据库的用法

实验10:将DataGridView表单数据保存为excel文件,表单的基本格式化

包含实验06~实验10的功能:github地址:实验10
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: