MFC使用Windows API实现U盘插拔检测,获取U盘容量,U盘内容移动删除,开启和关闭U盘以及获取盘符


前言

MFC实现对U盘的管理并用图形化界面展示出来。


项目下载地址
MFC教程

一、利用OS API实现对U盘的管理

  • 能够判断U盘是否存在
  • 能够显示U盘的总容量、使用容量和剩余容量;
  • 能够将某个目录上的文件或整个目录复制到U盘上;
  • 可以删除U盘上文件;
  • 禁止U盘的使用及开启U盘的使用;
  • 推荐使用C# winform,也可以使用其它语言;
  • 体会OS的API的作用; 尝试读取PCB信息;
  • 其它创意。 项目最终能以图形界面的形式完成。

二、项目到底长啥样

界面比较简单

三、实例代码

本项目主要采用MFC(微软基础类库)开发一个Windows窗体程序,实现对U盘的管理。项目第一个功能以及使用MFC开发的idea源自一位学长。其余功能由自己通过网上查阅资料实现。
项目传送门:
https://blog.csdn.net/qq_39861376/article/details/102678582

1.实时判断U盘插入与拔出

OnDeviceChange(UINT nEventType, DWORD dwData):
首先设置消息映射

afx_msg BOOL OnDeviceChange(UINT nEventType, DWORD dwData);
BEGIN_MESSAGE_MAP(CMFCUSBDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_BUTTON1, &CMFCUSBDlg::OnBnClickedButton1)
	//ON_WM_ACTIVATE()
	ON_WM_DEVICECHANGE()
END_MESSAGE_MAP()

注册USB设备消息,不注册消息处理函数无法响应, 只有注册了该设备,OnDeviceChange才能获得详细的信息,否则收到的nEventType参数都是0007,dwData无数据。

BOOL CMFCUSBDlg::OnInitDialog()
	// 注意向OnInitDialog中添加如下代码;
	DEV_BROADCAST_DEVICEINTERFACE Filter;
	ZeroMemory(&Filter, sizeof(Filter));
	Filter.dbcc_size = sizeof(Filter);   // size gets set to 29 with 1-byte packing or 32 with 4- or 8-byte packing
	Filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
	Filter.dbcc_classguid = {0xA5DCBF10,0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
	//DEVICE_NOTIFY_ALL_INTERFACE_CLASSES //关注所有设备事件
	RegisterDeviceNotification(this->m_hWnd, &Filter, DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
	if (NULL == RegisterDeviceNotification(this->m_hWnd, &Filter, DEVICE_NOTIFY_ALL_INTERFACE_CLASSES))
		TRACE("RegisterDeviceNotification failed!!");

常见的GUID
GUID:
https://blog.csdn.net/diyu122222/article/details/79791073

C/C++ code
static const GUID GUID_DEVINTERFACE_LIST[] = 
{
    // GUID_DEVINTERFACE_USB_DEVICE
    { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } },

    // GUID_DEVINTERFACE_DISK
    { 0x53f56307, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b } },

    // GUID_DEVINTERFACE_HID, 
    { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } },

    // GUID_NDIS_LAN_CLASS
    { 0xad498944, 0x762f, 0x11d0, { 0x8d, 0xcb, 0x00, 0xc0, 0x4f, 0xc3, 0x35, 0x8c } }

     GUID_DEVINTERFACE_COMPORT
    //{ 0x86e0d1e0, 0x8089, 0x11d0, { 0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73 } },

     GUID_DEVINTERFACE_SERENUM_BUS_ENUMERATOR
    //{ 0x4D36E978, 0xE325, 0x11CE, { 0xBF, 0xC1, 0x08, 0x00, 0x2B, 0xE1, 0x03, 0x18 } },

     GUID_DEVINTERFACE_PARALLEL
    //{ 0x97F76EF0, 0xF883, 0x11D0, { 0xAF, 0x1F, 0x00, 0x00, 0xF8, 0x00, 0x84, 0x5C } },

     GUID_DEVINTERFACE_PARCLASS
    //{ 0x811FC6A5, 0xF728, 0x11D0, { 0xA5, 0x37, 0x00, 0x00, 0xF8, 0x75, 0x3E, 0xD1 } }
};

接受设备变更消息:

BOOL CMFCUSBDlg::OnDeviceChange(UINT nEventType, DWORD dwData)
{
	//const int DBT_DEVICEARRIVAL = 0x8000; // system detected a new device
	//public const int DBT_DEVICEQUERYREMOVE = 0x8001;   // Preparing to remove (any program can disable the removal)
	//const int DBT_DEVICEREMOVECOMPLETE = 0x8004; // removed 
	//const int DBT_DEVTYP_VOLUME = 0x00000002;
	//DEV_BROADCAST_DEVICEINTERFACE * dbd = (DEV_BROADCAST_DEVICEINTERFACE*)dwData;
	//CString str;
	//str.Format(_T("U disk in !"));
	//SetDlgItemText(IDC_BUTTON1, str);第一个短点,判断是否接收到设备变动的信息
	CString detectMsg;
	char  m_decDriver;
	PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)dwData;
	switch (nEventType)
	{
	case DBT_DEVICEARRIVAL:
		{
		//第二个断点,判断nEventType传来的值是0007还是0X8000
		if (lpdb->dbch_devicetype==DBT_DEVTYP_VOLUME)//逻辑卷
		{
			
			PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
			switch (lpdbv->dbcv_flags)
			{
			case 0://U盘
			{
				m_decDriver = FirstDriveFromMask(lpdbv->dbcv_unitmask);
				detectMsg.Format(_T("检测到U盘:[%c]插入!"),m_decDriver);
				MessageBox(detectMsg);
			}
			break;
			case DBTF_MEDIA://光盘
				break;
			}
		}
	}
	break;
	case DBT_DEVICEREMOVECOMPLETE:
		//   Handle   device   removal
	{
		
		if (lpdb->dbch_devicetype == DBT_DEVTYP_VOLUME)//逻辑卷
		{
			PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
			switch (lpdbv->dbcv_flags)
			{
			case 0:                //U盘
			{
				m_decDriver = FirstDriveFromMask(lpdbv->dbcv_unitmask);
				//detectMsg.Format(_T("检测到U盘:[%s]拔出!"), m_decDriver/*.GetBuffer(0)*/);
				detectMsg.Format(_T("检测到U盘:[%c]拔出!"),m_decDriver);
				MessageBox(detectMsg);
				
			}
			break;
			case DBTF_MEDIA:    //光盘
				break;
			}
		}
	}
	break;
	}
	//CString str;
	//str.Format(_T("U disk in !"));
	//SetDlgItemText(IDC_BUTTON1, str);
	return  TRUE;
}

获取单元掩码

char FirstDriveFromMask(ULONG unitmask)
{
	char i;

	for (i = 0; i < 26; ++i)
	{
		if (unitmask & 0x1)
			break;
		unitmask = unitmask >> 1;
	}

	return(i + 'A');
}

2.能够显示U盘的总容量、使用容量和剩余容量

按钮事件:

void CMFCUSBDlg::OnBnClickedButton1()
{
	 CString m_decDriver = GetDiskLetter();
	// 获取盘符
	 INT_PTR nRes;
	 CTipDlg tipDlg;           // 构造对话框类CTipDlg的实例   
	 nRes = tipDlg.DoModal();  // 弹出对话框   
	 //从这里开始
	 UpdateData(true);
	   ULARGE_INTEGER nFreeBytesAvailable;
	   ULARGE_INTEGER nTotalNumberOfBytes;
	   ULARGE_INTEGER nTotalNumberOfFreeBytes;
		   //lpDirectoryName 是驱动器的名称。
		   //lpFreeBytesAvailableToCaller 是用户可用的磁盘空间。
		   //lpTotalNumberOfBytes 是磁盘总共的空间。
		   //lpTotalNumberOfFreeBytes 是磁盘空闲的空间。以上都是字节为单位。
	   if (GetDiskFreeSpaceEx(m_decDriver + ":", &nFreeBytesAvailable, &nTotalNumberOfBytes, &nTotalNumberOfFreeBytes))
	   {
		   CString str;
		   str.Format(_T("用户可用的磁盘空间:%.2fGB\n磁盘总空间:%.2fGB\n磁盘空闲空间:%.2fGB\n"), (GB(nFreeBytesAvailable)), (GB(nTotalNumberOfBytes)), (GB(nTotalNumberOfFreeBytes)));
		   MessageBox(str, _T("磁盘信息"), MB_OK);
	   }
	   UpdateData(false);
}

获取盘符:

CString GetDiskLetter()
{
	CString strDiskLetter = _T("");
	LPTSTR lpDrives = new TCHAR[MAX_PATH];
	DWORD dwLen = ::GetLogicalDriveStrings(MAX_PATH, lpDrives);
	CString sDrives[26];
	for (DWORD nIndex = 0; nIndex < dwLen / 4; nIndex++)
	{
		if (::GetDriveType(lpDrives + nIndex * 4) == DRIVE_REMOVABLE)
		{
			sDrives[nIndex] += (CString)(lpDrives + nIndex * 4);
			if (sDrives[nIndex] != _T("A://") && sDrives[nIndex] != _T("B://"))
			{
				CString usbPath = sDrives[nIndex];
				strDiskLetter = usbPath.Left(1);
				//AfxMessageBox(strDiskLetter);
				break;
			}
		}
	}
	delete lpDrives;
	return strDiskLetter;
}

字节与GB转化:

float GB(ULARGE_INTEGER TotalNumberOfBytes) {
	int iTotal = (TotalNumberOfBytes.u.HighPart << 12) + (TotalNumberOfBytes.u.LowPart >> 20);
	return iTotal / 1024.0;
}

3.能够将某个目录上的文件或整个目录复制到U盘上

获取文件路径:

void CMFCUSBDlg::OnBnClickedButtonfile()
{
	CString strFile = _T("");
	
		 CFileDialog    dlgFile(TRUE, NULL, NULL, OFN_HIDEREADONLY, _T("Describe Files (*.jpg)|*.jpg|All Files (*.*)|*.*||"), NULL);
	
		 if (dlgFile.DoModal())
		 {
		      strFile = dlgFile.GetPathName();
		 }
		 SetDlgItemText(EditFile, strFile);
	// TODO: 在此添加控件通知处理程序代码
}

获取目录路径:

void CMFCUSBDlg::OnBnClickedButtondir()
{
	TCHAR           szFolderPath[MAX_PATH] = { 0 };
	CString         strFolderPath = TEXT("");

	BROWSEINFO      sInfo;
	::ZeroMemory(&sInfo, sizeof(BROWSEINFO));
	sInfo.pidlRoot = 0;
	sInfo.lpszTitle = _T("请选择处理结果存储路径");
	sInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_EDITBOX | BIF_DONTGOBELOWDOMAIN;
	sInfo.lpfn = NULL;

	// 显示文件夹选择对话框  
	LPITEMIDLIST lpidlBrowse = ::SHBrowseForFolder(&sInfo);
	if (lpidlBrowse != NULL)
	{
		// 取得文件夹名  
		if (::SHGetPathFromIDList(lpidlBrowse, szFolderPath))
		{
			strFolderPath = szFolderPath;
		}
	}
	if (lpidlBrowse != NULL)
	{
		::CoTaskMemFree(lpidlBrowse);
	}
	CString strFile;
	GetDlgItemText(EditFile, strFile);
	string dirname = Replace(strFile);
	string filename;
	int flag;
	for (int i = dirname.length() - 1; i >= 0; i--)
	{
		if (dirname[i] == '/') {
			flag = i;
			break;
		}
	}
	for (int i = flag + 1; i < dirname.length(); i++) {
		filename += dirname[i];
	}
	//strFolderPath = strFolderPath + filename;
	
	CString cstrTest;
	cstrTest = CA2T(filename.c_str());
	SetDlgItemText(EditDir, strFolderPath+cstrTest);
			     //return strFolderPath		 
}

复制文件:

void CMFCUSBDlg::OnBnClickedButtoncopy()
{
	
	//string Ef= "C:/masm/usb/usb.txt";
	//string Nf = "D:/1usb.txt";
	CString Ef; 
	GetDlgItemText(EditFile, Ef);
	CString Nf; 
	GetDlgItemText(EditDir, Nf);
	CString str;
	//str.Format(_T("请选择要复制的文件"));
	if (Replace(Ef)=="") {
		//cout << 1;
		str.Format(_T("请选择要复制的文件"));
		MessageBox(str);		
	}
	else if (Replace(Nf) == "")
	{
		str.Format(_T("请选择文件复制位置"));
		MessageBox(str);
	}
	else{	
	wstring stempEf = s2ws(Replace(Ef));
	wstring stempNf = s2ws(Replace(Nf));
	LPCWSTR resultEf = stempEf.c_str();
	LPCWSTR resultNf = stempNf.c_str();
	// TODO: 在此添加控件通知处理程序代码
	CString str;
	str.Format(_T("复制失败,请检查您的文件路径"));
	if (!CopyFile(resultEf, resultNf, FALSE))
	{
		MessageBox(str);
	}	
	else {
		str.Format(_T("Copy successfully!"));
		MessageBox(str);
	}
	}
}

替换函数:

//把\替换成/
string Replace(CString str) {
	string strTest;
	//CString转换为String
	strTest = CT2A(str.GetString());
	for (int i = 0; i < strTest.length(); i++) {
		if (str[i] == '\\') {
			strTest = strTest.replace(i, 1, "/");
		}
	}
	return strTest;
}

类型转换函数(string转化为wstring再转换为LPCTSTR):

//string到wstring类型转换,wstring可以转换为LPCTSTR文件
wstring s2ws(const std::string& s) {

	int len;

	int slength = (int)s.length() + 1;

	len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);

	wchar_t* buf = new wchar_t[len];

	MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);

	std::wstring r(buf);

	delete[] buf;

	return r.c_str();

}

第二种类型转换的方法:

LPCWSTR stringToLPCWSTR(std::string orig)
{
	size_t origsize = orig.length() + 1;
	const size_t newsize = 100;
	size_t convertedChars = 0;
	wchar_t *wcstring = (wchar_t *)malloc(sizeof(wchar_t)*(orig.length() - 1));
	mbstowcs_s(&convertedChars, wcstring, origsize, orig.c_str(), _TRUNCATE);

	return wcstring;
}

第三种类型转化:

直接强转(LPCTSTR)string

4. 删除U盘上文件

获取文件路径:同复制操作
删除文件:

void CMFCUSBDlg::OnBnClickedButtondelete()
{
	CString Nf;
	GetDlgItemText(EditFile, Nf);
	wstring stempNf = s2ws(Replace(Nf));
	LPCWSTR resultNf = stempNf.c_str();
	BOOL del = DeleteFile(resultNf);
	if (del)
	{
		MessageBox(_T("删除成功"), _T("提示信息"), MB_OK);
	}
	else
	{
		DWORD error_rus = GetLastError();
		MessageBox( _T("删除失败"), _T("提示信息"), MB_OK);
	}

}

5.禁止U盘的使用及开启U盘的使用

这里采用更改注册表的方式达到禁用与开启的效果,详情可以见:
https://blog.csdn.net/weixin_44627672/article/details/109430607

void CMFCUSBDlg::OnBnClickedCloseu()
{
	long lRet;
	HKEY hKey;
	lRet = RegCreateKey(
		HKEY_LOCAL_MACHINE,
		_T("SYSTEM\\CurrentControlSet\\Services\\USBSTOR"),
		&hKey
	);
	DWORD KeyValue = 4;
	
	if (lRet == ERROR_SUCCESS)
	{

		//MessageBox(_T("right"), _T("Prompt"), MB_OK);
		lRet = RegSetKeyValue(HKEY_LOCAL_MACHINE,
			_T("SYSTEM\\CurrentControlSet\\Services\\USBSTOR"),
			_T("Start"),
			REG_DWORD,
			(LPBYTE)&KeyValue,
			sizeof(DWORD)
		);
		if (lRet == ERROR_SUCCESS)
		{
			MessageBox(_T("禁用成功"), _T("Prompt"), MB_OK);
		}
		else
		{
			MessageBox(_T("禁用失败,请检查您的权限"), _T("Prompt"), MB_OK);
		}
		RegCloseKey(hKey);
	}
	else {
		MessageBox(_T("权限不足"), _T("Prompt"), MB_OK);
		RegCloseKey(hKey);
	}
	//open+query查询值
	/*
	TCHAR tchData[64];
	DWORD dwSize;
	DWORD dwValue, dwType;
	DWORD dwBufLen = 255;
	DWORD dwRet;
	dwSize = sizeof(data);
	lRet = RegOpenKeyEx(
		HKEY_LOCAL_MACHINE,         // handle to open key
		_T("SYSTEM\\CurrentControlSet\\Services\\USBSTOR"),  // subkey name
		0,   // reserved
		KEY_QUERY_VALUE, // security access mask
		&hKey    // handle to open key
	);
	假设start说存储的数据是REG_SZ
	lRet = RegQueryValueEx(
			hKey,            // handle to key
			_T("Start"),  // value names
			NULL,   // reserved
			NULL,       // type buffer
			(LPBYTE)tchData,        // data buffer
			&dwSize      // size of data buffer
		);
	if (RegQueryValueEx(hKey, _T("Start"), NULL, &dwType, (LPBYTE)&dwValue, &dwBufLen) == ERROR_SUCCESS)
	{
		if (dwType == REG_DWORD)
			dwRet = dwValue;
	}
	else {
		MessageBox(_T("234"));
	}
	RegCloseKey(hKey);
	*/
}

6.尝试读取PCB信息

这里只获取了PID

//获取PCB信息如PID
void CMFCUSBDlg::OnBnClickedButtonpcb()
{
	CString str;
	DWORD csPid;
	csPid = GetCurrentProcessId();
	str.Format(_T("PID:%u"), csPid);
	//SetDlgItemText(IDC_EDIT1, _T("PID:[]"));
	SetDlgItemText(IDC_EDIT1, str);
}

四、总结

做完这个项目将近三天,因为自己基础不好实现起来不是很容易,好在终于解决了;主要还是提高了自己的文献检索能力(如何快速的从网上获得自己所需要的东西,这点很重要)、以及独立解决问题的能力(一个复杂的问题是如何一步一步解决的。从毫无思路到知道大概思路再到具体实现,不断发现问题解决问题,最后走到终点。)
就像海里航行的船,没有指南针,到处都没有路,也可以说到处都是路,如何走到终点这是一个很重要的问题。
最后感谢以下大佬的博客:
1.VS2017加装MFC以及创建第一个MFC程序
https://blog.csdn.net/qq_35987486/article/details/94456910
https://blog.csdn.net/qq_39861376/article/details/102678582
2.为什么找不到标志符
https://blog.csdn.net/taotaoah/article/details/46333983
三.判断U盘是否拔出与插入
1.如何进行消息映射
https://blog.csdn.net/eqiang8271/article/details/24477001?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param
2.USB GUID
https://blog.csdn.net/phenixyf/article/details/48783271
3.判断U盘插入拔出程序
https://blog.csdn.net/qq_39861376/article/details/102678582
4.各个函数的使用以及导入外部库(windows.h,dbt.h)
MSDN
四.判断U盘容量
lpDirectoryName 是驱动器的名称。
lpFreeBytesAvailableToCaller 是用户可用的磁盘空间。
lpTotalNumberOfBytes 是磁盘总共的空间。
lpTotalNumberOfFreeBytes 是磁盘空闲的空间。以上都是字节为单位。
1.GB与bytes转换:
https://blog.csdn.net/qq_41786318/article/details/80969881
2.获取盘符
忘了记录
3.判断U盘容量程序
https://blog.csdn.net/qq_39861376/article/details/102678582
4.如何用MFC框架生成模态对话框弹出式的程序(跟C#、VB,类似)
鸡啄米http://www.jizhuomi.com/software/160.html
五、复制文件操作
1.copyfile函数的使用
MSDN
https://www.jb51.net/article/190834.htm
2.如何查找文件路径
https://blog.csdn.net/wuhenyouyuyouyu/article/details/78521092
3.string中的replace函数
https://blog.csdn.net/qq_40968179/article/details/104380460
4.Cstring与string之间的相互转换
https://blog.csdn.net/Gordon_Wei/article/details/90443677
5.string如何转换成LPCTSTR类型
https://www.cnblogs.com/qinguoyi/p/7249561.html?utm_source=itdadao&utm_medium=referral
六、删除U盘上的文件
1.deleteFile函数等使用
MSDN
七、停止U盘的使用与终止U盘的使用
如何通过注册表禁用U盘
https://jingyan.baidu.com/article/e2284b2b892cd7e2e6118dbb.html
对注册表的操作
https://www.cnblogs.com/zhaoyixiang/p/12983823.html
如何修改REG_DWORD类型的start值
https://zhidao.baidu.com/question/1046050784719513299.html
八、尝试读取PCB的信息
VC如何获取当前自身PID:
https://blog.csdn.net/sunnysab/article/details/9367077?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.channel_param

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值