.NET編程技術——實驗07:Windows的註冊表的基本使用方法,文件流保存爲文本文件基礎用法

閱讀須知:純粹是本人啊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、添加子菜單和相應的窗體,可進行用戶密碼的設置和修改,可刪除/添加用戶,用戶和密碼信息保存在註冊表中;

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完成奮鬥,如果有疑問或者在哪些地方有錯誤的,歡迎大家指出,共同學習,共同進步。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章