1.mysql数据库安装
(1)安装及环境配置参考链接: MySQL数据库的安装与配置.
(2)安装参考链接:Windows环境安装mysql8.0.
此处最好按照第一个连接步骤来。
安装的方式有所不同,因本人的机器无法直接使用mysql的程序自动安装,故采用免安装方式即第2个连接。但此链接没有环境变量配置的步骤,故可参考第一个连接。
2.解决navicat连接mysql的密码错误问题
3.创建数据库、用户表、插入数据
(1)进入mysql数据库
此处能直接在cmd进入数据库,是前面环境变量配置好了的缘故,若未配置好,则无法进入。
(2) 创建数据库dnn_learning,用户表users,及插入用户数据到用户表
注意: 此处的密码,使用了MD5加密,故在数据库中不会明文显示
mysql> create database dnn_learning;
mysql> use dnn_learning;
mysql> create table users(
id int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
username varchar(64) NOT NULL UNIQUE,
password varchar(32) NOT NULL
);
mysql> insert into users values(1, 'admin', md5('admin'));
(3)创建好的数据库
4.VS2017属性配置
(1)首先将数据库的环境变量配置好,在前面已经配置好了,这里就不用配置了
(2)配置VS的属性,调用mysql库的功能,以此来连接数据库获取数据。
第一步:打开mysql的安装目录,默认安装目录如下:C:\Program Files\MySQL\MySQL Server 8.0,确认 lib 目录和include 目录是否存在。
第二步:打开VS2019,新建一个空工程,控制台应用程序即可,注意:解决方案平台选择 X64
第三步:右击工程名,打开属性页
第四步:打开VC++目录,在包含目录中,将mysql安装文件中的include文件的路径添加到这里
第五步:打开VC++目录,在库目录中将mysql文件中的lib文件路径添加进来
第六步:在属性页的链接器中,点击“输入”,将mysql安装文件夹中lib目录下的libmysql.lib文件加到“附加依赖项”中,注意,这里直接把libmysql.lib这个依赖名加进去即可,不要加路径。
第七步:把mysql安装目录里的lib\libmysql.dll复制到c:\windows\system32下
5.建立MFC应用程序
(1)新建MFC应用程序,并配置好其属性后
(2)解决文字字体及其大小问题
参考链接: MFC下改变窗口或编辑框标题字体大小的方法.
安照上面链接操作后,运行程序初始化的过程改变了字体大小,
(3)后加入的对话框如何在初始化首先显示
进入主程序的cpp文件,注意是开始建立mfc应用时就有的cpp文件,不带Dlg.cpp的cpp文件,在里面找到InitInstance()函数。
(4)如何在打开新对话框的同时关闭本对话框
//如果登录成功就打开主程序界面,同时关闭本窗口
CDialog::OnCancel(); //关闭本窗口
CDNNLearningDlg dlg;
//dlg.user = user; //将本窗口获得的用户传递给下一窗口,如果还需大量信息,可以声明万能指针
dlg.DoModal(); //打开对话框
6.所有程序代码
此处有database.h与database.cpp文件,主要用来连接数据库,对数据库的查询、添加数据等操作都在这里。
而有关界面的应用程序在LoginDlg.h及LoginDlg.cpp中。
(1)database.h
#pragma once
#include <string>
using namespace std;
#define LINE 48
#define COLUMN 48
/*在数据库中用户表的信息(对应表的信息要构建对应的结构体)
id |
username |
password |
address_id |
*/
//用户信息
typedef struct _userinfo {
int id; //用户id
string username; //用户名
string password; //密码
//int address_id; //其他表的相关id,后续可以添加,如图片的本机地址等数据表
}userinfo;
//用于从数据库中将对于的账号名称和密码对应的id和关卡id得到
bool fetch_user_info(userinfo &user);
//用于注册用户,即在数据库中插入用户表的信息,新加用户
bool insert_user(userinfo& user);
(2)database.cpp
#include "pch.h"
#include "database.h"
#include <mysql.h>
#include <stdio.h>
#define DB_NAME "dnn_learning"
#define DB_HOST "127.0.0.1"
#define DB_PORT 3306
#define DB_USER "root"
#define DB_USER_PASSWD "password"
static int debug = 1; //用于打印调试信息的,正是发布时可以改成0,就不会打印调试信息了
static bool connect_db(MYSQL &mysql); //static只对当前文件有效
/*****************************************
*功能:通过用户名和密码从数据库获取用户信息
*输入:
* user - 用户信息结构体
*返回值:
* 获取成功返回true,失败false
******************************************/
bool fetch_user_info(userinfo &user) {
//要定义一个句柄,连接数据库都要使用
MYSQL mysql;
MYSQL_RES* res; //查询结果集
MYSQL_ROW row; //记录结构体
char sql[256]; //一般定义256个就足够了
bool ret = false;
//1.连接到数据库
if (connect_db(mysql) == false) {
return false;
}
//2.根据用户名和密码获取用户信息(id,level_id)
//select id, level_id表明只查询这两个值在条件用户名和密码的情况下
snprintf(sql, 256, "select id from users where username='%s' and password=md5('%s');", user.username.c_str(), user.password.c_str()); //将查询语句放到sql中
ret = mysql_query(&mysql, sql); //执行查询mysql_query,成功返回0
if (ret) {
//printf("数据库查询出错,%s 错误原因: %s\n", sql, mysql_error(&mysql));
mysql_close(&mysql); //当出错了后要关闭连接,释放资源
return false;
}
//3.获取结果
res = mysql_store_result(&mysql); //将获取的结果放到res,获得结果集
//记录可能有多行,允许一条一条的拿出来
row = mysql_fetch_row(res); //从结果集中取行,实际row是个指针变量
if (row == NULL) { //没有查找到记录
mysql_free_result(res); //一旦取到了res,就需要释放在结束连接的时候
mysql_close(&mysql); //当出错了后要关闭连接,
return false;
}
//row保存的是字符串,在前面的select id, level_id from,说明结果中有row[0]和row[1]两个
//下面是将字符串变成整型,并将转换成的结果打印出来
user.id = atoi(row[0]);
//if (debug) printf("userid: %d\n", user.id); //打印ID
//4.关闭连接、释放资源
//释放结果集
mysql_free_result(res);
//关闭数据库
mysql_close(&mysql);
return true;
}
//用于插入用户信息(不需要像前面一样拿到数据库结果集,写入即可)
/*****************************************
*功能:注册用户
*输入:
* user - 用户信息结构体(里面包含要输入到数据库中的账号密码)
*返回值:
* 获取成功返回true,失败false
******************************************/
bool insert_user(userinfo& user) {
//要定义一个句柄,连接数据库都要使用
MYSQL mysql;
MYSQL_RES* res; //查询结果集
MYSQL_ROW row; //记录结构体
char sql[256]; //一般定义256个就足够了
bool ret = false;
//1.连接到数据库
if (connect_db(mysql) == false) {
return false;
}
//2. 直接插入用户数据,mysql语句id为主键,可以自增,不用设定插入的数据insert into users values(1, 'admin', md5('admin'));
snprintf(sql, 256, "insert into users values(NULL, '%s', md5('%s'));", user.username.c_str(), user.password.c_str());
ret = mysql_query(&mysql, sql);
//当出错了,可以使用此排除错误
if (ret) {
printf("数据库查询出错,%s 错误原因: %s\n", sql, mysql_error(&mysql));
mysql_close(&mysql); //当出错了后要关闭连接,释放资源
return false;
}
//4.关闭连接、无需释放结果集(此处不是查询结果,而是写入数据)
//关闭数据库
mysql_close(&mysql);
return true;
}
//1.连接到数据库
bool connect_db(MYSQL &mysql) {
//1.初始化数据库句柄
mysql_init(&mysql);
//2.设置字符编码
//因为右击控制台,属性gbk编码,需要显示到控制台故需要gbk编码,别的编码可能
mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, "gbk");//(给句柄设置字符编码),在windows上基本gbk字符集
//3.连接到数据库
//通过网络连接服务器并通信,此函数接口mysql_real_connect
if (mysql_real_connect(&mysql, DB_HOST, DB_USER, DB_USER_PASSWD, DB_NAME, DB_PORT, NULL, 0) == NULL) {
printf("数据库连接出错, 错误原因: %s\n", mysql_error(&mysql));
return false;
}
return true;
}
(3)LoginDlg.h
#pragma once
#include "database.h"
// LoginDlg 对话框
class LoginDlg : public CDialogEx
{
DECLARE_DYNAMIC(LoginDlg)
public:
LoginDlg(CWnd* pParent = nullptr); // 标准构造函数
virtual ~LoginDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_LOGIN_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnBnClickedBtnRegister();
afx_msg void OnBnClickedBtnLogin();
// 登录界面的账号
CEdit m_userName;
// 登录界面的密码
CEdit m_passWord;
userinfo user;
virtual BOOL OnInitDialog();
//用来专门改变字体大小的类CFont,与static配合使用
static CFont one1;
// //标题名称
CStatic m_staticT1;
};
(4)LoginDlg.cpp
// LoginDlg.cpp: 实现文件
//
#include "pch.h"
#include "DNN_Learning.h"
#include "LoginDlg.h"
#include "afxdialogex.h"
#include "DNN_LearningDlg.h"
CFont LoginDlg::one1;
// LoginDlg 对话框
IMPLEMENT_DYNAMIC(LoginDlg, CDialogEx)
LoginDlg::LoginDlg(CWnd* pParent /*=nullptr*/)
: CDialogEx(IDD_LOGIN_DIALOG, pParent)
{
}
LoginDlg::~LoginDlg()
{
}
void LoginDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDIT_UserName, m_userName);
DDX_Control(pDX, IDC_EDIT_PassWord, m_passWord);
DDX_Control(pDX, IDC_STATIC_T1, m_staticT1);
}
BEGIN_MESSAGE_MAP(LoginDlg, CDialogEx)
ON_BN_CLICKED(IDC_BTN_Register, &LoginDlg::OnBnClickedBtnRegister)
ON_BN_CLICKED(IDC_BTN_Login, &LoginDlg::OnBnClickedBtnLogin)
END_MESSAGE_MAP()
// LoginDlg 消息处理程序
BOOL LoginDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// TODO: 在此添加额外的初始化
//设置static字体类型及大小
one1.CreatePointFont(200, _T("宋体")); //字体和大小根据自己的需要自行改变
m_staticT1.SetFont(&one1); //将字体和想要改变的框体变量进行关联
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
//注册
void LoginDlg::OnBnClickedBtnRegister()
{
// TODO: 在此添加控件通知处理程序代码
CString userName, passWord;
m_userName.GetWindowTextW(userName);
m_passWord.GetWindowTextW(passWord);
//判断输入是否为空
if (userName.IsEmpty() || passWord.IsEmpty()) {
AfxMessageBox(_T("账号或者密码为空,请输入!"));
return;
}
//判断账号密码是否已经存在,若存在,无需再次注册
//将CString转换成string类型,后续判断时会使用
//userinfo user; //窗口类变量,表名当前用户的身份
user.username = CW2A(userName.GetString());
user.password = CW2A(passWord.GetString());
//调用database.h中的函数判断,用户密码是否匹配,为真,证明登录成功
bool ret = false;
ret = fetch_user_info(user);
if (ret == true) {
AfxMessageBox(_T("账号及密码已经存在,无需再次注册!"));
return;
}
//进行用户的注册操作
insert_user(user);
}
//登录
void LoginDlg::OnBnClickedBtnLogin()
{
// TODO: 在此添加控件通知处理程序代码
CString userName,passWord;
m_userName.GetWindowTextW(userName);
m_passWord.GetWindowTextW(passWord);
//判断输入是否为空
if (userName.IsEmpty() || passWord.IsEmpty()) {
AfxMessageBox(_T("账号或者密码为空,请输入!"));
return;
}
//判断账号密码是否正确
//将CString转换成string类型,后续判断时会使用
//userinfo user; //窗口类变量,表名当前用户的身份
user.username = CW2A(userName.GetString());
user.password = CW2A(passWord.GetString());
//调用database.h中的函数判断,用户密码是否匹配,为真,证明登录成功
bool ret = false;
ret = fetch_user_info(user);
if (ret == false) {
//当返回是可以给个重试的机会
AfxMessageBox(_T("账号或者密码错误,请重新输入或者再次注册!"));
return;
}
//如果登录成功就打开主程序界面,同时关闭本窗口
CDialog::OnCancel(); //关闭本窗口
CDNNLearningDlg dlg;
//dlg.user = user; //将本窗口获得的用户传递给下一窗口,如果还需大量信息,可以声明万能指针
dlg.DoModal(); //打开新的窗口
}