教程二: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盘是否存在并获取盘符
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进行编写,希望这个小项目可以帮助更多的人。
最近事情有点多,以后养成规划时间的习惯,每天都看备忘录,认真规划。加油!