閱讀須知:純粹是本人啊Jun作爲初學者的筆記和個人對其中知識的複習,大神請繞道。
實驗01:VS2015 對象瀏覽器的使用、簡單調試和斷點使用。
實驗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、添加子菜單和相應的窗體,可進行用戶密碼的設置和修改,可刪除/添加用戶,用戶和密碼信息保存在註冊表中;
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);
}
}
}
點擊‘-’進行刪除用戶操作,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; //靜態變量,表示記錄數
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();
}
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); //文件夾不存在,創建一個“溫度記錄文件”文件夾
}
但是有一次用我的程序放在別人的電腦上用的時候,很多時候會出問題,原因是別人的電腦路徑有別。突然想起了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 對象瀏覽器的使用、簡單調試和斷點使用。
實驗05:RS485串口通訊,串口指令的收發應用,完成串口基礎功能,github地址:實驗05
實驗06:窗體應用的最小化在Windows狀態欄中顯示托盤圖標
實驗07:Windows的註冊表的基本使用方法,文件流保存爲文本文件基礎用法
實驗08:利用GDI+技術生成簡單驗證碼,類似excel的數據折線圖、並保存爲圖片
實驗09:Access數據庫的創建、表創建、MD5加密,數據、圖片保存在數據庫的用法
實驗10:將DataGridView表單數據保存爲excel文件,表單的基本格式化
包含實驗06~實驗10的功能:github地址:實驗10