实现功能:

通过使用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进行编写,希望这个小项目可以帮助更多的人。

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