U盘插入时复制
版权说明:欢迎转载,但转载时请注明作者及出处!
这个程序的作用就是当USB可移动设备插入电脑的时候,USB_COPY会提示是否拷贝所有文件到指定目录。
这个程序的主要思路就是当USB设备插入时,系统会发送WM_DEVICECHANGE消息,只要对这个消息进行处理,添加拷贝函数就完成了框架。其他的地方还得细化,下面就来分析这个程序的核心部分。
分析的过程中本程序没有使用的结构体或者函数一笔带过,如果要想了解更多,请参见MSDN--Windows开发最权威的宝典^_^
1、对WM_DEVICECHANGE消息的处理。
WM_DEVICECHANGE消息的wParam参数存储的是设备改变的事件,lParam参数存储的是指向相应的结构体指针。
wParam参数取值如下:
DBT_CONFIGCHANGECANCELED--A request to change the current configuration (dock or undock) has been canceled.
DBT_CONFIGCHANGED--The current configuration has changed, due to a dock or undock.
DBT_CUSTOMEVENT--A custom event has occurred.Windows NT 4.0 and Windows 95: This value is not supported.
DBT_DEVICEARRIVAL--A device or piece of media has been inserted and is now available.
DBT_DEVICEQUERYREMOVE--Permission is requested to remove a device or piece of media. Any application can deny this request and cancel the removal.
DBT_DEVICEQUERYREMOVEFAILED--A request to remove a device or piece of media has been canceled.
DBT_DEVICEREMOVECOMPLETE--A device or piece of media has been removed.
DBT_DEVICEREMOVEPENDING--A device or piece of media is about to be removed. Cannot be denied.
DBT_DEVICETYPESPECIFIC--A device-specific event has occurred.
DBT_DEVNODES_CHANGED--A device has been added to or removed from the system.
Windows NT 4.0 and Windows Me/98/95: This value is not supported.
DBT_QUERYCHANGECONFIG--Permission is requested to change the current configuration (dock or undock).
DBT_USERDEFINED--The meaning of this message is user-defined.
我们需要考虑的就是DBT_DEVICEARRIVAL--系统设备(或者光盘)插入并且变为有效。
lParam参数的取值为指向wParam参数事件的结构体。本程序所用到的为指向DBT_DEVICEARRIVAL事件结构体DEV_BROADCAST_HDR的指针,其他的请参考MSDN。
DEV_BROADCAST_HDR定义如下:
typedef struct _DEV_BROADCAST_HDR {
DWORD dbch_size;//Size of this structure, in bytes.
DWORD dbch_devicetype;//Device type, which determines the event-specific information that follows the first three members.
DWORD dbch_reserved;//Reserved; do not use.
} DEV_BROADCAST_HDR, *PDEV_BROADCAST_HDR;
我们主要使用的就是dbch_devicetype结构成员,取值如下:
DBT_DEVTYP_DEVICEINTERFACE--Class of devices. This structure is a DEV_BROADCAST_DEVICEINTERFACE structure.Windows XP/2000 and Windows Me/98: This value is not supported.
DBT_DEVTYP_HANDLE--File system handle. This structure is a DEV_BROADCAST_HANDLE structure.Windows XP/2000 and Windows Me/98: This value is not supported.
DBT_DEVTYP_OEM--OEM- or IHV-defined device type. This structure is a DEV_BROADCAST_OEM structure.
DBT_DEVTYP_PORT--Port device (serial or parallel). This structure is a DEV_BROADCAST_PORT structure.
DBT_DEVTYP_VOLUME--Logical volume. This structure is a DEV_BROADCAST_VOLUME structure.
我们用到的是DBT_DEVTYP_VOLUME成员,这个成员说明插入的是逻辑卷,也就是磁盘(光盘)。
好了,介绍了这么些关于WM_DEVICECHANGE消息的结构体,下面来看具体实现代码:
/* 系统设备改变事件处理函数 OnDeviceChange
参数:
wParam: 设备改变的事件
lParam: 指向相应的结构体指针
*/
LRESULT CUSBCopyDlg::OnDeviceChange(WPARAM wParam, LPARAM lParam)
{
CString strMobileDriver;
switch (wParam)
{
case DBT_DEVICEARRIVAL://系统设备插入并且变为有效
DEV_BROADCAST_HDR *stHDR;
stHDR = (DEV_BROADCAST_HDR *)lParam;
switch(stHDR->dbch_devicetype)//判断设备类型
{
case DBT_DEVTYP_VOLUME://逻辑卷标
strMobileDriver = GetMobileDrive();//取得可移动磁盘盘符存储在strMobileDriver成员函数中
if (strMobileDriver )
strSourcePath = strMobileDriver + "\\*.*";
else
break;
break;
}
default : //别的事件
break;
}
return 0;
}
2、CopyFile和GetMobileDrive函数--注
2.1 CopyFile函数
这个函数中使用了SHFILEOPSTRUCT结构体,这个结构体定义如下:
typedef struct _SHFILEOPSTRUCT {
HWND hwnd;//要显示文件复制状态的Windows对话框句柄
UINT wFunc;//指示文件操作方式(复制、删除、移动、重命名)
LPCTSTR pFrom;//源文件地址(支持通配符)
LPCTSTR pTo;//目的地址(支持通配符)
FILEOP_FLAGS fFlags;//控制文件操作的标志
BOOL fAnyOperationsAborted;//是否接受用户中断文件操作
LPVOID hNameMappings;//名字映射
LPCTSTR lpszProgressTitle;//文件操作进度对话框题头
} SHFILEOPSTRUCT, *LPSHFILEOPSTRUCT;
在这个本程序中,只需设置wFunc为FO_COPY(文件复制),pFrom,pTo,fAnyOperationsAborted为FALSE(既不允许用户中断文件操作),fFlags根据参数设置为FOF_SILENT(不现实进度对话框)或者为NULL;
填写好SHFILEOPSTRUCT结构体之后,就调用SHFileOperation函数进行文件操作,该函数定义如下:
int SHFileOperation(
LPSHFILEOPSTRUCT lpFileOp //指向SHFILEOPSTRUCT结构体的指针
);
具体代码:
/* 文件拷贝函数CopyFile
参数:
Hint: 文件复制结束是否提示
CopyEnable: 是否允许复制文件
SourcePath: 文件源地址
DestinationPath:文件目的地址
返回值:
true: 复制成功
false: 复制失败
*/
bool CUSBCopyDlg::CopyFile(bool Hint, bool CopyEnable, CString SourcePath, CString DestinationPath)
{
if (CopyEnable)
{
CString DirName = GetDirectoryName(); //取得复制到的目录名称
CreateDirectory(DestinationPath + '\\' + DirName , NULL);//创建目录
SHFILEOPSTRUCT FileOP; //声明文件操作结构体
FileOP.hwnd = m_hWnd; //句柄
if (bProgress)
FileOP.fFlags = NULL;
else
FileOP.fFlags = FOF_SILENT ; //操作标志位
FileOP.wFunc = FO_COPY; //操方式
FileOP.pFrom = SourcePath; //源地址
FileOP.pTo = DestinationPath + '\\' + DirName; //目的地址
FileOP.fAnyOperationsAborted = false; //是否允许中断操作
FileOP.hNameMappings = NULL;
FileOP.lpszProgressTitle = NULL;
int MSG = SHFileOperation(&FileOP); //执行复制操作
if (MSG == 0) //复制成功
{
CopyEnable = true;
bResult = true;
if(Hint)
MessageBox("复制成功!" , "提示" , MB_OK | MB_ICONINFORMATION);
}
else //复制失败
{
bResult = false;
if (Hint)
MessageBox("复制失败!" , "提示" , MB_OK | MB_ICONERROR);
}
}
return bResult;
}
2.2GetMoblieDriver函数
这个函数使用了GetLogicalDrives函数取得系统所有盘符:
DWORD GetLogicalDrives(void);
函数的返回值是当前系统所有有效磁盘驱动器的位掩码(如00000011111)。
位掩码中位置为0的位如果为1,那么代表驱动器A存在,位置为1的位如果为1,那么代表驱动器B存在,位置为2的位如果为1,那么代表驱动器C存在,以此类推。
然后调用GetDriveType函数来寻找是否存在可移动磁盘:
UINT GetDriveType(
LPCTSTR lpRootPathName //盘符(X:),一定要有冒号!
);
GetDriveType函数的返回值为:
DRIVE_UNKNOWN--The drive type cannot be determined.
DRIVE_NO_ROOT_DIR--The root path is invalid. For example, no volume is mounted at the path.
DRIVE_REMOVABLE--The disk can be removed from the drive.
DRIVE_FIXED--The disk cannot be removed from the drive.
DRIVE_REMOTE--The drive is a remote (network) drive.
DRIVE_CDROM--The drive is a CD-ROM drive.
DRIVE_RAMDISK--The drive is a RAM disk.
USB移动存储器是DRIVE_REMOVABLE,我们只要判断GetDriverType返回的那个盘符的类型是不是DRIVE_REMOVABLE类型就可以了。
具体代码:
/* 取得可移动磁盘盘符函数GetMobileDrive(void)
参数:
无
返回值:
盘符的字符串(如C:、D:等)
*/
CString CUSBCopyDlg::GetMobileDrive(void)
{
CString strDriver;
DWORD id = GetLogicalDrives();
for (int i = 1; i < 26; i++)
{
if ((id & (1 << i)) != 0)
{
CString strDrv = CString(char('A' + i)) + ":";
if (GetDriveType(strDrv) == DRIVE_REMOVABLE)
strDriver = strDrv;
}
}
return strDriver;
}
3、显示目录浏览对话框
这个函数使用了BROWSEINFO结构体:
typedef struct _browseinfo {
HWND hwndOwner;//所属Windows窗体句柄
LPCITEMIDLIST pidlRoot;//默认选择的文件夹
LPTSTR pszDisplayName;//接受用户选择目录路径的字符串
LPCTSTR lpszTitle;//对话框标题
UINT ulFlags;//对话框选项标记
BFFCALLBACK lpfn;//回调函数
LPARAM lParam;//回调函数的参数
int iImage;//接受关联到选择的文件夹的图像变量(为索引值)
} BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO;
然后调用SHBrowseForFolder函数:
LPITEMIDLIST SHBrowseForFolder(
LPBROWSEINFO lpbi //指向BROWSEINFO结构体指针
);
返回指向选项的标示符表,这个标示符表存储了所选择的文件夹的路径
调用SHGetPathFromIDList函数提取存储在LPITEMIDLIST中的文件夹路径
BOOL SHGetPathFromIDList(
LPCITEMIDLIST pidl, //LPITEMIDLIST地址
LPTSTR pszPath //接受路径的地址指针
);
成功返回TRUE。
具体代码:
void CConfigDlg::OnBnClickedBrower()
{
char buf[MAX_PATH];
char Value[MAX_PATH];
BROWSEINFO bi; //选择目录对话框结构体
bi.hwndOwner = m_hWnd; //所属窗口
bi.iImage = 0;
bi.lParam = 0; //回调函数参数
bi.lpfn = NULL; //回调函数
bi.lpszTitle = "请选择保存目录";
bi.pidlRoot = NULL;
bi.pszDisplayName = buf;
bi.ulFlags = BIF_RETURNONLYFSDIRS; //标记位
LPITEMIDLIST pItemIDList = SHBrowseForFolder(&bi); //显示对话框
if ( pItemIDList ) //点击确定
{
SHGetPathFromIDList(pItemIDList, buf);
m_CtrlDirPath = buf;
}
else //关闭对话框或者点击取消
MessageBox("您没有选择保存目录,将使用默认路径" , "提示" , MB_OK | MB_ICONINFORMATION);
UpdateData(false);
}
不足:插入多个U盘可能出现错误,原因在于获取盘符时只获得第一个。
后记:
我正在考虑把它改写成课件拷贝软件(基本原理、核心不变)。在学校老师的课件都拷贝到电脑一个盘上,下课同学们都去拷课件,插上U盘后到硬盘里找课件,然后复制,粘贴到U盘里。我想把这个小程序改写一下,把老师的课件存放情况弄成一个列表,然后当同学的U盘插上后,自动弹出课件信息列表的对话框,同学选择一个或多个老师的课件然后按确定,就自动拷贝到U盘了。