封装一个sqlite3动态库

作者:小蜗牛向前冲

名言:我可以接受失败,但我不能接受放弃

  如果觉的博主的文章还不错的话,还请点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正

目录

一、项目案例

二、sqlite3数据库

2.1、sqlite3数据库的介绍

2.2、sqlite3数据库的常用函数

2.2.1、基本函数

 2.2.2、sqlite3_exec于sqlite3_prepare_v2 + sqlite3_step的区别在哪里

2.3、sqlite3在MFC中的使用

三、动态库的制作过程

3.1、创建一个动态库的项目

 3.2、生成三件套文件

3,3、使用生成的动态库

四、项目实现细节

4.1、封装sqlite3动态库

 4.2、实现项目界面各类点击操作


一、项目案例

功能要求用 sqlite3 数据库,
实现一个学生信息管理系统。
1.  添加学生、删除学生
2.  按条件进行升序降序 查询数据库中的学生信息
3.  封装成一个类来实现

效果演示

为了完成这个功能,首先我要在本地下载好sqlite3数据库。 会有如下文件:

在include中包含:

 在lib中包含(x64平台):

 其次,就是写一个DataBases的动态库,主要包含了对sqilit3:打开数据库,建表,插入,查询,删除接口的封装。写完后,我就要进行编译,成功我们需要动态库文件。这里最为关键的是.dll文件(动态链接库)。这里我们将我们需要的文件,重新用一个文件包含起来DATABASE文件夹。

在include中包含:

  在lib中包含(x64平台):

  • dDataBases.dll
    • 这是一个 动态链接库(DLL) 文件,包含了程序的可执行代码和函数,其他程序可以通过调用它提供的接口来使用其中的功能。
  • dDataBases.exp
    • 这是一个 导出文件(Exports File),包含了 DLL 中导出的函数和符号信息,通常是链接过程中的一部分。它帮助链接器在构建时找到 DLL 中的符号。
  • dDataBases.lib
    • 这是一个 静态库(Object File Library) 文件,它包含了程序或 DLL 的目标代码(即编译后的代码)。该文件通常在构建 DLL 时生成,它可以被其他程序链接到以调用 DLL 中的函数。
  • dDataBases.pdb
    • 这是一个 程序调试数据库(Program Debug Database) 文件,包含了调试信息,如源代码行号、变量名、函数名称等。它帮助调试器在运行时进行符号解析,以便进行调试。

最后,我们就是要在MFM,基于对话框,去实现界面的各种逻辑 。

为了完成这个项目,我们需要知道下面的知识点。

二、sqlite3数据库

2.1、sqlite3数据库的介绍

SQLite 是一个轻量级的关系型数据库管理系统(RDBMS),与其他常见的数据库(如 MySQL 或 PostgreSQL)不同,SQLite 不需要一个独立的服务器进程来运行,它直接将数据库文件嵌入到应用程序中,因此特别适合资源受限的环境。

我们可以理解为sqlite3就是一个库(文件),我们要引用他,只要包含他的头文件就好。

2.2、sqlite3数据库的常用函数

2.2.1、基本函数

1)sqlite3_open打开(或创建)数据库文件。

int sqlite3_open(const char* filename, sqlite3** db);
  • filename:数据库文件的路径。
  • db:指向 sqlite3* 类型的指针,用于返回数据库连接对象。

 2)sqlite3_close关闭数据库连接。

int sqlite3_close(sqlite3* db);

 3)sqlite3_exec执行一条 SQL 语句(可以是查询、插入、更新等)。

int sqlite3_exec(sqlite3* db, const char* sql, int (*callback)(void*, int, char**, char**), void* data, char** errMsg);
  • db:数据库连接对象。
  • sql:要执行的 SQL 语句。
  • callback:处理查询结果的回调函数(如果是查询操作时使用)。
  • data:回调函数的自定义参数。
  • errMsg:如果有错误,返回错误消息。

4)sqlite3_prepare_v2准备执行 SQL 语句(用于查询)

int sqlite3_prepare_v2(sqlite3* db, const char* sql, int nByte, sqlite3_stmt** stmt, const char** tail);
  • b:数据库连接对象。
  • sql:SQL 语句。
  • nByte:SQL 语句的最大字节数(-1 表示字符串的长度)。
  • stmt:返回的 sqlite3_stmt* 指针,用于后续执行查询。
  • tail:SQL 语句中的尾部部分(通常为 NULL)。

5)sqlite3_step执行 SQL 语句(例如查询)。

int sqlite3_step(sqlite3_stmt* stmt);
  • 查询操作

    • sqlite3_step 在查询操作中用于逐行获取结果集。当查询成功时,sqlite3_step 会返回 SQLITE_ROW,表示当前有数据可以获取。
    • 当查询结束时(即没有更多的数据行时),它返回 SQLITE_DONE,表示查询完成。
  • 非查询操作

    • 对于插入、更新、删除等 SQL 操作,sqlite3_step 会在第一次执行时完成操作,返回 SQLITE_DONE,表示操作已完成。

6) sqlite3_column_int获取查询结果中某一列的整数值。

int sqlite3_column_int(sqlite3_stmt* stmt, int col);
  • stmt:查询语句对象。
  • col:列的索引(从 0 开始)。

7) sqlite3_column_text获取查询结果中某一列的文本值。

const char* sqlite3_column_text(sqlite3_stmt* stmt, int col);

8)sqlite3_finalize 释放准备好的 SQL 语句对象。

int sqlite3_finalize(sqlite3_stmt* stmt);
  • stmt:要释放的 sqlite3_stmt 对象。 

9)sqlite3_errmsg获取数据库连接的错误信息。

const char* sqlite3_errmsg(sqlite3* db);

 10)sqlite3_changes获取上次操作影响的行数(适用于插入、删除、更新操作)。

int sqlite3_changes(sqlite3* db);

11)sqlite3_busy_timeout设置数据库的忙等待超时时间(如果数据库被锁定时,设置重试时间)。

int sqlite3_busy_timeout(sqlite3* db, int milliseconds);
  • db:数据库连接对象。
  • milliseconds:超时时间(毫秒)。

 2.2.2、sqlite3_exec于sqlite3_prepare_v2 + sqlite3_step的区别在哪里

sqlite3_execsqlite3_prepare_v2 sqlite3_step 都是 SQLite 中用于执行 SQL 语句的函数。

对于sqlite3_exec他马上执行一条SQL语句,运用在简单场景中。

sqlite3_prepare_v2sqlite3_step 是一种更细粒度的执行方式,通常用于处理复杂的 SQL 查询和需要逐步执行的场景。

sqlite3_prepare_v2他是将SQL语句准换为sqlite3_stmt语句对象。sqlite3_step就是执行sqlite3_prepare_v2准备好的SQL语句。

2.3、sqlite3在MFC中的使用

这里首先我们要先下载好sqlite3得到他的sqlite3.dll动态库文件和sqlite3.h头文件。

我们将需要的四个文件放到sq3目录下

 

其次。在 MFC 项目中配置 SQLite3 

 1)点击项目属性->C/C++->常规->附加包含目录,进行配置

2) 在打开的属性页中选择 链接器 -> 常规 -> 附加库目录 -> 编辑

在打开的属性页中选择 链接器 -> 输入 -> 附加依赖项 -> 编辑 ,输入“sqlite3.lib”,点击确定。

这样就完成配置了 ,这里我们包含一下sqlite3.h看报不报错误,没有报错就是配置成功了,注意我们这里需要相对引用找到这个头文件。

三、动态库的制作过程

3.1、创建一个动态库的项目

点击动态链接库建立项目

 项目创建完成后,会有framework和pch的.h文件和.cpp文件。

framework.h 主要用作项目的全局头文件,包含必要的MFC库和公共头文件。

pch.hpch.cpp 提供预编译头机制,加速编译过程,提高开发效率。

上面的文件都是编译器生成的,这些都是辅助文件,真正的动态库文件,要我们自己添加,如下我添加了MyDll.h和MyDll.cpp的文件。 

 

 3.2、生成三件套文件

MyDll.这个是我们动态库的头文件

一般都用定义下面的电

#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)  // 导出
#else
#define MYDLL_API __declspec(dllimport)  // 导入
#endif

 上面是我们要导入和导出动态库的基本代码。

MYDLL_API这个宏是为了管理动态库的导入和导出,当那个类或者函数要导出,就要加上这个宏。

这里我们简单导入一个类

#pragma once

#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)  // 导出
#else
#define MYDLL_API __declspec(dllimport)  // 导入
#endif

//这里表示这个类要导出
class MYDLL_API MyDll
{
public:
	int addNum(int num);
};

在.cpp文件,完成函数的编写

#include"pch.h"
#include"MyDll.h"

//每次都加10
int MyDll::addNum(int num)
{
	return 10 + num;
}

 完成这些后就可以编译程序了。

编译成功后,需要找到三件套, 打开文件所在目录。

找到第一个文件MyDll.h文件,新建立一个文件夹,用来存放这三件套。

拿到.h文件后我们再去找lib和dll ,在x64文件中。注意这个x64文件,指的是上一个目录下的的x64->Debug,我们就能拿导MyDll.dll和MyDll.lib文件

将这三个文件都放入,新建立的文件夹中 

1. MyDll.dll

这是动态链接库文件(DLL)。它包含了你编写的代码,并且可以被其他程序调用。通过导出函数,其他程序可以使用该 DLL 中提供的功能。这个文件是编译项目后生成的。

2. MyDll.h

这是头文件(Header file)。头文件包含了声明外部函数或类的接口,在其他项目中使用 DLL 时,程序会包含这个头文件来获取函数声明和类定义。通常,头文件中会使用 __declspec(dllexport)__declspec(dllimport) 来声明导出/导入的函数。

3. MyDll.lib

这是静态库文件,用于在编译时链接到目标程序。在使用 DLL 的项目中,MyDll.lib 提供了符号信息,使得链接器能够正确链接到 DLL 中的函数。它不包含代码本身,只是一个包含函数地址的引用文件,在链接时会告诉程序去加载相应的 DLL 文件。

3,3、使用生成的动态库

 这里我们创建项目后,就简单规划出一个简单界面,进行演示:

然后执行下面的操作 

(1)将动态链接库的头文件复制到MFC程序里面,可以打开MFC程序的“cpp”文件的所在位置,然后就把MyDll.h放在这 

然后在“解决方案管理器”新建一个“筛选器”,名为“自定义库” 

再往“自定义库”里面添加现有项,将刚刚的“MyDll.h”添加进来 

 (2)在MyDll.h”所在的目录新建一个文件夹“Tlib”用来放“MyDll.h.lib”

目前所作的准备,都是为了编译的时候能用上MyDll里的东西,还有需要最后一步,我们就可以将MyDll完全导入MFC程序,那就是包含头文件“MyDl.h”和导入指定路径的"MyDl.lib",在MFCDLLDlg.cpp操作 

#include "MyDll.h"//导入头文件,

#pragma comment(lib,"Tlib/MyDll.lib")//指定路径,导入静态链接库

 (3)之后就可以在界面上按钮的函数里面使用“MYDll”了,双击按钮,进入该函数

void CMFCDLLDlg::OnBnClickedButton1()
{
	// TODO: 在此添加控件通知处理程序代码
	MyDll dll;
	int ret = dll.addNum(10);
	CString str;
	str.Format(_T("%d"), ret);  // 将整数转换为字符串
	m_text.SetWindowText(str);   // 设置到控件中
}

最关键是我们这里使用了MyDll类 

这样我成功调用了,自己写的动态库 

四、项目实现细节

好了,上面我们学习完成如何在MFC中使用sqlite3和制作动态库,下面我们就可以完成我们的项目了。

4.1、封装sqlite3动态库

1)在完成封装之前我们要完成项目的配置

在VC++目录上,配置好外部包含目录,和库目录。

我们配置外部包含目录是为了让编译器知道sqlite.h头文件在哪里位置,让编译器在链接的时候找到相关的头文件。

库目录,这里一般包含的是库文件(.lib和.dll),在编译时,编译器生成的是目标文件(.obj),但最终的链接阶段需要将目标文件与静态库或动态库链接在一起,以生成可执行文件或动态链接库。通过配置库目录,链接器就能找到正确的库文件进行链接。

 在链接器的输入配置附加依赖项目,是为了指定在链接阶段需要链接的额外库文件。这对于使用外部库(例如 SQLite)非常重要,因为它告诉链接器在生成最终可执行文件时,应该包含哪些库文件,

对了,上面这些内容是你安装了sqlite3的情况下,并且有他的一下配置文件。

 下面实现DataBases.h

#pragma once


#ifdef DATABASES_EXPORTS
#define DATABASES_API __declspec(dllexport)
#else
#define DATABASES_API __declspec(dllimport)
#endif
#include <string>
#include "sqlite3.h"
#include <vector>
#include <atlstr.h>

//封装用户数据

struct DATABASES_API UserData
{
	std::string userName;
	std::string userId;
	int age;
	double grades;
};


//这个类是从dll导出的
class DATABASES_API DataBases
{
public:
	//构造函数
	DataBases();
	bool openDataBases(const char* path);
	void CreateDable();
	void InsertData(std::string& name,std::string& id,int age,double grades);
	void QueryDataAll(std::vector<UserData>& result);
	void QueryData(const std::string& key, const std::string& values, std::vector<UserData>& result);
	void DeleteData(const std::string& byid, bool byname);
private:
	std::string m_strUserName;
	std::string m_strUserId;
	int m_iAge;
	double m_dGrades;

	sqlite3* m_pDB;//指向数据库
	char* m_cErr_msg;//错误信息
};

extern DATABASES_API int nDataBases;

DATABASES_API int fnDataBases(void);

实现 DataBases.cpp文件


#include "pch.h"
#include "framework.h"
#include "DataBases.h"
#include <iostream>
#pragma comment(lib,"sqlite3.lib")


// 这是导出变量的一个示例
DATABASES_API int nDataBases = 0;

// 这是导出函数的一个示例。
DATABASES_API int fnDataBases(void)
{
	return 0;
}

//构造函数
DataBases::DataBases()
	:m_iAge(0)
	,m_dGrades(0)
	, m_cErr_msg(nullptr)
	, m_pDB(nullptr)
{}


//打开数据库,并且建立好表
bool DataBases::openDataBases(const char* path)
{
	if (m_pDB != nullptr)
	{
		//数据库已经打开
		std::cout << "open dataBase"<<std::endl;
		return true;
	}

	//1、打开数据库
	//返回SQLITE_OK(0):成功打开数据库。
	int ret = sqlite3_open(path, &m_pDB);
	if (ret != SQLITE_OK)
	{
		std::cout << "failed open dataBase" << std::endl;
		return false;
	}
	//sqlite3_exec这个函数是为了执行SQL语句
	//int sqlite3_exec(
	//  sqlite3* db,                  // 数据库连接对象
	//	const char* sql,               // 要执行的 SQL 语句
	//	int (*callback)(void*, int, char**, char**), // 回调函数(可选)
	//	void* data,                    // 回调函数的参数(可选)
	//	char** errMsg                  // 错误信息(如果发生错误)
	//	);
	
	if (sqlite3_exec(m_pDB, CREATE_TABLE, nullptr, nullptr, &m_cErr_msg) != SQLITE_OK)
	{
		std::cout << "SQL error:" << m_cErr_msg;

		//记得释放掉,m_cErr_msg,当SQL出现错误的时候,sqlite3_exec() 函数动态分配了内存来存储这些错误信息
		sqlite3_free(m_cErr_msg);
		//关闭数据库
		sqlite3_close(m_pDB);

		return false;
	}
	return true;
}

//建表
void DataBases::CreateDable()
{
	if (m_pDB==nullptr)
	{
		return;
	}

	if (sqlite3_exec(m_pDB, CREATE_TABLE, nullptr, nullptr, &m_cErr_msg) != SQLITE_OK)
	{
		std::cout << "SQL error:" << m_cErr_msg;

		//记得释放掉,m_cErr_msg,当SQL出现错误的时候,sqlite3_exec() 函数动态分配了内存来存储这些错误信息
		sqlite3_free(m_cErr_msg);
		//关闭数据库
		sqlite3_close(m_pDB);

		return;
	}
}

//插入
void DataBases::InsertData(std::string& name, std::string& id, int age, double grades)
{
	m_strUserName = name;
	m_strUserId = id;
	m_iAge = age;
	m_dGrades = grades;
	sqlite3_stmt* stmt;

	//sqlite3_prepare_v2() 是一个更底层的 API,用于将 SQL 语句编译成可执行的语句句柄(sqlite3_stmt)。
	//这个 API 适用于需要重复执行的 SQL 语句、带有占位符(?)的参数化查询
	//int sqlite3_prepare_v2(
	//	sqlite3 * db,             // 数据库连接对象
	//	const char* sql,         // 要执行的 SQL 语句
	//	int nByte,               // SQL 语句的字节数(-1表示自动计算)
	//	sqlite3_stmt * *ppStmt,   // 输出:编译后的 SQL 语句的句柄
	//	const char** pzTail      // 输出:未解析的 SQL 部分(适用于多个 SQL 语句)
	//);
	//执行插入操作
	if (sqlite3_prepare_v2(m_pDB, INSERT_DATA, -1, &stmt, nullptr) != SQLITE_OK)
	{
		std::cout << "Failed to prepare statement!" << std::endl;
		sqlite3_close(m_pDB);
		return;
	}
	//int sqlite3_bind_text(
	//sqlite3_stmt* stmt,      // 已准备好的 SQL 语句句柄
	//	int index,               // 占位符的位置(从 1 开始)
	//	const char* value,       // 要绑定的文本值
	//	int n,                   // 绑定的文本的长度(-1 表示直到遇到 NULL 字符)
	//	void (*destructor)(void*) // 指向析构函数的指针,用于释放值的内存(可选)
	//	);

	//绑定参数
	//SQLITE_STATIC:表示数据在绑定时已经是静态的,SQLite 不需要再管理它的生命周期。
	sqlite3_bind_text(stmt, 1, m_strUserName.c_str(), -1, SQLITE_STATIC);
	sqlite3_bind_text(stmt, 2, m_strUserId.c_str(), -1, SQLITE_STATIC);
	sqlite3_bind_int(stmt, 3, m_iAge);
	sqlite3_bind_double(stmt, 4, m_dGrades);

	//插入
	if (sqlite3_step(stmt) != SQLITE_DONE)
	{
		std::cout << "Execution failed!" << std::endl;
	}
	sqlite3_finalize(stmt); // 释放资源
}

//查询
void DataBases::QueryDataAll(std::vector<UserData>& result)
{
	sqlite3_stmt* query_stmt;
	if (sqlite3_prepare_v2(m_pDB, QUERY_DATA, -1, &query_stmt, nullptr) != SQLITE_OK)
	{
		std::cout << "Failed to prepare statement!" << std::endl;
		sqlite3_close(m_pDB);
		return;
	}
	//SQLITE_ROW 表示 查询结果中有一行数据
	while (sqlite3_step(query_stmt) == SQLITE_ROW)
	{
		UserData user;
		user.userName = std::string(reinterpret_cast<const char*>(sqlite3_column_text(query_stmt, 0)));
		user.userId = std::string(reinterpret_cast<const char*>(sqlite3_column_text(query_stmt, 1)));
		user.age = sqlite3_column_int(query_stmt, 2);
		user.grades = sqlite3_column_double(query_stmt,3);

		//保存结果
		result.push_back(user);
	}

	//释放查找的资源
	sqlite3_finalize(query_stmt);
}

void DataBases::QueryData(const std::string& key, const std::string& values, std::vector<UserData>& result)
{
	sqlite3_stmt* query_stmt;

	// 动态构建查询语句
	std::string sqlQuery = "select UserName, Id, Age, Grades from User_Info where ";
	if (key == "UserName")
	{
		sqlQuery += "UserName = ?";
	}
	else if (key == "Id")
	{
		sqlQuery += "Id = ?";
	}
	else
	{
		std::cout << "Invalid search criterion!" << std::endl;
		return;
	}

	// 准备查询的SQL语句
	if (sqlite3_prepare_v2(m_pDB, sqlQuery.c_str(), -1, &query_stmt, nullptr) != SQLITE_OK)
	{
		std::cout << "Failed to prepare statement!" << std::endl;
		return;  // 不再关闭数据库连接,因为数据库连接可能会在其他地方继续使用
	}

	// 绑定参数,根据 key 类型绑定不同类型的值
	if (key == "UserName")
	{
		sqlite3_bind_text(query_stmt, 1, values.c_str(), -1, SQLITE_STATIC);
	}
	else if (key == "Id")
	{
		// 假设 "Id" 是整数类型,你可以根据实际情况调整
		int id = std::stoi(values); // 将 string 转换为整数
		sqlite3_bind_int(query_stmt, 1, id);
	}

	// 查询结果
	while (sqlite3_step(query_stmt) == SQLITE_ROW)
	{
		UserData user;
		user.userName = std::string(reinterpret_cast<const char*>(sqlite3_column_text(query_stmt, 0)));
		user.userId = std::string(reinterpret_cast<const char*>(sqlite3_column_text(query_stmt, 1)));
		user.age = sqlite3_column_int(query_stmt, 2);
		user.grades = sqlite3_column_double(query_stmt, 3);

		// 保存结果
		result.push_back(user);
	}

	// 如果没有查询到任何数据,输出提示
	if (result.empty())
	{
		std::cout << "No matching records found." << std::endl;
	}

	// 释放查询语句的资源
	sqlite3_finalize(query_stmt);
}


//根据id或者name删除数据
void DataBases::DeleteData(const std::string& identifier,bool byname)
{
	sqlite3_stmt* stmt;
	const char* sql;
	if (byname)
	{
		sql = "delete from user_info where UserName = ?";
	}
	else
	{
		sql = "delete from user_info where Id = ?";
	}

	//准备SQL语句
	if (sqlite3_prepare_v2(m_pDB, sql, -1, &stmt, nullptr) != SQLITE_OK)
	{
		std::cout << "Failed to prepare delete statement!" <<std::endl;
		sqlite3_close(m_pDB);
		return;
	}

	sqlite3_bind_text(stmt, 1, identifier.c_str(), -1, SQLITE_STATIC);

	//执行删除
	if (sqlite3_step(stmt) != SQLITE_DONE)
	{
		std::cout << "Execution failed!" << std::endl;
	}
	else
	{
		std::cout << (byname ? "User " : "ID ") << identifier << " deleted successfully!" << std::endl;
	}

	// 释放资源
	sqlite3_finalize(stmt);
}



 4.2、实现项目界面各类点击操作

前面我们封装sqlite3的动态库,具体咋使用的,参考上面制作动态库的制作,哪里,进行配置,才可以使用我们封装的动态库。

下面就是在我们的界面项目中使用动态库,实现学生管理系统了。

MainDlgDlg.cpp,这里实现主窗口的逻辑 


// MainDlgDlg.cpp: 实现文件
//

#include "pch.h"
#include "framework.h"
#include "MainDlg.h"
#include "MainDlgDlg.h"
#include "afxdialogex.h"
#include <Windows.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


#include "AddData.h"
#include "QueryData.h"
#include "DelData.h"


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 支持

// 实现
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()


// CMainDlgDlg 对话框



CMainDlgDlg::CMainDlgDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_MAINDLG_DIALOG, pParent)
	,m_bRed(true)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMainDlgDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_SHOW, m_Text);
}


BEGIN_MESSAGE_MAP(CMainDlgDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_CLOSE()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON1, &CMainDlgDlg::OnBnClickedButton1)
	ON_BN_CLICKED(IDC_BUTTON2, &CMainDlgDlg::OnBnClickedButton2)
	ON_BN_CLICKED(IDC_BUTTON3, &CMainDlgDlg::OnBnClickedButton3)
	ON_BN_CLICKED(IDC_BUTTON4, &CMainDlgDlg::OnBnClickedButton4)
	ON_WM_CTLCOLOR()//这个宏帮助处理控件的 颜色设置
END_MESSAGE_MAP()


// CMainDlgDlg 消息处理程序
//定义一个全部数据库变量
DataBases* g_pDB = nullptr;
BOOL CMainDlgDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 将“关于...”菜单项添加到系统菜单中。

	// IDM_ABOUTBOX 必须在系统命令范围内。
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// 设置此对话框的图标。  当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标

	// TODO: 在此添加额外的初始化代码
	m_Text.SetWindowTextW(TEXT("未连接"));

	if (g_pDB == nullptr) {
		g_pDB = new DataBases();
	}
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

void CMainDlgDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。  对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CMainDlgDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CMainDlgDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}


//链接数据库

void CMainDlgDlg::OnBnClickedButton1()
{
	// TODO: 在此添加控件通知处理程序代码
	if (g_pDB->openDataBases("UserData.db"))
	{
		m_Text.SetWindowTextW(TEXT("已连接"));
		m_bRed = false;
		Invalidate();
		AfxMessageBox(_T("链接成功"));
	}
	else
	{
		m_Text.SetWindowTextW(TEXT("未连接"));
		m_bRed = true;
		Invalidate();
		AfxMessageBox(_T("链接失败"));
	}
}

//添加数据
void CMainDlgDlg::OnBnClickedButton2()
{
	// TODO: 在此添加控件通知处理程序代码
	if (m_bRed == true)
	{
		AfxMessageBox(_T("请先链接数据库"));
	}
	else
	{
		AddData AD;
		AD.DoModal();
	}
}

//查询数据库
void CMainDlgDlg::OnBnClickedButton3()
{
	// TODO: 在此添加控件通知处理程序代码
	if (m_bRed == true)
	{
		AfxMessageBox(_T("请先链接数据库"));
	}
	else
	{
		QueryData QD;
		QD.DoModal();
	}
}

//删除数据
void CMainDlgDlg::OnBnClickedButton4()
{
	// TODO: 在此添加控件通知处理程序代码
	if (m_bRed == true)
	{
		AfxMessageBox(_T("请先链接数据库"));
	}
	else
	{
		DelData DD;
		DD.DoModal();
	}
}
//当我们关闭窗口的时候条用,释放资源
void CMainDlgDlg::OnClose()
{
	if (g_pDB != nullptr) {
		delete g_pDB;
		g_pDB = nullptr;
	}
	CDialogEx::OnOK();
}
HBRUSH CMainDlgDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
	HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);

	// TODO:  在此更改 DC 的任何特性
	if (TRUE == m_bRed)
	{
		pDC->SetTextColor(RGB(255, 0, 0));//红色
	}
	else
	{
		pDC->SetTextColor(RGB(0, 255, 0));//绿色
	}
	// TODO:  如果默认的不是所需画笔,则返回另一个画笔
	return hbr;
}

addData.cpp,这里实现数据的添加

// AddData.cpp: 实现文件
//

#include "pch.h"
#include "MainDlg.h"
#include "AddData.h"
#include "afxdialogex.h"


// AddData 对话框

IMPLEMENT_DYNAMIC(AddData, CDialogEx)

AddData::AddData(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG1, pParent)
	, m_strName(_T(""))
	, m_strId(_T(""))
	, m_iAge(0)
	, m_dGrades(0)
{

}

AddData::~AddData()
{
}

void AddData::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT1, m_strName);
	DDX_Text(pDX, IDC_EDIT2, m_strId);
	DDX_Text(pDX, IDC_EDIT3, m_iAge);
	DDX_Text(pDX, IDC_EDIT4, m_dGrades);
}


BEGIN_MESSAGE_MAP(AddData, CDialogEx)
	ON_BN_CLICKED(IDC_BUTTON1, &AddData::OnBnClickedButton1)
	ON_BN_CLICKED(IDC_BUTTON5, &AddData::OnBnClickedButton5)
END_MESSAGE_MAP()


// AddData 消息处理程序


//添加数据
void AddData::OnBnClickedButton1()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData(true);//将控件中的数据传输到成员变量中去
	//CT2A是将 CString(宽字符字符串) 转换为 const char*(ANSI 字符串)。
	std::string strname = CT2A(m_strName.GetString(), CP_UTF8);
	std::string strid = CT2A(m_strId.GetString(), CP_UTF8);;
	g_pDB->InsertData(strname, strid, m_iAge, m_dGrades);
	UpdateData(false);//成员变量->控件
	AfxMessageBox(_T("添加成功"));
}

//取消按键
void AddData::OnBnClickedButton5()
{
	// TODO: 在此添加控件通知处理程序代码
	CDialogEx::OnCancel();
}

 DelData.cpp,这里实现数据的删除

// DelData.cpp: 实现文件
//

#include "pch.h"
#include "MainDlg.h"
#include "DelData.h"
#include "afxdialogex.h"
#include"ListCtrl.h"

// DelData 对话框

IMPLEMENT_DYNAMIC(DelData, CDialogEx)

DelData::DelData(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG3, pParent)
	, m_strName(_T(""))
	, m_strId(_T(""))
{

}

DelData::~DelData()
{
}

void DelData::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT1, m_strName);
	DDX_Text(pDX, IDC_EDIT2, m_strId);
}


BEGIN_MESSAGE_MAP(DelData, CDialogEx)
	ON_BN_CLICKED(IDC_BUTTON1, &DelData::OnBnClickedButton1)
	ON_BN_CLICKED(IDC_BUTTON4, &DelData::OnBnClickedButton4)
	ON_BN_CLICKED(IDC_BUTTON6, &DelData::OnBnClickedButton6)
	ON_BN_CLICKED(IDC_BUTTON7, &DelData::OnBnClickedButton7)
END_MESSAGE_MAP()


// DelData 消息处理程序

//按照名字删除
void DelData::OnBnClickedButton1()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData(true);
	std::string str_delname = CT2A(m_strName.GetString(), CP_UTF8);
	g_pDB->DeleteData(str_delname, true);
	AfxMessageBox(_T("删除成功"));
}

//按照ID删除
void DelData::OnBnClickedButton4()
{
	// TODO: 在此添加控件通知处理程序代码
	UpdateData(true);
	std::string str_delId = CT2A(m_strId.GetString(), CP_UTF8);
	g_pDB->DeleteData(str_delId, true);
	AfxMessageBox(_T("删除成功"));
}


void DelData::OnBnClickedButton6()
{
	// TODO: 在此添加控件通知处理程序代码
	std::vector<UserData> result;
	UpdateData(true);
	g_pDB->QueryDataAll(result);

	ListCtrl ListCtrl(result); // 将查询结果传递给目标对话框
	ListCtrl.DoModal();
}


void DelData::OnBnClickedButton7()
{
	// TODO: 在此添加控件通知处理程序代码
	CDialogEx::OnCancel();
}

ListCtrl.cpp,这里实现list Control控件,制作数据库的查询结果显示界面

// ListCtrl.cpp: 实现文件
//

#include "pch.h"
#include "MainDlg.h"
#include "ListCtrl.h"
#include "afxdialogex.h"
#include <algorithm>

// ListCtrl 对话框

IMPLEMENT_DYNAMIC(ListCtrl, CDialogEx)

ListCtrl::ListCtrl(const std::vector<UserData>& data, CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG4, pParent)
	,m_data(data)
	,m_bAgeFlag(false)
	,m_bGraFlag(false)
{

}

ListCtrl::~ListCtrl()
{
}

void ListCtrl::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST5, m_List);
}
//转格式UTF8格式转为CString风格
CString ListCtrl::UTF8ToCString(const std::string& utf8Str)
{
	int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, NULL, 0);
	if (wideLen <= 0) return CString("");

	wchar_t* wideStr = new wchar_t[wideLen];
	MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, wideStr, wideLen);
	CString result(wideStr);
	delete[] wideStr;
	return result;
}
//
BOOL ListCtrl::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// 设置ListCtrl的扩展样式
	m_List.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);

	//设置标题
	m_List.InsertColumn(0, _T("UserName"), LVCFMT_LEFT, 100);
	m_List.InsertColumn(1, _T("Id"), LVCFMT_LEFT, 100);
	m_List.InsertColumn(2, _T("Age"), LVCFMT_LEFT, 50);
	m_List.InsertColumn(3, _T("Grades"), LVCFMT_LEFT, 50);
	AdjustListCtrlColumns();

	//将查询到的结果进行填充
	for (size_t i = 0; i < m_data.size(); ++i)
	{
		const UserData& user = m_data[i];
		// 插入第一列(用户名)
		int index = m_List.InsertItem(i, UTF8ToCString(user.userName));
		// 插入其他列
		m_List.SetItemText(index, 1, UTF8ToCString(user.userId));
		m_List.SetItemText(index, 2, UTF8ToCString(std::to_string(user.age)));
		m_List.SetItemText(index, 3, UTF8ToCString(std::to_string(user.grades)));
	}
	return TRUE;  // 返回TRUE以启用对话框
}

//调整控件
void ListCtrl::AdjustListCtrlColumns()
{
	// 获取 CListCtrl 控件的客户区宽度
	CRect rect;
	m_List.GetClientRect(&rect);
	int totalWidth = rect.Width();  // 控件的总宽度

	// 计算每列的宽度
	int columnWidth = totalWidth / 4;

	// 设置每列的宽度,假设你有 4 列
	for (int i = 0; i < 4; ++i)
	{
		m_List.SetColumnWidth(i, columnWidth);  // 设置第 i 列的宽度
	}
}

BEGIN_MESSAGE_MAP(ListCtrl, CDialogEx)
	ON_WM_CLOSE()
	ON_BN_CLICKED(IDC_BUTTON1, &ListCtrl::OnBnClickedAge)
	ON_BN_CLICKED(IDC_BUTTON3, &ListCtrl::OnBnClickedGrades)
END_MESSAGE_MAP()


// ListCtrl 消息处理程序

//年龄排序
void ListCtrl::OnBnClickedAge()
{
	// TODO: 在此添加控件通知处理程序代码
	std::sort(m_data.begin(), m_data.end(), [this](const UserData& a, const UserData& b) {
		if (m_bAgeFlag)
		{
			return a.age < b.age;//升序
		}
		else
		{
			return a.age > b.age;//减序
		}
		});
	m_bAgeFlag = !m_bAgeFlag;
	// 清空 CListCtrl
	m_List.DeleteAllItems();

	//重新布局界面
	for (int i = 0; i < m_data.size(); ++i)
	{
		const UserData& user = m_data[i];
		// 插入第一列(用户名)
		int index = m_List.InsertItem(i, UTF8ToCString(user.userName));
		// 插入其他列
		m_List.SetItemText(index, 1, UTF8ToCString(user.userId));
		m_List.SetItemText(index, 2, UTF8ToCString(std::to_string(user.age)));
		m_List.SetItemText(index, 3, UTF8ToCString(std::to_string(user.grades)));

	}
}

//成绩排序
void ListCtrl::OnBnClickedGrades()
{
	// TODO: 在此添加控件通知处理程序代码

	std::sort(m_data.begin(), m_data.end(), [this](const UserData& a, const UserData& b) {
		if (m_bGraFlag)
		{
			return a.grades < b.grades;//升序
		}
		else
		{
			return a.grades > b.grades;//减序
		}
		});
	m_bGraFlag = !m_bGraFlag;
	// 清空 CListCtrl
	m_List.DeleteAllItems();

	//重新布局界面
	for (int i = 0; i < m_data.size(); ++i)
	{
		const UserData& user = m_data[i];
		// 插入第一列(用户名)
		int index = m_List.InsertItem(i, UTF8ToCString(user.userName));
		// 插入其他列
		m_List.SetItemText(index, 1, UTF8ToCString(user.userId));
		m_List.SetItemText(index, 2, UTF8ToCString(std::to_string(user.age)));
		m_List.SetItemText(index, 3, UTF8ToCString(std::to_string(user.grades)));

	}
}

void ListCtrl::OnClose()
{
	CDialogEx::OnOK();
}

QueryData.cpp ,这里实现查询数据界面的逻辑。

// QueryData.cpp: 实现文件
//

#include "pch.h"
#include "MainDlg.h"
#include "QueryData.h"
#include "afxdialogex.h"
#include"ListCtrl.h"

// QueryData 对话框

IMPLEMENT_DYNAMIC(QueryData, CDialogEx)

QueryData::QueryData(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG2, pParent)
	, m_strName(_T(""))
	, m_strId(_T(""))
{

}

QueryData::~QueryData()
{
}

void QueryData::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT1, m_strName);
	DDX_Text(pDX, IDC_EDIT2, m_strId);
}


BEGIN_MESSAGE_MAP(QueryData, CDialogEx)
	ON_BN_CLICKED(IDC_BUTTON1, &QueryData::OnBnClickedButton1)
	ON_BN_CLICKED(IDC_BUTTON4, &QueryData::OnBnClickedButton4)
	ON_BN_CLICKED(IDC_BUTTON6, &QueryData::OnBnClickedButton6)
	ON_BN_CLICKED(IDC_BUTTON7, &QueryData::OnBnClickedButton7)
END_MESSAGE_MAP()


// QueryData 消息处理程序

//姓名查询
void QueryData::OnBnClickedButton1()
{
	// TODO: 在此添加控件通知处理程序代码
	std::vector<UserData> result;
	UpdateData(true);
	std::string strname = CT2A(m_strName.GetString(), CP_UTF8);
	g_pDB->QueryData("UserName", strname, result);

	ListCtrl ListCtrl(result); // 将查询结果传递给目标对话框
	ListCtrl.DoModal();
}

//id查询
void QueryData::OnBnClickedButton4()
{
	// TODO: 在此添加控件通知处理程序代码
	std::vector<UserData> result;
	UpdateData(true);
	std::string strid = CT2A(m_strId.GetString(), CP_UTF8);
	g_pDB->QueryData("UserName", strid, result);

	ListCtrl ListCtrl(result); // 将查询结果传递给目标对话框
	ListCtrl.DoModal();
}

//查询所有
void QueryData::OnBnClickedButton6()
{
	// TODO: 在此添加控件通知处理程序代码
	std::vector<UserData> result;
	UpdateData(true);
	g_pDB->QueryDataAll(result);

	ListCtrl ListCtrl(result); // 将查询结果传递给目标对话框
	ListCtrl.DoModal();
}

//取消
void QueryData::OnBnClickedButton7()
{
	// TODO: 在此添加控件通知处理程序代码
	CDialogEx::OnCancel();
}

 主要代码就这些了,具体细节大家自己完善吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/970685.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Qt Creator 5.0.2 (Community)用久了突然变得很卡

目录 1.现象 2.解决方案 1.现象 很久没有用Qt Creator开发项目了&#xff0c;刚刚结束的项目又是用VS2019开发的&#xff1b;这两天刚好有时间去学习一下Qt&#xff0c;刚好要用Qt Creator&#xff0c;结果一打开就没反应&#xff0c;主界面显示出来要好几分钟&#xff0c;最…

[C++]多态详解

目录 一、多态的概念 二、静态的多态 三、动态的多态 3.1多态的定义 3.2虚函数 四、虚函数的重写&#xff08;覆盖&#xff09; 4.1虚函数 4.2三同 4.3两种特殊情况 &#xff08;1&#xff09;协变 &#xff08;2&#xff09;析构函数的重写 五、C11中的final和over…

mac 意外退出移动硬盘后再次插入移动硬盘不显示怎么办

第一步&#xff1a;sudo ps aux | grep fsck 打开mac控制台输入如下指令&#xff0c;我们看到会出现两个进程&#xff0c;看进程是root的这个 sudo ps aux|grep fsck 第二步&#xff1a;杀死进程 在第一步基础上我们知道不显示u盘的进程是&#xff1a;62319&#xff0c;我们…

国家队出手!DeepSeek上线国家超算互联网平台!

目前,国家超算互联网平台已推出 DeepSeek – R1 模型的 1.5B、7B、8B、14B 版本,后续还会在近期更新 32B、70B 等版本。 DeepSeek太火爆了!在这个春节档,直接成了全民热议的话题。 DeepSeek也毫无悬念地干到了全球增速最快的AI应用。这几天,国内的云计算厂家都在支持Dee…

ElasticSearch基础和使用

ElasticSearch基础 1 初识ES相关组件 &#xff08;1&#xff09;Elasticsearch是一款非常强大的开源搜索引擎&#xff0c;可以帮助我们从海量数据中快速找到需要的内容。Elasticsearch结合kibana、Logstash、Beats组件 也就是elastic stack&#xff08;ELK&#xff09; 广泛应…

使用Python爬虫实时监控行业新闻案例

目录 背景环境准备请求网页数据解析网页数据定时任务综合代码使用代理IP提升稳定性运行截图与完整代码总结 在互联网时代&#xff0c;新闻的实时性和时效性变得尤为重要。很多行业、技术、商业等领域的新闻都可以为公司或者个人发展提供有价值的信息。如果你有一项需求是要实时…

vs2022支持.netframework4.0

下载nuget包 .netframework4.0 解压nuget 复制到C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework 参考 https://www.cnblogs.com/bdqczhl/p/18670152 https://blog.csdn.net/xiaomeng1998_/article/details/135979884

【云安全】云原生- K8S kubeconfig 文件泄露

什么是 kubeconfig 文件&#xff1f; kubeconfig 文件是 Kubernetes 的配置文件&#xff0c;用于存储集群的访问凭证、API Server 的地址和认证信息&#xff0c;允许用户和 kubectl 等工具与 Kubernetes 集群进行交互。它通常包含多个集群的配置&#xff0c;支持通过上下文&am…

IoTDB 常见问题 QA 第五期

关于 IoTDB 的 Q & A 情人节之际&#xff0c;让 IoTDB Q&A 陪您一起共度解惑&#xff01;我们将定期汇总我们将定期汇总社区讨论频繁的问题&#xff0c;并展开进行详细回答&#xff0c;通过积累常见问题“小百科”&#xff0c;方便大家使用 IoTDB。 Q1&#xff1a;导入…

SwiftUI 5.0 中宝藏视图修改器 containerRelativeFrame 趣谈(下)

概览 小伙伴们都知道,为了将 SwiftUI 中多如牛毛的视图井然有序、有条不紊的组织起来,我们必须借助容器(Container)伏虎降龙般地威力。而如何最大限度的让容器中的子视图能根据容器尺寸安排自己的空间,则需要一些技术手段来洞幽察微。 在过去,我们往往使用 GeometryRead…

家里WiFi信号穿墙后信号太差怎么处理?

一、首先在调制解调器&#xff08;俗称&#xff1a;猫&#xff09;测试网速&#xff0c;网速达不到联系运营商&#xff1b; 二、网线影响不大&#xff0c;5类网线跑500M完全没问题&#xff1b; 三、可以在卧室增加辅助路由器&#xff08;例如小米AX系列&#xff09;90~200元区…

windows,docker停止所有容器

CMD命令窗口 你可以尝试使用以下命令来停止所有正在运行的Docker容器&#xff1a; FOR /f "tokens*" %i IN (docker ps -q) DO docker stop %i这条命令的工作原理是&#xff1a; docker ps -q 列出所有正在运行的容器的ID。 FOR /f "tokens*" %i IN (c…

Jenkins介绍

什么是Jenkins Jenkins 是一个开源的自动化服务器&#xff0c;主要用于持续集成和持续交付&#xff08;CI/CD&#xff09;。它帮助开发团队自动化构建、测试和部署软件&#xff0c;从而提高开发效率和软件质量。 如果一个系统是前后端分离的开发模式&#xff0c;在集成阶段会需…

解锁电商数据宝藏:淘宝商品详情API实战指南

在电商蓬勃发展的今天&#xff0c;数据已成为驱动业务增长的核心引擎。对于商家、开发者以及数据分析师而言&#xff0c;获取精准、实时的商品数据至关重要。而淘宝&#xff0c;作为国内最大的电商平台&#xff0c;其海量商品数据更是蕴含着巨大的价值。 本文将带你深入探索淘…

嵌入式硬件篇---OpenMV的硬件流和软件流

文章目录 前言一、硬件流控制&#xff08;Hardware Flow Control&#xff09;1. 基本原理RTSCTS 2. OpenMV中的实现• 硬件要求• 代码配置• 工作流程 二、软件流控制&#xff08;Software Flow Control&#xff09;1. 基本原理XONXOFF 2. OpenMV中的实现• 代码配置• 工作流…

小米平板怎么和电脑共享屏幕

最近尝试使用小米平板和电脑屏幕分屏互联 发现是需要做特殊处理的&#xff0c;需要下载一款电脑安装包&#xff1a;小米妙享 关于这个安装包&#xff0c;想吐槽的是&#xff1a; 没有找到官网渠道&#xff0c;是通过其他网络方式查到下载的 不附录链接&#xff0c;原因是因为地…

ML.Net二元分类

ML.Net二元分类 文章目录 ML.Net二元分类前言项目的创建机器学习模型的创建添加模型选择方案训练环境的选择训练数据的添加训练数据的选择训练数据的格式要预测列的选择模型评估模型的使用总结前言 ‌ML.NET‌是由Microsoft为.NET开发者平台创建的免费、开源、跨平台的机器学习…

在本地校验密码或弱口令 (windows)

# 0x00 背景 需求是验证服务器的弱口令&#xff0c;如果通过网络侧校验可能会造成账户锁定风险。在本地校验不会有锁定风险或频率限制。 # 0x01 实践 ## 1 使用 net use 命令 可以通过命令行使用 net use 命令来验证本地账户的密码。打开命令提示符&#xff08;CMD&#xff0…

全方位养生指南:打造健康生活蓝图

在这个追求高效与速度的时代&#xff0c;健康养生的重要性愈发凸显&#xff0c;它是我们享受美好生活的前提。下面&#xff0c;让我们从饮食、运动、作息、情绪管理四个维度&#xff0c;解锁健康密码。 饮食是养生的根基&#xff0c;合理搭配才能滋养身心。一日三餐&#xff0c…

【Java 面试 八股文】Spring Cloud 篇

Spring Cloud 篇 1. Spring Cloud 5大组件有哪些&#xff1f;2. 服务注册和发现是什么意思&#xff1f;Spring Cloud 如何实现服务注册发现&#xff1f;3. 我看你之前也用过nacos&#xff0c;你能说下nacos与eureka的区别&#xff1f;4. 你们项目负载均衡如何实现的&#xff1f…