MSDN 2005 -> Win32 和 COM 开发 -> User Interface -> Windows User Experience -> Windows Shell -> Windows Shell -> Shell Programmer's Guide -> Shell Basics -> Managing the File System
翻译完这篇文章后,对《浏览名字空间》一文的示例程序进行了修改,增加在右键单击列表项时弹出菜单,提供剪切、复制、粘贴和删除功能。每次粘贴后,会把所有目标文件(文件夹)添加到当前用户的最近文档列表中,从开始菜单中选择“文档”命令就可以看到这个列表。主要使用的API为SHFileOperation、SHAddToRecentDocs和SHChangeNotify。程序修改后新的界面如下:
主要代码为:
1 在弹出右键菜单前更新菜单项状态
int CShellNamespace::UpdateContextMenu(HMENU hMenu) { BOOL bRet; TCHAR* pNames; // 如果当前有选中的文件系统对象,则启用剪切/复制/删除,否则禁用 pNames = GetSelectedItemNames(); EnableMenuItem(hMenu,IDC_CUT, MF_BYCOMMAND | (pNames ? MF_ENABLED : MF_GRAYED)); EnableMenuItem(hMenu,IDC_COPY, MF_BYCOMMAND | (pNames ? MF_ENABLED : MF_GRAYED)); EnableMenuItem(hMenu,IDC_DELETE,MF_BYCOMMAND | (pNames ? MF_ENABLED : MF_GRAYED)); delete []pNames; // 先前进行过剪切/复制操作,且当前文件夹是文件系统文件夹时,启用"粘贴",否则变灰 bRet = FALSE; if (NULL != m_pSaveObjNames) { TCHAR szCurPath[MAX_PATH]; bRet = SHGetPathFromIDList(m_pIdl,szCurPath); } EnableMenuItem(hMenu,IDC_PASTE,MF_BYCOMMAND | (bRet ? MF_ENABLED : MF_GRAYED)); return 0; } |
GetSelectedItemNames()用于根据当前选中项生成SHFileOperation所要求格式的文件名列表,即多个以空字符结束的文件名字符串串接在一起,最后再多加一个空字符,表示整个列表的结束。如果没有选中任何项,GetSelectedItemNames()会返回空,此时使菜单中的剪切、复制、删除三项变灰。
SHGetPathFromIDList(m_pIdl,szCurPath)会判断当前文件夹是否是文件系统文件夹。如果不是文件系统文件夹,就不可能是粘贴目标,需要使菜单中的粘贴项变灰。
2 剪切和复制
int CShellNamespace::Cut() { if (m_pSaveObjNames) delete []m_pSaveObjNames; m_pSaveObjNames = GetSelectedItemNames(); m_bCutFlag = TRUE; return 0; } int CShellNamespace::Copy() { if (m_pSaveObjNames) delete []m_pSaveObjNames; m_pSaveObjNames = GetSelectedItemNames(); m_bCutFlag = FALSE; return 0; } |
执行剪切和复制命令时只是保存选中项的文件名列表,真正的操作要到粘贴时才进行。
3 粘贴
int CShellNamespace::Paste() { SHFILEOPSTRUCT opStruct={0}; TCHAR szDstDir[MAX_PATH + 10]={0}; if (!SHGetPathFromIDList(m_pIdl,szDstDir)) return -1; opStruct.hwnd = GetParent(m_pListCtrl->m_hWnd); opStruct.wFunc = (m_bCutFlag ? FO_MOVE : FO_COPY); opStruct.pFrom = m_pSaveObjNames; opStruct.pTo = szDstDir; opStruct.fFlags = FOF_SIMPLEPROGRESS; SHFileOperation(&opStruct); // 所有目标文件(夹)添加到最近文档列表 AddDstObjectsToRecentDocs(szDstDir); // 目标文件夹内容已经改变 SHChangeNotify(SHCNE_UPDATEDIR,SHCNF_IDLIST,m_pIdl,0); // 源文件夹内容已经改变 if (m_bCutFlag) { TCHAR szSrcDir[MAX_PATH]; StrCpy(szSrcDir,m_pSaveObjNames); PathRemoveBackslash(szSrcDir); PathRemoveFileSpec(szSrcDir); SHChangeNotify(SHCNE_UPDATEDIR,SHCNF_PATH,szSrcDir,0); // 剪切后,源文件(夹)已经不存在了,不能再次粘贴 delete []m_pSaveObjNames; m_pSaveObjNames = NULL; } RefreshList(); return 0; }
|
代码很简单,主要是调用SHFileOperation执行文件和文件夹的移动和复制操作。AddDstObjectsToRecentDocs()把所有目标文件和文件夹添加到当前用户的最近文档列表中,会在下面介绍。执行粘贴操作后,目标文件夹增加了新的内容;如果先前执行的是剪切操作,则源文件夹减少了一些内容。这时应该调用SHChangeNotify通知Shell名字空间已经发生的变化。
4 删除
int CShellNamespace::Delete() { SHFILEOPSTRUCT opStruct={0}; TCHAR* pDelObjNames; pDelObjNames = GetSelectedItemNames(); if (NULL == pDelObjNames) return -1; opStruct.hwnd = GetParent(m_pListCtrl->m_hWnd); opStruct.wFunc = FO_DELETE; opStruct.pFrom = pDelObjNames; opStruct.fFlags = FOF_SIMPLEPROGRESS; SHFileOperation(&opStruct); // 当前文件夹内容已经发生变化 SHChangeNotify(SHCNE_UPDATEDIR,SHCNF_IDLIST,m_pIdl,0); RefreshList(); delete []pDelObjNames; return 0; } |
与移动和复制文件操作类似,只是SHFILEOPSTRUCT结构体的wFunc成员值是FO_DELETE,表示删除对象。
5 添加粘贴的目标文件和文件夹到用户最近文档列表中
void CShellNamespace::AddDstObjectsToRecentDocs(const TCHAR* pszDstDir) { const TCHAR* pNextItem; TCHAR szObjName[MAX_PATH]; TCHAR szDstObjName[MAX_PATH]; pNextItem = m_pSaveObjNames; while (_T('/0') != *pNextItem) { StrCpy(szObjName,pNextItem); PathStripPath(szObjName); StrCpy(szDstObjName,pszDstDir); PathAddBackslash(szDstObjName); StrCat(szDstObjName,szObjName); SHAddToRecentDocs(SHARD_PATH,szDstObjName); pNextItem += (lstrlen(pNextItem) + 1); } } |
主要是调用SHAddToRecentDocs添加名字空间对象的链接到用户的最近文档列表中。m_pSaveObjNames是粘贴的源文件和文件夹列表,注意其格式为:多个以空字符结束的文件名字符串串接在一起,最后再多加一个空字符,表示整个列表的结束。
翻译的文档原文见本文第二部分
这就是
菊子曰啦!