教程二:windows api(c mfc vs2017)实现U盘插拔检测,获取U盘容量,U盘内容移动,开启和关闭U盘以及获取盘符等

实现功能:

通过使用OS的API编写一个程序,满足下列要求:

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

完整项目源代码下载地址:https://download.csdn.net/download/qq_39861376/11888792

实验报告下载:https://download.csdn.net/download/qq_39861376/11937766

如果想要本节的源代码,请私信qq193769981

界面较为简单,但实现了全部功能,可自行设计更加美观的界面。

(vs2017解压打开可以点.sin文件直接用,如果进去之后报错可以点击项目属性页(如下图),将字符集改为使用多字节字符集,如果还不行,请联系我 qq193769981)


 

目录

一、功能

功能一:判断u盘是否存在

1.1 单纯判断u盘是否存在并获取盘符

1.2 时刻检测U盘插入和拔出

功能二:能够显示U盘的总容量、使用容量和剩余容量

功能三:能够将某个目录上的文件或整个目录复制到U盘上

3.1目录

3.2文件

3.3按钮控件

功能四:可以删除U盘上文件

功能五:禁止U盘的使用及开启U盘的使用

功能六:尝试读取PCB信息

二、各个功能实现时的参考资料(站在巨人的肩膀上)

三、心得


 

一、功能

功能一:判断u盘是否存在

1.1 单纯判断u盘是否存在并获取盘符

CString check() 
{
	int DSLength = GetLogicalDriveStrings(0, NULL);
	//通过GetLogicalDriveStrings()函数获取所有驱动器字符串信息长度。
	char* DStr = new char[DSLength];//用获取的长度在堆区创建一个c风格的字符串数组
	GetLogicalDriveStrings(DSLength, (LPTSTR)DStr);
	//通过GetLogicalDriveStrings将字符串信息复制到堆区数组中,其中保存了所有驱动器的信息。
	CString a;
	int DType;
	int si = 0;
	for (int i = 0; i < DSLength / 4; ++i)
		//为了显示每个驱动器的状态,则通过循环输出实现,由于DStr内部保存的数据是A:\NULLB:\NULLC:\NULL,这样的信息,所以DSLength/4可以获得具体大循环范围
	{
		char dir[3] = { DStr[si],':','\\' };
		DType = GetDriveType(DStr + i * 4);
		//GetDriveType函数,可以获取驱动器类型,参数为驱动器的根目录
		if (DType == DRIVE_REMOVABLE)
		{
			a = dir[0];
			return a;
		}
		si += 4;
	}
	return "无U盘";
}

在OnInitDiglog()函数初始换对话框时调用此函数即可。

BOOL CUDISKMFCDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    m_decDriver = check();

     。。。。。。

}

1.2 时刻检测U盘插入和拔出

这里有两种方法,只介绍一种,重载OnDeviceChange()函数

BOOL CUDISKMFCDlg::OnDeviceChange(UINT nEventType, DWORD dwData)
{
	//DEV_BROADCAST_DEVICEINTERFACE * dbd = (DEV_BROADCAST_DEVICEINTERFACE*)dwData;
	CString detectMsg;
	PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)dwData;
	switch (nEventType)
	{
		case DBT_DEVICEARRIVAL:
		{
			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)*/);
						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));
						MessageBox(detectMsg);
                    }
                    break;
			        case DBTF_MEDIA:    //光盘
                    break;
                }	
            }
		}	
		break;
	}
	return  TRUE;
}

功能二:能够显示U盘的总容量、使用容量和剩余容量

void CUDISKMFCDlg::OnBnClickedAddButton()
{
	//INT_PTR nRes;             // 用于保存DoModal函数的返回值   
	CTipDlg tipDlg;           // 构造对话框类CTipDlg的实例   
	nRes = tipDlg.DoModal();  // 弹出对话框   
	//nRes = MessageBox(_T("您确定要进行加法计算吗?"), _T("加法计算器"), MB_OKCANCEL | MB_ICONQUESTION);
	//if (IDCANCEL == nRes)     // 判断对话框退出后返回值是否为IDCANCEL,如果是则return,否则继续向下执行   
	//	return;
	 TODO: 在此添加控件通知处理程序代码
	//UpdateData(true);  //TRUE表示从控件传给变量
	//m_editSum = m_editSummand + m_editAddend;
	//UpdateData(false); //FALSE表示从变量传给控件

	//从这里开始
	UpdateData(true); 
	ULARGE_INTEGER nFreeBytesAvailable;
	ULARGE_INTEGER nTotalNumberOfBytes;
	ULARGE_INTEGER nTotalNumberOfFreeBytes;
	
	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,"磁盘信息",MB_OK);
		//cout << GB(nFreeBytesAvailable) << "GB" << endl << GB(nTotalNumberOfBytes) << "GB" << endl << GB(nTotalNumberOfFreeBytes) << "GB" << endl;
	}
	UpdateData(false);
	//从这里结束

}

功能三:能够将某个目录上的文件或整个目录复制到U盘上

3.1目录

void CUDISKMFCDlg::OnBnClickedButtonFile()
{
	SetDlgItemText(IDC_EDIT_DIR,NULL);
	CString sFile;
	GetDlgItemText(IDC_EDIT_FILE, sFile);

	std::string strFile = sFile;
	if (IDOK == BaseFunc::selFile(strFile,"*","true"))
	{
		SetDlgItemText(IDC_EDIT_FILE, strFile.c_str());
		UpdateData(true);
	}
	m_flag = false;
}

3.2文件

void CUDISKMFCDlg::OnBnClickedButtonDir()
{
	SetDlgItemText(IDC_EDIT_FILE,NULL);
	// TODO: 在此添加控件通知处理程序代码
	CString sDir;
	GetDlgItemText(IDC_EDIT_DIR, sDir);//可扩展个返回string

	std::string strDir = sDir;
	if (IDOK == BaseFunc::selDir(strDir, GetSafeHwnd()))
	{
		SetDlgItemText(IDC_EDIT_DIR, strDir.c_str());
		UpdateData(true);
	}
	m_flag = true;//默认是删除一个指定文件
}

3.3按钮控件

void CUDISKMFCDlg::OnBnClickedMoveButton()
{
	//MessageBox(m_filepath);
	if (m_filepath == "" && m_dirpath == "")
	{
		MessageBox("请选择要移动的文件夹或者文件!");
	}
	else
	{
		CString strSource;
		if (m_flag == false)
		{
			strSource = m_filepath;
		}
		else
		{
			strSource = m_dirpath;
		}
		//strSource = "C:\\Users\\Administrator\\Desktop\\ccf";   //文件和文件夹都可以
		strSource += '\0';//注意必须是'\0'而不是"\0"!~!!
		//MessageBox(m_decDriver);
		CString strDes = m_decDriver + ":\\";
		strDes += '\0';
		SHFILEOPSTRUCT fop;
		fop.wFunc = FO_COPY;//选择执行类型,FO_COPY,FO_DELETE,FO_RENAME,FO_MOVE四种
		fop.pFrom = strSource;//源文件夹的路径,以'\0'即空为结尾
		fop.pTo = strDes;//拷入文件的文件夹路径,以'\0'即空为结尾
		if (SHFileOperation(&fop) == 0)
		{
			MessageBox("成功");
		}
		else
		{
			MessageBox("不成功");
		}
	}
}

功能四:可以删除U盘上文件

选择文件或者文件夹都可以删除,选择方法和3.1,3.2中的函数一样,这里只介绍删除按钮的函数

//把整个文件夹里的内容删除
BOOL DelDirFileOpt(string szPath)
{
	WIN32_FIND_DATA wfd;
	HANDLE hFind;
	string sFullPath;
	string sFindFilter;
	DWORD dwAttributes = 0;
	sFindFilter = szPath;
	sFindFilter += _T("\\*.*");
	if ((hFind = FindFirstFile(sFindFilter.c_str(), &wfd)) == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}
	do
	{
		if (_tcscmp(wfd.cFileName, _T(".")) == 0 ||
			_tcscmp(wfd.cFileName, _T("..")) == 0)
		{
			continue;
		}

		sFullPath = szPath;
		sFullPath += _T('\\');
		sFullPath += wfd.cFileName;
		//去掉只读属性
		dwAttributes = GetFileAttributes(sFullPath.c_str());
		if (dwAttributes & FILE_ATTRIBUTE_READONLY)
		{
			dwAttributes &= ~FILE_ATTRIBUTE_READONLY;
			SetFileAttributes(sFullPath.c_str(), dwAttributes);
		}
		if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			printf("进入目录%s\n", sFullPath.c_str());
			DelDirFileOpt(sFullPath.c_str());
			RemoveDirectory(sFullPath.c_str());
			printf("删除目录%s成功\n", sFullPath.c_str());
		}
		else
		{
			if (_tcsicmp(wfd.cFileName, _T("index.dat")) == 0)
			{
				//WipeFile(szPath, wfd.cFileName);
			}
			DeleteFile(sFullPath.c_str());
			printf("文件%s删除成功\n", sFullPath.c_str());
		}
		printf("文件%s删除成功\n", sFullPath.c_str());

	} while (FindNextFile(hFind, &wfd));
	FindClose(hFind);
	return TRUE;
}
//只删除指定文件
BOOL DelFileOpt(string szPath)
{
	TRACE(szPath.c_str());
	return DeleteFile(szPath.c_str());
}
void CUDISKMFCDlg::OnBnClickedDeleteButton()
{
	//MessageBox(m_dirpath);
	//DelDirFileOpt(m_dirpath.GetBuffer(0));
	BOOL result;
	if (m_flag==false)
	{
		result = DelFileOpt(m_filepath.GetBuffer(0));
	}
	else
	{
		result = DelDirFileOpt(m_dirpath.GetBuffer(0));
	}
	if (result)
	{
		MessageBox("成功");
	}
	else
	{
		MessageBox("不成功");
	}
}

功能五:禁止U盘的使用及开启U盘的使用

BOOL HideVolume(LPCTSTR lpDriveLetter, LPCTSTR lpDevice, BOOL bAddMountPoint)
{
	BOOL bRet = FALSE;
	TCHAR szDriveLetterAndSlash[4] = { 0 };
	TCHAR szDriveLetter[3] = { 0 };
	TCHAR szUniqueVolumeName[MAX_PATH] = { 0 };
	// 	if(lpDriveLetter && lpDevice)
	// 	{
	szDriveLetter[0] = lpDriveLetter[0];
	szDriveLetter[1] = TEXT(':');
	szDriveLetter[2] = TEXT('\0');

	szDriveLetterAndSlash[0] = lpDriveLetter[0];
	szDriveLetterAndSlash[1] = TEXT(':');
	szDriveLetterAndSlash[2] = TEXT('\\');
	szDriveLetterAndSlash[3] = TEXT('\0');
	/*	}*/
	if (bAddMountPoint)
	{
		//Try to Attach lpDevice to lpDriveLetter
		bRet = DefineDosDevice(DDD_RAW_TARGET_PATH, szDriveLetter,
			lpDevice);

		if (bRet)
		{
			if (!GetVolumeNameForVolumeMountPoint(szDriveLetterAndSlash,
				szUniqueVolumeName,
				MAX_PATH))
			{
				//Can't Find Attached lpDevice 's VolumeName
				szUniqueVolumeName[0] = '\0';
			}

			bRet = DefineDosDevice(
				DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION |
				DDD_EXACT_MATCH_ON_REMOVE, szDriveLetter,
				lpDevice);

			if (!bRet)
				return bRet;

			bRet = SetVolumeMountPoint(szDriveLetterAndSlash,
				szUniqueVolumeName);
		}
	}
	else
	{
		bRet = DeleteVolumeMountPoint(szDriveLetterAndSlash);
	}
	return bRet;

}
void CUDISKMFCDlg::OnBnClickedButton4OPENUDISK()
{
	// TODO: 在此添加控件通知处理程序代码
	HideVolume(_T("F://"), DosPath, TRUE);
}
void CUDISKMFCDlg::OnBnClickedButton5CLOSEUDISK()
{
	//MessageBox(m_decDriver);
	QueryDosDevice(_T(m_decDriver + ":"), DosPath, MAX_PATH);
	HideVolume(_T(m_decDriver + "://"), NULL, FALSE);
}

功能六:尝试读取PCB信息

void CUDISKMFCDlg::OnBnClickedButton3PCB()
{
	// TODO: 在此添加控件通知处理程序代码
	const char *name = "U_DISK_MFC.exe";
	CString str;
	DWORD id = GetProcessID(name);
	str.Format("%d", id);
	MessageBox("进程名字为:U_DISK_MFC.exe,进程号为:"+str);
}

二、各个功能实现时的参考资料(站在巨人的肩膀上)

  • 1.鸡啄米
  • 2.创建选择目录
  • https://www.cnblogs.com/greatverve/archive/2012/12/16/SHBrowseForFolder-CFileDialog.html
  • 3.解决”不能将 "const char *" 类型的值分配到 "LPCWSTR" 类型的实体“问题
  • https://blog.csdn.net/whhit111/article/details/69662014
  • 4.检测u盘状态的两种方法,目前尝试了一种,博客没记下来
  • 5.检测u盘容量
  • 6.cstring和string互转   
  • https://www.cnblogs.com/HappyEDay/p/7016162.html
  • 7.删除指定文件或者整个文件夹
  • 指定文件:DeleteFile()函数
  • 文件夹:https://blog.csdn.net/zww0815/article/details/7952780
  • 8.mfc 输出:https://blog.csdn.net/qq_40544338/article/details/86637744
  • 9.copyfile 只是想内容替换,并不是真正意义上的复制过去
  • SHFileOperation复制文件夹、文件用法:可以实现:https://blog.csdn.net/qq_35097289/article/details/80242656
  • 10.u盘禁用与开启(注册表形式):https://iask.sina.com.cn/b/iRGhTczKwuwT.html
  • https://zhidao.baidu.com/question/63937233.html(正在测试)
  • SETUPAPI形式:https://www.cnblogs.com/2018shawn/p/9455069.html
  • 11.设置u盘盘符:https://jingyan.baidu.com/article/f3e34a12e58117f5eb653514.html
  • 12.删除与恢复指定卷标的盘符:https://blog.csdn.net/miromelo/article/details/6040170

三、心得

这个U盘是一个比较简单的小项目,当时准备好好做一做,但是时间紧张,仅用一天时间粗略的完成了这个项目,等以后有时间了再对windows api进行深入了解,对mfc进行深入学习。

通过这个项目只能说锻炼了自己查阅文献的能力,站在巨人的肩膀上。。。感谢以上提到的各位大佬的博客,才能完成这个小项目。但是目前网上的都是vs2010的也不能直接用,这里我用vs2017进行编写,希望这个小项目可以帮助更多的人。

最近事情有点多,以后养成规划时间的习惯,每天都看备忘录,认真规划。加油!

  • 9
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: MFC(Microsoft Foundation Class)是一个基于Windows操作系统的编程框架,提供了一套面向对象的API,使得Windows应用程序的开发更加高效简洁。在MFC获取移动硬盘的USB信息,可以通过以下步骤实现: 1. 获取系统中所有的USB设备信息,可以调用Windows API函数“SetupDiGetClassDevs”和“SetupDiEnumDeviceInterfaces”,并以“GUID_DEVINTERFACE_USB_DEVICE”为参数,可以获得所有USB设备的设备接口。 ``` HDEVINFO hDevInfo; CONST GUID* GUID_CLASS_USB_DEVICE = (LPGUID)&GUID_DEVINTERFACE_USB_DEVICE; hDevInfo = SetupDiGetClassDevs(GUID_CLASS_USB_DEVICE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); ``` 2. 枚举所有设备接口,找到可移动硬盘的USB设备,并获取设备信息,包括设备路径和设备名称等。 ``` SP_DEVICE_INTERFACE_DATA ifdata; ifdata.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); for (int i = 0; SetupDiEnumDeviceInterfaces(hDevInfo, NULL, GUID_CLASS_USB_DEVICE, i, &ifdata); i ++ ) { PSP_DEVICE_INTERFACE_DETAIL_DATA ifDetail; ULONG reqLen = 0; SetupDiGetDeviceInterfaceDetail(hDevInfo, &ifdata, NULL, 0, &reqLen, NULL); ifDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(reqLen); ifDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &ifdata, ifDetail, reqLen, &reqLen, NULL)) { HANDLE hDevice = CreateFile(ifDetail->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); STORAGE_DEVICE_NUMBER sdn; DWORD bytesReturned = 0; if (DeviceIoControl(hDevice, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof(sdn), &bytesReturned, NULL)) { if (sdn.DeviceType == FILE_DEVICE_DISK && sdn.PartitionNumber == 0 && sdn.DeviceNumber > 1) { CString strDeviceName; strDeviceName.Format(_T("\\\\.\\PHYSICALDRIVE%d"), sdn.DeviceNumber); CString strDevicePath(ifDetail->DevicePath); // 可移动硬盘的USB设备信息:设备名称和设备路径 TRACE("Device Name: %s, Device Path: %s\n", strDeviceName, strDevicePath); } } CloseHandle(hDevice); } } ``` 通过上述过程,就可以获取到可移动硬盘的USB信息,包括设备路径和设备名称等。需要注意的是,这里仅仅是一个示例代码,实际情况下还需要根据具体需求做一些优化或修改。 ### 回答2: MFC是一个Microsoft Foundation Class库,可以方便地使用Windows API和Win32 API来开发Windows应用程序。如果要获取移动硬盘的USB信息,可以使用MFC中提供的一组类和函数。 首先,需要调用CWinApp类中的AfxEnableControlContainer函数来启用Windows控件容器。然后,创建一个CComboBox对象,使用其成员函数InitStorage和初始化下拉列表框中的项。接下来,使用函数CSystemTray::Create来创建一个系统托盘图标,监视可移动磁盘的插入和拔出事件。 当检测到可移动磁盘的插入和拔出事件时,可以使用函数WNetGetConnection获取磁盘的网络连接信息。然后,使用函数GetDriveType获取磁盘的驱动器类型,并根据类型来获取该磁盘的标签、序列号和文件系统等信息。 最后,将获取到的USB信息填充到下拉列表框中,让用户可以方便地查看。 总体来说,通过MFC的各种类和函数可以方便地获取移动硬盘的USB信息,可以帮助开发者实现更加智能化和高效的Windows应用程序。 ### 回答3: MFC是微软提供的一种基于面向对象的Windows应用程序框架,它可以帮助程序员快速地开发可视化的Windows应用程序。而要获取移动硬盘的USB信息,可以借助MFC框架中提供的API实现。 首先,我们需要使用MFC中的CWnd类来获取当前系统中所有的窗口句柄。然后,我们可以遍历所有的窗口句柄,使用MFC中的CWnd::GetWindowText()函数来获取当前窗口的标题,判断是否包含“可移动磁盘”等关键字。 如果窗口标题中确实包含可移动磁盘相关的关键字,我们可以使用MFC中的CWnd::SendMessage()函数发送WM_DEVICECHANGE消息到系统中,并且填写相关的消息参数。这样,系统就会重新枚举USB设备,并且我们可以通过使用MFC中的CWnd::RegisterWindowMessage()函数来获取通知消息的具体内容,包括USB设备的插拔状态、设备实例ID等信息。 最后,我们可以将这些获取到的USB信息存储在一个数据结构中,然后进行相关的数据处理和操作。总之,借助MFC框架中提供的API获取移动硬盘的USB信息并不难,只需要对CWnd类的相关函数和消息进行熟练运用即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值