文章目录
前言
MFC实现对U盘的管理并用图形化界面展示出来。
一、利用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