閱讀須知:純粹是本人啊Jun作爲初學者的筆記和個人對其中知識的複習,大神請繞道。
實驗01:VS2015 對象瀏覽器的使用、簡單調試和斷點使用。
實驗05:RS485串口通訊,串口指令的收發應用,完成串口基礎功能,github地址:實驗05
實驗06:窗體應用的最小化在Windows狀態欄中顯示托盤圖標
實驗07:Windows的註冊表的基本使用方法,文件流保存爲文本文件基礎用法
實驗08:利用GDI+技術生成簡單驗證碼,類似excel的數據折線圖、並保存爲圖片
實驗09:Access數據庫的創建、表創建、MD5加密,數據、圖片保存在數據庫的用法
實驗10:將DataGridView表單數據保存爲excel文件,表單的基本格式化
包含實驗06~實驗10的功能:github地址:實驗10
實驗09題目如下:
2、通過Access創建出相應MDB數據庫,根據數據庫關係圖,建好數據表;
3、在系統中建立全局性的靜態的數據庫鏈接對象,在系統的登錄界面中,可以通過數據庫進行用戶名和密碼的驗證,確保只有合法用戶才能登錄,密碼要求用MD5進行加密;
4、系統中添加註銷功能,通過註銷動作,能夠清除用戶信息,能夠關閉數據庫鏈接,但無需關閉主界面。可以重複“登錄”-“註銷”這兩個動作;
5、在收發溫度中,能實時把溫度數據存儲到數據庫中;
6、在保存曲線圖時,可以選擇把曲線圖片直接保存到數據庫中。
在期末驗收的時候,我看到很多同學,都是先創建好access數據庫,並且在數據庫裏面建好表以後,在運行程序。
因爲強迫症的原因,堅決要我的程序可以自動創建數據庫和自動創建表結構,所以在這方面可是花了比較多的時間,不過當可以實現我的想法的時候,就不會覺得這些時間是被浪費掉了。
首先需要引用微軟的ADOX.dll。什麼是ADOX呢,百度給出解釋(Microsoft ActiveX Data ObjectsExtensions for Data Definition Language and Security)是對ADO對象和編程模型的擴展。他可用於創建,修改和刪除對象。
在找資料的同時,我發現好像普通的sql語句是不能創建一個數據庫的,唯有用了ADOX纔可以動態地創建數據庫。例如動態創建mdb文件。其實實現起來也不難,但是如果沒有了這一環節,我覺得會給用戶帶來很多的不方便。
using ADOX;
private ADOX.Catalog catalog = new Catalog();
public Access(string filePath) //Access構造函數
{
this.filePath = filePath;
this.connection = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + filePath; //新建數據庫的路徑
}
/// <summary>
/// 創建數據庫
/// </summary>
public void creatDataDB()
{
if (!File.Exists(filePath)) //如果數據庫不存在,那就添加數據庫
{
try
{
catalog.Create(connection); // ADOX.Catalog 對象,可以新建一個數據庫
}
catch (Exception ex)
{
Console.WriteLine("error:" + ex);
}
}
}
因爲我的程序都是LoginForm(登錄界面)開始的,所以Access數據庫對象,我用瞭如下的全局變量,
public static Access access; //Access數據庫對象
public static string accessFileRoad = @"..\..\..\\Jun.mdb"; //數據庫路徑
LoginForm.access.creatDataDB(); //創建數據庫
LoginForm.access.createUserTable("管理員"); //在數據庫創建管理員和一般用戶
LoginForm.access.createUserTable("一般用戶");
下面就是老師教的程序的單一入口了,我的主窗體(MainForm)纔是真正的主程序,在初始化MainForm_Load事件之前,先定義一個LoginForm(登錄界面)對象,便進入了登錄界面。
/// <summary>
/// 主程序的單一入口
/// </summary>
public MainForm()
{
LoginForm loginForm = new LoginForm(); //先定義一個LoginForm 窗體對象
DialogResult result = loginForm.ShowDialog(); //窗體對象的模式對話結果,
if (result == DialogResult.Cancel) //如果結果爲Cancel,就退出整個程序
{
Environment.Exit(0);
}
else if (result == DialogResult.Yes) //如果結果爲Yes,就初始化 MainForm_Load
{
InitializeComponent();
}
}
進入登錄界面後,這是管理員的登錄,我這裏將註冊表和數據庫的信息都匹配了
if (adminRegistry.isRegistryValueNameExist(userName, ref password) && accessPassword != null) //先判斷該管理員是否在數據庫和註冊表內
{
if (string.Equals(passwordTextBox.Text.Trim(), password) && string.Equals(md5.MD5Encryption(passwordTextBox.Text.Trim()), accessPassword)) //密碼匹配正確,先將輸入框密碼MD5加密,再和數據庫的加密後的密碼比較
{
if (captchaText == recaptcha) //驗證碼匹配正確
{
this.DialogResult = DialogResult.Yes; //返回ok ,進入主窗體
}
else
MessageBox.Show("驗證碼輸入錯誤!");
}
else
MessageBox.Show("密碼錯誤!");
}
else
MessageBox.Show("該管理員不存在!");
this.DialogResult = DialogResult.Yes; //返回ok ,進入主窗體
下面就是ADOX的對象,但是我只創建了Catalog這個對象,Catalog(目錄),其他的對象基本都是創建表
和索引之類的,但是這些操作,我就更加喜歡用sql語句操作就好了,所以爲了動態創建一個mdb文件,
就大費周章調用一個引用,不知道是好是壞。
動態建完數據庫後,就可以運用sql語句進行建表和插入數據了,首先是引用OLEDB.dill這個玩意,
oledb鏈接SQL SERVER:
connection ="PROVIDER=SQLOLEDB; Data Source=" + filePath;
oledb鏈接Access:
connection = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" + filePath;
全局性的靜態的數據庫鏈接對象:OleDbConnection cn= new OleDbConnection(connection);
下面就是創建表的結構
//sql語句,
string queryString = "create table " + tableName + " (id AUTOINCREMENT primary key, 用戶名 varchar(50) NOT NULL, 密碼 varchar(50) NOT NULL)";
OleDbConnection cn = new OleDbConnection(connection);
cn.Open();
OleDbCommand cmd = new OleDbCommand(queryString, cn);
int i = cmd.ExecuteNonQuery();
Console.WriteLine("i: " + i);
cmd.Dispose();
cn.Close();
cn.Dispose();
我建了五個表,如下。其中“管理員”和“一般用戶”就是儲存管理員和一般用戶的信息,其中密碼已經經過了MD5加密算法進行了加密。
我記得老師說,MD5加密現在目前爲止是普通加密中最爲安全的,之前沒瞭解到,用了RSA加密,但是RSA加密
需要容器的,有加密也有解密,但是MD5是隻有加密,不能解密;意思就是你輸入你的密碼後,要匹配數據庫裏面
的加密後的密碼,也是需要MD5加密後才能匹配,所以一般就算在匹配途中,別人竊取了你的密碼都是加密後的
密文,不會出現明文,所以是比較安全。之前我不相信,在百度找MD5解密的工具:MD5解密 ,老師說,這個只
不過是記錄了24萬億條數據,並不是什麼解密算法,你只需要設置你的密碼爲8位以上,他就解不開了。
感覺非常有道理。
MD5加密也比較簡單,代碼:
using System.Security.Cryptography; //MD5需要的引用
class Md5
{
private MD5 md5;
public Md5()
{
md5 = new MD5CryptoServiceProvider();
}
/// <summary>
/// MD5不可逆的加密
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public string MD5Encryption(string input)
{
string output = null;
byte[] inputByte = Encoding.Default.GetBytes(input);
byte[] outputByte = md5.ComputeHash(inputByte);
for(int i=0;i<outputByte.Length;i++)
{
output += outputByte[i].ToString("X"); //轉換爲16進制數
}
// output = Encoding.Default.GetString(outputByte);
return output;
}
}
4、系統中添加註銷功能,通過註銷動作,能夠清除用戶信息,能夠關閉數據庫鏈接,但無需關閉主界面。可以重複“登錄”-“註銷”這兩個動作;
這裏註銷的功能,我想了好久,最後無能爲力,只能重新啓動程序好了
Application.Restart();
5、在收發溫度中,能實時把溫度數據存儲到數據庫中;
其實我覺得,會了Access操作,這些都是同一樣東西來的,數據庫的增刪改查,
但是我寫的Access類就不算很好的一個類。很多重複的操作,很是尷尬。
LoginForm.access.insertTemperature("溫度表", LoginForm.userName, DateTime.Now.ToString(), tempTextBox.Text + "℃");
溫度表就是儲存用戶獲取的溫度和獲取時間
6、在保存曲線圖時,可以選擇把曲線圖片直接保存到數據庫中。
數據庫插入圖片代碼:(由於存進去的是長二進制類型,其實就是byte數組)其實這種插入語句我不太會用,但是從語法看起來就是在這些字段中Add一個變量進去數據庫。
cmd.Parameters.Add("@type",OleDbType.VarChar).Value = userType;
代碼:
/// <summary>
/// 插入圖片
/// </summary>
/// <param name="tableName"></param>
/// <param name="fileName"></param>
public void insertPicutre(string tableName, string userType, string userName, string pictureName, string fileName)
{
try
{
FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
BinaryReader binaryReader = new BinaryReader(fs);
byte[] data = binaryReader.ReadBytes((int)fs.Length);
Console.WriteLine("輸入圖片信息:" + Encoding.Default.GetString(data));
//string queryString = "insert into " + tableName + " (圖片名, 圖片) values( '" + pictureName + "', '" + data + "')"; //原來的語句,插入圖片不完整
string queryString1 = "insert into " + tableName + " (用戶類型, 用戶名, 圖片名, 圖片) values (@type, @name, @pic, @data)"; //改良後的語句,插入圖片完整
OleDbConnection cn = new OleDbConnection(connection);
cn.Open();
OleDbCommand cmd = new OleDbCommand(queryString1, cn);
cmd.Parameters.Add("@type",OleDbType.VarChar).Value = userType; // 另一種插入方法
cmd.Parameters.Add("@name", OleDbType.VarChar).Value = userName;
cmd.Parameters.Add("@pic", OleDbType.VarChar).Value = pictureName;
cmd.Parameters.Add("@data", OleDbType.Binary).Value = data;
cmd.ExecuteNonQuery();
cmd.Dispose();
cn.Close();
cn.Dispose();
}
catch (OleDbException ex)
{
Console.WriteLine("error from insertPicture: " + ex);
}
}
既然能插入圖片了,那麼查詢圖片就簡單了,這裏查找的sql語句就好理解一點。由於這次的實驗沒有給出返回圖片
的功能,但是期末考覈我就添加上了,到時我會整理在Github上:https://github.com/JunStitch
查詢圖片代碼:(返回一個byte數組就ok啦)
/// <summary>
/// get 一張 圖片
/// </summary>
/// <param name="tableName"></param>
/// <param name="pictureName"></param>
public byte[] getPicture(string tableName, string userType, string userName, string pictureName)
{
byte[] pictureByte = null;
try
{
//string queryString = "select 圖片 from " + tableName + " where ";
string queryString = "select 圖片 from " + tableName + " where 用戶類型 = '" + userType + "' And 用戶名 = '" + userName + "' And 圖片名 = '" + pictureName+"' ";
OleDbConnection cn = new OleDbConnection(connection);
cn.Open();
OleDbCommand cmd = new OleDbCommand(queryString, cn);
pictureByte = (byte[])cmd.ExecuteScalar();
Console.WriteLine("輸出圖片信息:" + Encoding.Default.GetString(pictureByte));
cmd.Dispose();
cn.Close();
cn.Dispose();
}
catch (OleDbException ex)
{
Console.WriteLine("error from getPicture: " + ex);
}
return pictureByte;
}
還有就是,只有管理員纔可以進行串口的設置,一般用戶不能進行串口設置,只能獲取溫度。
按照我的理解,由於我電腦串口名稱是COM3,除了溫度計這個串口,還有其他的串口,所以爲了區分串口名,在註銷之前,我就會將串口的所有設置保存在了“串口配置表”裏,如下圖。
/// <summary>
/// “註銷”按鈕 , 當“管理員” 註銷時,其設置的串口配置將會被保存
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
///
private void logoutButton_Click(object sender, EventArgs e)
{
if (LoginForm.userType == "管理員")
{
config[0] = portComboBox.Text;
config[1] = baudRateComboBox.Text;
config[2] = dataBitComboBox.Text;
config[3] = stopBitComboBox.Text;
if (hexReceRadioButton.Checked) //如果接收類型爲16進制
config[4] = "HEX";
else
config[5] = "ASCII";
if (hexSendRadioButton.Checked) //如果發送類型爲16進制
config[5] = "HEX";
else
config[5] = "ASCII";
LoginForm.access.updateConfig("串口配置表", config);
}
Application.Restart();
}
下面附上用戶信息的Access數據庫的增刪改查代碼吧:
/// <summary>
/// 創建管理員和普通用戶用戶表
/// </summary>
/// <param name="tableName"></param>
public void createUserTable(string tableName)
{
if (File.Exists(filePath))//如果數據庫存在
{
try
{
//sql語句,
string queryString = "create table " + tableName + " (id AUTOINCREMENT primary key, 用戶名 varchar(50) NOT NULL, 密碼 varchar(50) NOT NULL)";
OleDbConnection cn = new OleDbConnection(connection);
cn.Open();
OleDbCommand cmd = new OleDbCommand(queryString, cn);
int i = cmd.ExecuteNonQuery();
Console.WriteLine("i: " + i);
cmd.Dispose();
cn.Close();
cn.Dispose();
}
catch (OleDbException ex)
{
Console.WriteLine("error: " + ex.Message);
}
}
else
{
Console.WriteLine("數據庫不存在,不能創建表");
// MessageBox.Show("數據庫不存在,不能創建表");
}
}
/// <summary>
/// 插入用戶或者管理員,插入成功返回true
/// </summary>
/// <param name="tableName"></param>
/// <param name="fileName"></param>
public bool insertUser(string tableName, string name, string password)
{
bool flag = false;
try
{
if (searchUser(tableName, name) == null)
{
string queryString = "insert into " + tableName + " (用戶名,密碼) values( '" + name + "' , '" + password + "')";
//string queryString = "insert into " + tableName + " (用戶名,密碼) values('', '" + name + "' , '" + password + "')";
//string queryString = "insert into " + tableName + " values('1','admin','admin')";
OleDbConnection cn = new OleDbConnection(connection);
cn.Open();
OleDbCommand cmd = new OleDbCommand(queryString, cn);
cmd.ExecuteNonQuery();
cmd.Dispose();
cn.Close();
cn.Dispose();
flag = true;
}
else
{
Console.WriteLine("用戶名已存在!");
//MessageBox.Show("用戶名已存在!");
}
}
catch (OleDbException ex)
{
Console.WriteLine("error from insertUser: " + ex);
}
return flag;
}
/// <summary>
/// 查找用戶,並且返回密碼
/// </summary>
/// <param name="tableName"></param>
/// <param name="name"></param>
public string searchUser(string tableName, string name)
{
string password = null;
if (File.Exists(filePath))//如果數據庫存在
{
try
{
bool flag = false;
string queryString = "select 用戶名, 密碼 from " + tableName + " where 用戶名 = " + " '" + name + "' ";
OleDbConnection cn = new OleDbConnection(connection);
cn.Open();
OleDbDataAdapter adapter;
adapter = new OleDbDataAdapter(queryString, cn);
DataSet set = new DataSet();
adapter.Fill(set);
foreach (DataRow row in set.Tables[0].Rows)
{
foreach (DataColumn col in set.Tables[0].Columns)
{
Console.Write(row[col].ToString() + '\t');
if (name == row["用戶名"].ToString()) //如果查找到相同的用戶名,則返回密碼,且退出循環
{
flag = true;
password = row["密碼"].ToString();
break;
}
}
Console.WriteLine();
}
if (!flag)
{
Console.WriteLine("沒有此用戶!");
//MessageBox.Show("沒有此用戶!");
}
}
catch (OleDbException ex)
{
Console.WriteLine("error from searchUser: " + ex);
}
}
else
{
Console.WriteLine("數據庫不存在,不能查詢表");
// MessageBox.Show("數據庫不存在,不能創建表");
}
return password;
}
/// <summary>
/// 用戶修改密碼
/// </summary>
/// <param name="tableName"></param>
/// <param name="config"></param>
public bool updatePassword(string tableName, string name, string oldPWD, string newPWD)
{
bool flag = false;
try
{
string password = searchUser(tableName, name);
if (password != null)
{
if (password == oldPWD) //輸入驗證的沒有 md5 加密
{
OleDbConnection cn = new OleDbConnection(connection);
cn.Open();
string str = "update " + tableName + " set 密碼 = '" + newPWD + "' where 用戶名 = '" + name + "' ";
OleDbCommand cmd = new OleDbCommand(str, cn);
cmd.ExecuteNonQuery();
cmd.Dispose();
cn.Close();
cn.Dispose();
flag = true;
}
else
{
Console.WriteLine("密碼錯誤!");
//MessageBox.Show("密碼錯誤!");
}
}
else
{
Console.WriteLine("不存在此用戶!");
// MessageBox.Show("不存在此用戶!");
}
}
catch (OleDbException ex)
{
Console.WriteLine("error from updateConfig : " + ex);
}
return flag;
}
/// <summary>
/// 刪除普通用戶
/// </summary>
/// <param name="tableName"></param>
/// <param name="name"></param>
/// <returns></returns>
public bool deleteUser(string tableName, string name)
{
bool flag = false;
try
{
if (searchUser(tableName, name) != null)
{
string quertString = "delete * from " + tableName + " where 用戶名 = '" + name + "' ";
OleDbConnection cn = new OleDbConnection(connection);
cn.Open();
OleDbCommand cmd = new OleDbCommand(quertString, cn);
cmd.ExecuteNonQuery();
cmd.Dispose();
cn.Close();
cn.Dispose();
flag = true;
}
else
{
Console.WriteLine("此用戶不存在!");
// MessageBox.Show("不存在此用戶!");
}
}
catch (OleDbException ex)
{
Console.WriteLine("error from updateConfig : " + ex);
}
return flag;
}
實驗09完成,如果有疑問或者在哪些地方有錯誤的,歡迎大家指出,共同學習,共同進步。
實驗01:VS2015 對象瀏覽器的使用、簡單調試和斷點使用。
實驗05:RS485串口通訊,串口指令的收發應用,完成串口基礎功能,github地址:實驗05
實驗06:窗體應用的最小化在Windows狀態欄中顯示托盤圖標
實驗07:Windows的註冊表的基本使用方法,文件流保存爲文本文件基礎用法
實驗08:利用GDI+技術生成簡單驗證碼,類似excel的數據折線圖、並保存爲圖片
實驗09:Access數據庫的創建、表創建、MD5加密,數據、圖片保存在數據庫的用法
實驗10:將DataGridView表單數據保存爲excel文件,表單的基本格式化
包含實驗06~實驗10的功能:github地址:實驗10