在VC中如何获取系统的目录树,即整个系统的树结构,如下所示:
桌面
|
我的电脑
|-C:盘
   |-文件夹1
   |-文件夹2
  
|-D:盘
   |-文件夹1
   |-文件夹2
.
.
.
.

解决方案 »

  1.   

    下面这个类从CTreeView拜生.参考了DELPHI的相应控件源代码写的,将你的类从此类拜生,就能做你想做的了. 该类只导出两个函数:
    1.SetCurPath :  设置选中一个路径.如果需要的话,它会展开相应有文件夹
    2.GetSelectedDir : 返回当前选中的文件夹,没有选中的话,返回自然就是空串//////////////////////////////////////////////////////////////////////////
    // ShellFolderView.h  ----   头文件
    #if !defined(AFX_SHELLFOLDERVIEW_H__D30D10ED_3D11_4873_9909_146B8E18BEB1__INCLUDED_)
    #define AFX_SHELLFOLDERVIEW_H__D30D10ED_3D11_4873_9909_146B8E18BEB1__INCLUDED_#if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000
    // ShellFolderView.h : header file
    //**********************************************************************
    // 该类根据DELPHI相应控件源代码改编
    /***********************************************************************
     简介:
     CShellFolderView从CTreeView拜生,它显示整个WINDOWS的文件目录树.在WINDOWS中
     又叫外壳名字空间(Shell Name Space).外壳名字空间是Windows下的标准文件系统,
     它大大扩展了Dos文件系统,形成了以“桌面”(Desktop)为根的单一的文件系统
     树,原有的C盘、D盘等目录树变成了“我的电脑”这一外壳名字空间子树的下一级
     子树,而像“控制面板”、“回收站”、“网上邻居”等应用程序及“打印机”等设
     备也被虚拟成了外壳名字空间中的节点。另外,与DOS中物理存储只能和文件系统
     项一一对应这一点不同的是,一个实际目录在外壳名字空间中可以表现为不同的项。
     例如“我的文档”与“C:\My Documents”其实都指向“C:\My Documents”目录,但
     它们在外壳名字空间中是不同的项。 外壳名字空间下的路径: PIDL
     PIDL是一个元素类型为ITEMIDLIST结构的数组,数组中元素的个数是未知的,但紧接
     着数组末尾的必是一个双字节的零。每个数组元素代表了外壳名字空间树中的一层(
     即一个文件夹或文件),数组中的前一元素代表的是后一元素的父文件夹。由此可见,
     PIDL实际上就是指向一块由若干个顺序排列的ITEMIDLIST结构组成、并在最后有一个
     双字节零的空间的指针。所以PIDL的类型就被Windows定义为ITEMIDLIST结构的指针。 PIDL亦有“绝对路径”与“相对路径”的概念。表示“相对路径”的PIDL只有一个
     ITEMIDLIST结构的元素,用于标识相对于父文件夹的“路径”;表示“绝对路径”的
     PIDL(简称为“绝对PIDL”)有若干个ITEMIDLIST结构的元素,第一个元素表示外壳
     名字空间根文件夹(“桌面”)下的某一子文件夹A,第二个元素则表示文件夹A下的
     某一子文件夹B,其余依此类推。这样绝对PIDL就通过保存一条从“桌面”下的直接子
     文件夹或文件的绝对PIDL与相对PIDL是相同的,而其他的文件夹或文件的相对PIDL就
     只是其绝对PIDL的最后一部分了。由于所有的PIDL都是从桌面下的某一个子文件夹开
     始的,所以对于桌面本身来说,它的PIDL数组显然一个元素都没有。这样就只剩下PIDL
     数组最后的那个双字节的零了。所以,“桌面”的PIDL就是一个16位的零。  **********************************************************************/
    // 监视指定的文件夹的变化的类. 该类由CShellFolderView专用,所以定义在源文件中
    // 不对外部公开,在这里进行提前声明,因为下面要用到
    class  CFolderChangeMonitor;  // 外壳名字空间下结点的串表示
    typedef struct _FILEPATHLIST
    {
     char cNormal[MAX_PATH];     // 绝对名称(如,"我的电脑")
     char cForParsing[MAX_PATH]; // 用来分析解释的名称, 可以根据它来产生PIDL
     char cInFolder[MAX_PATH];   // 相对名称
    } FILEPATHLIST, *LPFILEPATHLIST;// 文件夹的分类
    typedef enum emFolderType{
     ftFolder,               // 文件夹
     ftNetworkNeighborhood,  // 网上邻居
     ftRecycleBin,           // 回收站
     ftMyComputer,           // 我的电脑
     ftDesktop,              // 桌面
     ftNetNode,              // 网络结点
     ftNone                  // 空
    }FOLDERTYPE;// 路径信息
    typedef struct _PATHINFO{
         char           cPath[MAX_PATH];  // 路径
         char           cText[MAX_PATH];  // 名称
         UINT           uIconIndex;       // 图标索引
         UINT           uSelectedIndex;   // 选中时的图标索引
         LPITEMIDLIST   pidl;             // PIDL
    }PATHINFO,*LPPATHINFO;// 结点数据
    typedef struct _FOLDERNODE{
     char          cPath[MAX_PATH];   // 路径
     BOOL          bHasParent;        // 是否有父结点
     LONG          Vol_Ser;           // 卷标
     ULONG         dwAttributes;      // 属性
     FOLDERTYPE    fType;             // 类型
     IShellFolder* pShellFolder;      // 指向该文件夹的IShellFolder接口
     LPITEMIDLIST  pidl;              // 文件夹的PIDL
     WORD          widlen;            // PIDL的长度
     CFolderChangeMonitor*  pMonitorThread; // 监视文件夹变化的线程
    }FOLDERNODE, *LPFOLDERNODE;
    /////////////////////////////////////////////////////////////////////////////
    // CShellFolderView viewclass CShellFolderView : public CTreeView
    {
    protected:
     CShellFolderView();           // protected constructor used by dynamic creation
     DECLARE_DYNCREATE(CShellFolderView)// Attributes
    public:// Operations
    public:
     BOOL SetCurPath(LPCTSTR lpszPath);    // 设置当前选中的文件夹, 成功返回TRUE
     CString GetSelectedDir();             // 取得当前选中的文件夹,没有则返回一个空串// Overrides
     // ClassWizard generated virtual function overrides
     //{{AFX_VIRTUAL(CShellFolderView)
     protected:
     //virtual void OnDraw(CDC* pDC);      // overridden to draw this view
     //}}AFX_VIRTUAL// Implementation
    protected:
     virtual ~CShellFolderView();
    #ifdef _DEBUG
     virtual void AssertValid() const;
     virtual void Dump(CDumpContext& dc) const;
    #endif // Generated message map functions
    protected:
     //{{AFX_MSG(CShellFolderView)
     afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
     afx_msg void OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult);
     afx_msg void OnDestroy();
     //}}AFX_MSG
     afx_msg LRESULT OnFolderChanged(WPARAM wParam, LPARAM); // 处理文件夹发生变化的消息
     DECLARE_MESSAGE_MAP()
    private:
     IMalloc*      m_pMalloc;      // COM内存分配接口用来分配和回收PIDL使用的空间
    protected:
     void GetSpecialFolder(int nFolder, LPPATHINFO  lppi); // 取得指定的特殊文件夹的信息
     void GetNetHood(LPPATHINFO lpPI);        // 取得网上邻居结点
     // 创建一个结点
     HTREEITEM CreateFolderNode(LPITEMIDLIST lpidl, HTREEITEM hParent, BOOL bVirtual);
     CString GetPath(HTREEITEM hNode);  // 取得结点的对应的路径
     BOOL AttachFolders(HTREEITEM hNode);  // 展开指定的结点下的子结点
     LONG GetVol_Ser(LPCTSTR strDriver);   // 获取卷标
     void ShowDesktop();                   // 显示桌面
     void RetrieveSysImageList();          // 获取系统图像列表
     void FreeNode(HTREEITEM hNode);       // 释放结点
     void FreeNodeData(LPFOLDERNODE lpfn); // 释放结点数据
     int  Expand(HTREEITEM hNode, UINT nCode); // 展开结点};
      

  2.   

    ///////////////////////////////////////////////////////////////////////////////{{AFX_INSERT_LOCATION}}
    // Microsoft Visual C++ will insert additional declarations immediately before the previous line.#endif // !defined(AFX_SHELLFOLDERVIEW_H__D30D10ED_3D11_4873_9909_146B8E18BEB1__INCLUDED_) ///////////////////////////////////////////////////////////////////////////
    // ShellFolderView.cpp : implementation file
    ///////////////////////////////////////////////////////////////////////////#include "stdafx.h"
    #include "ShellFolderView.h"#ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif#define  WM_U_FOLDERCHANGED   (WM_USER+215)  // 消息// 一些固定的名字:
    static const char c_cMyComputer[] = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}";  // Normal = 我的电脑
    static const char c_cNetworkNeighborhood[] = "::{208D2C60-3AEA-1069-A2D7-08002B30309D}";  // Normal = 网上邻居
    static const char c_cRecycleBin[] = "::{645FF040-5081-101B-9F08-00AA002F954E}";  // Normal = 回收站
    static const char c_cController[] = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}";  // Normal = 控制面板
    static const char c_cPrinter[] = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}";  // Normal = 打印机
    static const char c_cDial[] = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}";  // Normal = 网络和拨号连接
    static const char c_cFonts[] = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{D20EA4E1-3957-11D2-A40B-0C5020524152}";  // Normal = 字体
    static const char c_cManager[] = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{D20EA4E1-3957-11D2-A40B-0C5020524153}";  // Normal = 管理工具
    static const char c_cPlans[] = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{D6277990-4C6A-11CF-8D87-00AA0060F5BF}";  // Normal = 任务计划// 文件夹监视线程类
    class  CFolderChangeMonitor
    {
    public:
     CFolderChangeMonitor(LPCTSTR lpszPath, HWND hWnd, WPARAM wParam);
     ~CFolderChangeMonitor();private:
     CWinThread* m_pThread;
     CString     m_strPath;
     HWND        m_hWnd;
     WPARAM      m_wParam;
     BOOL        m_bStop; friend UINT MonitorProc(void* lp);
     void Run();
    };/////////////////////////////////////////////////////////////////////////////
    // CFolderChangeMonitor class
    /////////////////////////////////////////////////////////////////////////////
    // 文件夹监视线程类的构造函数
    // lpszPath : 被监视的文件夹
    // hWnd     : 文件夹已经改变的消息将发往该窗口
    // wParam   : 消息的参数,在这里是树上的一结点句柄
    CFolderChangeMonitor::CFolderChangeMonitor(LPCTSTR lpszPath, HWND hWnd, WPARAM wParam)
    :m_hWnd(hWnd), m_wParam(wParam), m_strPath(lpszPath)
    {
     m_bStop = FALSE;
     // 开始监视线程
     m_pThread = AfxBeginThread(MonitorProc, (void*)this);
    }CFolderChangeMonitor::~CFolderChangeMonitor()
    {
     // 停止监视线程
     m_bStop = TRUE;
     // 原来可以等待线程结束后才返回,后来不知道怎么搞的,居然会死锁,只好先不等待了,嘻嘻
    // ::WaitForSingleObject(m_pThread->m_hThread, INFINITE);
    }// 监视线程函数
    UINT MonitorProc(void*lp)
    {
     if(lp)
     {
      // 将线程反调回类中
      ((CFolderChangeMonitor*)lp)->Run();
     }
     return 0;
    }// 监视函数
    void CFolderChangeMonitor::Run()
    {
     // 检测结束标志, 没有就一直循环
     while(!m_bStop)
     {
      // 创建事件句柄,当文件夹发生了指定的变化时,该事件被触发
      HANDLE handle = ::FindFirstChangeNotification(m_strPath, FALSE,
       FILE_NOTIFY_CHANGE_DIR_NAME);
      // 如果事件创建不成功则结束
      if(handle == INVALID_HANDLE_VALUE) break;  // 开始监视
      if(::FindNextChangeNotification(handle))
      {
       int i = WAIT_TIMEOUT;
       // 除非指明结束监视或者发生了一次事件,否则一直循环等待
       while(i == WAIT_TIMEOUT && !m_bStop)
       {
        // 等待事件发生(每次等待20mS)因为线程需要不断检测m_bStop
        // 标志,以便及时退出,所以不能无限制地等待下去
        i = ::WaitForSingleObject(handle, 20);
       }
       // 循环结束,如果不是指定结束,则一定是发事件被触发了
       if(!m_bStop)
       {
        // 发送消息通知窗口来更新目录树
        ::SendMessage(m_hWnd, WM_U_FOLDERCHANGED, m_wParam,0);
       }
      }
      else
      {
       // 监视不能开始,则退出
       ::FindCloseChangeNotification(handle);
       break;
      }
      // 关闭事件
      ::FindCloseChangeNotification(handle);
     }
    }
    /***************************************************************
      FUNCTION : GetName  描述:lpio lpsf所指的IshellFolder接口代表的文件夹下的相对PIDL,
      本函数获得lpi所指项的显示名称,dwFlags表明欲得到的显示名称类型,
      lpFriendlyName为存放显示名称的缓冲区。
     ***************************************************************/BOOL GetName(LPSHELLFOLDER lpsf,LPITEMIDLIST lpi,DWORD dwFlags,LPSTR lpFriendlyName)
    {
     STRRET str;
    AGAIN:
     //得到显示名称
     if(NOERROR!=lpsf->GetDisplayNameOf(lpi,dwFlags,&str))
      return FALSE; //根据返回值进行转换
     switch(str.uType)
     {
     case STRRET_WSTR: //如为Unicode字符串,则转成Ansi字符集的字符串case STRRET_WSTR:
      if(str.pOleStr != NULL)
       WideCharToMultiByte(CP_ACP,0,str.pOleStr,-1,lpFriendlyName,MAX_PATH,NULL,NULL);
      else if(dwFlags == SHGDN_NORMAL)
      {
        dwFlags = SHGDN_FORPARSING;
        goto AGAIN;
      }
      break;
     case STRRET_OFFSET: //如为偏移量,则去除偏移量
      lstrcpy(lpFriendlyName,(LPSTR)lpi+str.uOffset);
      break;
     case STRRET_CSTR: // 如为Ansi字符串,则直接拷贝
      lstrcpy(lpFriendlyName,(LPSTR)str.cStr);
      break;
     default: //非法情况
      return FALSE; } return TRUE;
    }// 获取路径
    void GetLongFilePath(IShellFolder* pShellFolder, LPITEMIDLIST  lpidl, LPFILEPATHLIST lpfpl)
    {
     BOOL bRelease = FALSE;
     // 如果IShellFolder接口为空,则使用桌面的IShellFolder接口
     if(pShellFolder == NULL)
     {
      if(SHGetDesktopFolder(&pShellFolder) != NOERROR)
       return;
      bRelease = TRUE;
     }
     // 取得显示, PIDL及相对名称
     GetName(pShellFolder, lpidl, SHGDN_NORMAL,     lpfpl->cNormal);
     GetName(pShellFolder, lpidl, SHGDN_FORPARSING, lpfpl->cForParsing);
     GetName(pShellFolder, lpidl, SHGDN_INFOLDER,   lpfpl->cInFolder); // 取得的COM接口一定要释放
     if(bRelease) pShellFolder->Release();
    }
      

  3.   


    /////////////////////////////////////////////////////////////////////////////
    // CShellFolderViewIMPLEMENT_DYNCREATE(CShellFolderView, CTreeView)CShellFolderView::CShellFolderView()
    {
     // 取得内存分配的COM接口, 这里一定要成功,否则接下来的程序中无法分配空间
     VERIFY(SHGetMalloc(&m_pMalloc)==NOERROR);
    }CShellFolderView::~CShellFolderView()
    {
     // 释放IMalloc接口
     m_pMalloc->Release();
    }
    BEGIN_MESSAGE_MAP(CShellFolderView, CTreeView)
     //{{AFX_MSG_MAP(CShellFolderView)
     ON_WM_CREATE()
     ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemexpanding)
     ON_WM_DESTROY()
     //}}AFX_MSG_MAP
     ON_MESSAGE(WM_U_FOLDERCHANGED, OnFolderChanged)
    END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////
    // CShellFolderView drawing//void CShellFolderView::OnDraw(CDC* pDC)
    //{
    // CDocument* pDoc = GetDocument();
     // TODO: add draw code here
    //}/////////////////////////////////////////////////////////////////////////////
    // CShellFolderView diagnostics#ifdef _DEBUG
    void CShellFolderView::AssertValid() const
    {
     CTreeView::AssertValid();
    }void CShellFolderView::Dump(CDumpContext& dc) const
    {
     CTreeView::Dump(dc);
    }
    #endif //_DEBUG/////////////////////////////////////////////////////////////////////////////
    // CShellFolderView message handlersint CShellFolderView::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
     if (CTreeView::OnCreate(lpCreateStruct) == -1)
      return -1;
     
     // TODO: Add your specialized creation code here
     // 设置树控件的风格:带有展开按钮,结点间有连线
     GetTreeCtrl().ModifyStyle(0, TVS_HASLINES|TVS_HASBUTTONS|TVS_LINESATROOT);
     RetrieveSysImageList(); // 取得系统图标列表
     // 显示桌面虚拟文件夹
     ShowDesktop(); return 0;
    }// 当某个结点将要被展开前
    void CShellFolderView::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult)
    {
     NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
     // TODO: Add your control notification handler code here
     // 如果是展开操作
     if(pNMTreeView->action == TVE_EXPAND)
     {
      // 如果被展开的结点还没有子结点则为该结点附加它的子结点
      // 我们不能一开始就将所有的结点添加上去,这将是一个漫长的过程,且浪费
      // 大量的系统资源,只有那些被展开的结点才需要显示其子结点
      if(GetTreeCtrl().GetChildItem(pNMTreeView->itemNew.hItem) == NULL)
      {
       AttachFolders(pNMTreeView->itemNew.hItem);
      }
     }
     *pResult = 0;
     *pResult = 0;
    }void CShellFolderView::OnDestroy()
    {
     // 释放所有的结点
     FreeNode(NULL); CTreeView::OnDestroy();
     
    }// 当被监视的文件夹发生变化时,该消息被监视线程发送过来
    LRESULT CShellFolderView::OnFolderChanged(WPARAM wParam, LPARAM)
    {
     // 变化对应的结点句柄
     HTREEITEM   hNode = (HTREEITEM)wParam;
     CTreeCtrl& tree  = GetTreeCtrl();   // 视图上的树控件
     // 保存该结点的状态(是否是已经展开的)
     BOOL bExpanded = tree.GetItemState(hNode, TVIS_EXPANDED) == TVIS_EXPANDED;
     // 它的子结点
     HTREEITEM hChild = tree.GetChildItem(hNode);
     // 删除该结点下的所有子结点
     while(hChild != NULL)
     {
      HTREEITEM  hCur = hChild;
      hChild = tree.GetNextSiblingItem(hChild); // 下一个子结点
      FreeNode(hCur);
      tree.DeleteItem(hCur);
     } // 重新生成子结点.
     AttachFolders(hNode);
     
     // 恢复展开状态(如果以前是展开的)
     if(bExpanded) tree.Expand(hNode, TVE_EXPAND); return 0;
    }
      

  4.   


    //////////////////////////////////////////////////////////////////
    // protected member function// 获取系统图标列表
    void CShellFolderView::RetrieveSysImageList()
    {
     PATHINFO     DesktopInfo;
     SHFILEINFO   FileInfo;
     CString      strDesktop;
     GetSpecialFolder(CSIDL_DESKTOPDIRECTORY, &DesktopInfo);
     // SHGetFileInfo函数的返回值就是系统图标列表的句柄(可参考MSDN)
     HIMAGELIST   hSysImageList = (HIMAGELIST)::SHGetFileInfo(
            (LPCTSTR)DesktopInfo.pidl, 0, &FileInfo, sizeof(FileInfo),
            SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
     // 设置重叠图标
     // 设置1号重叠图标对应图标列表中索引为0的图标,这是一个手托,象征被共享的文件夹
     // 设置2号重叠图标对应图标列表中索引为1的图标,这是一个箭头,象征快捷方式
     // 设置3号重叠图标对应图标列表中索引为2的图标,这个图标我研究了好长时间也没看出来是个什么东东
     ImageList_SetOverlayImage(hSysImageList, 0,1);
     ImageList_SetOverlayImage(hSysImageList, 1,2);
     ImageList_SetOverlayImage(hSysImageList, 2,3);
     // 树控件就使用这个图标列表
     GetTreeCtrl().SetImageList(CImageList::FromHandle(hSysImageList),TVSIL_NORMAL);}// 获取特殊的文件夹信息
    void CShellFolderView::GetSpecialFolder(int nFolder, LPPATHINFO lppi)
    {
     LPITEMIDLIST   lpidl = NULL;
     // 取得该文件夹的PIDL
     if(SUCCEEDED(SHGetSpecialFolderLocation(m_hWnd, nFolder, &lpidl)))
     {
      lppi->pidl = lpidl;  // Get the actual path of the directory from the PItemIDList
      SHGetPathFromIDList(lpidl, lppi->cPath); // Do it  // Get the Normal Image index
      SHFILEINFO   shfi;
      SHGetFileInfo((LPCTSTR)lpidl, SFGAO_SHARE, &shfi, sizeof(shfi),
         SHGFI_PIDL | SHGFI_SYSICONINDEX);
      lppi->uIconIndex = shfi.iIcon;  // Get the selected image index
      SHGetFileInfo((LPCTSTR)lpidl, SFGAO_SHARE, &shfi, sizeof(shfi),
         SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_OPENICON);
      lppi->uSelectedIndex = shfi.iIcon;
     }
    }void CShellFolderView::GetNetHood(LPPATHINFO lpPI)
    {
     FILEPATHLIST  fpl;
     GetSpecialFolder(CSIDL_NETWORK, lpPI);
     GetLongFilePath(NULL, lpPI->pidl, &fpl);
     strcpy(lpPI->cText, fpl.cNormal);
    }// CreateFolderNode creates a Folder_Node and inserts it under
    // the "parent" node (if any), using the last of the path
    // string as the Name, and setting the new node's text
    // property to match.
    HTREEITEM CShellFolderView::CreateFolderNode(
       LPITEMIDLIST lpidl,        // 文件夹的PIDL
                HTREEITEM    hParent,      // 父结点
                BOOL         bVirtual)     // 是否包含虚拟文件夹
    {
     CTreeCtrl&    tree = GetTreeCtrl();
     LPFOLDERNODE  lpfnParent = NULL;
     FILEPATHLIST  fpl; // 父结点的信息
     if(hParent != NULL)
     {
      lpfnParent = (LPFOLDERNODE)tree.GetItemData(hParent);
      GetLongFilePath(lpfnParent->pShellFolder, lpidl, &fpl);
     }
     else
     {
      GetLongFilePath(NULL, lpidl, &fpl);
     } CString strFileName(fpl.cInFolder); // 相对名字
     CString strPath(fpl.cForParsing);   // 法定名字 // 以"::"开头的,是一些特殊的文件夹,普通的目录或文件的名称中是不能包含":"字符的
     // 打印出来看看都是些什么东东
     if(strncmp(fpl.cForParsing, "::", 2) ==0)
     {
      TRACE3("<%s> = \"%s\";  // Normal = %s\n", fpl.cInFolder, fpl.cForParsing, fpl.cNormal);
     } // 取得属性
     ULONG  Attributes = SFGAO_SHARE | SFGAO_FILESYSTEM |
          SFGAO_LINK | SFGAO_HASSUBFOLDER;
     if(lpfnParent == NULL ||
      lpfnParent->pShellFolder->GetAttributesOf(
       1, (LPCITEMIDLIST*)&lpidl, &Attributes) != NOERROR)
     {
      Attributes = 0;
      //lpfnParent = NULL;
     } // 是否是一个虚拟文件夹
     BOOL bVirFolder = ((Attributes & SFGAO_FILESYSTEM) == 0); if(lpfnParent != NULL)
     {
      switch(lpfnParent->fType)
      {
      case ftNetworkNeighborhood:
      case ftNetNode:
       bVirFolder = TRUE;
      }
     }
     if(!bVirtual && bVirFolder)
      return NULL; // 生成该文件夹的绝对PIDL,方法就是将相对PIDL附加到父PIDL后
     WORD         wParentPIDLLen = 0;
     LPITEMIDLIST lpidlParent    = NULL;
     if(lpfnParent != NULL)
     {
      wParentPIDLLen = lpfnParent->widlen;
      lpidlParent    = lpfnParent->pidl;
     } WORD pidlen = lpidl->mkid.cb;
     // 使用IMalloc接口分配新PIDL需要的空间.因为从系统API返回的PIDL
     // 都是通过IMalloc接口分配的空间,所以为了统一,这里也使用IMalloc来分配空间
     LPITEMIDLIST  lpidlNew = (LPITEMIDLIST)m_pMalloc->Alloc(pidlen + wParentPIDLLen + 2); if(wParentPIDLLen != 0)
     {
      memcpy(lpidlNew, lpidlParent, wParentPIDLLen);
      memcpy((char*)lpidlNew+wParentPIDLLen, lpidl,pidlen);
     }
     else
     {
      memcpy(lpidlNew, lpidl, pidlen);
     } *(WORD*)((char*)lpidlNew + wParentPIDLLen + pidlen) = 0; LPITEMIDLIST lpidlTemp = /*bVirFolder ? lpidl : */lpidlNew; // 获取图标索引
     SHFILEINFO  shfi;
     UINT   uIcon, uSelectedIcon;
     ::SHGetFileInfo((LPCTSTR)lpidlTemp, SFGAO_SHARE, &shfi, sizeof(shfi),
        SHGFI_PIDL | SHGFI_SYSICONINDEX);
     uIcon = uSelectedIcon = shfi.iIcon;
     //if((Attributes & SFGAO_FOLDER) == SFGAO_FOLDER)
     {
      ::SHGetFileInfo((LPCTSTR)lpidlTemp, SFGAO_SHARE, &shfi, sizeof(shfi),
         SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_OPENICON);
      uSelectedIcon = shfi.iIcon;
     } // 一个新的结点
     LPFOLDERNODE  lpNewNode = new FOLDERNODE;
     lpNewNode->widlen    = wParentPIDLLen + pidlen;
     lpNewNode->pidl      = lpidlNew;
     strcpy(lpNewNode->cPath, (LPCTSTR)strPath);
     lpNewNode->bHasParent = (hParent != NULL);
     //lpNewNode->pChangeThread = NULL;
     //lpNewNode->VolSer      = 1;
     // 结点的类型(只要和几个固定的名字比较一下啦)
     if(strPath.Compare(c_cMyComputer) == 0)
      lpNewNode->fType = ftMyComputer;
     else if(strPath.Compare(c_cNetworkNeighborhood) == 0)
      lpNewNode->fType = ftNetworkNeighborhood;
     else if(strPath.Compare(c_cRecycleBin) == 0)
      lpNewNode->fType = ftRecycleBin;
     else
      lpNewNode->fType     = ftFolder;
     lpNewNode->dwAttributes = Attributes;
     lpNewNode->pMonitorThread = NULL; // 获取该文件夹对应的IShellFolder接口
     // If this Node has no parent, it mut be the Desktop.
     if(lpfnParent != NULL)
     {
      lpfnParent->pShellFolder->BindToObject(lpidl, NULL, IID_IShellFolder,
           (void**)&lpNewNode->pShellFolder);
      //if(lpfnParent->fType == ftNetworkNeighborhood)
      //{
      // lpNewNode->fType = ftNetNode;
      // sprintf(lpNewNode->cPath, "\\\\%s", (LPCTSTR)strFileName);
      //} }
     else
     {
      // 桌面的IShellFolder
      ::SHGetDesktopFolder(&lpNewNode->pShellFolder);
     } // 插入树结点
     TVINSERTSTRUCT  tvis;
     tvis.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE |
         TVIF_STATE | TVIF_PARAM;
     // 如果该文件夹包含子文件夹,则对应的树结点也应该有子结点
     if(Attributes & SFGAO_HASSUBFOLDER)
     {
      tvis.item.mask |= TVIF_CHILDREN;
      // 使用 I_CHILDRENCALLBACK 值告诉控件,该结点有子结点,但具体的结点还没给出
      // 当该结点被展开时就会通知父窗口.这时你应该为该结点添加子结点
      tvis.item.cChildren = I_CHILDRENCALLBACK;
     } // 设置覆盖图标
     if(Attributes & SFGAO_SHARE)      // 共享的
      tvis.item.state = INDEXTOOVERLAYMASK(1);
     else if(Attributes & SFGAO_LINK)  // 快捷方式
      tvis.item.state = INDEXTOOVERLAYMASK(2);
     else                              // 其它的
      tvis.item.state = INDEXTOOVERLAYMASK(0); tvis.item.stateMask = TVIS_OVERLAYMASK;  // 指明状太标志包含覆盖图标
     tvis.item.iImage = uIcon;                // 正常图标
     tvis.item.iSelectedImage = uSelectedIcon;// 选中时的图标
     //CString strTemp = strFileName + "(" + strPath + ")"; // 看看它的完整路径
     tvis.item.pszText = (char*)fpl.cNormal;  // 标题
     tvis.hParent = hParent;                  // 父结点
     tvis.hInsertAfter = TVI_LAST;            // 插入到其它兄弟结点的后面
     tvis.item.lParam = (DWORD)lpNewNode;     // 自定义的结点数据 HTREEITEM hIns = tree.InsertItem(&tvis);  // 插入该结点 if(hIns == NULL)   // 插入失败??不大可能的事
      FreeNodeData(lpNewNode); return hIns;       // 返回新结点句柄
    }
      

  5.   


    // GetPath returns the path name for true folders, and a null string
    // for virtual folders.
    CString CShellFolderView::GetPath(HTREEITEM hNode)
    {
     CString strPath(""); if(hNode == NULL) return strPath; // 对于虚拟文件夹返回空串
     switch(((LPFOLDERNODE)(GetTreeCtrl().GetItemData(hNode)))->fType)
     {
     case ftNetworkNeighborhood:
     case ftNetNode:
     case ftRecycleBin:
     case ftMyComputer:
      return strPath;
     } // 对于真正的文件夹返回绝对路径
     strPath = ((LPFOLDERNODE)(GetTreeCtrl().GetItemData(hNode)))->cPath; if(strPath.GetLength() > 0)         // If path is null, it's a virtual folder
     {
      if(strPath.GetLength() < 3)     // If the result is less than 3 bytes,
       strPath += "\\";            //  It's the root -- add a "\".
     } return strPath;
    }// 对目录进行排序的回调函数
    // The lParam1 and lParam2 parameters correspond to the lParam member
    //      of the TVITEM structure for the two items being compared.
    // The lParamSort parameter corresponds to the lParam member of this structure.static int CALLBACK  ShortItemProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
    {
     CTreeCtrl* pCtrl = (CTreeCtrl*) lParamSort; LPFOLDERNODE lpfn1 = (LPFOLDERNODE)lParam1;
     LPFOLDERNODE lpfn2 = (LPFOLDERNODE)lParam2;
     if(lpfn1 == NULL || lpfn2 == NULL) return 0; // 虚拟文件夹排在前面,并且不改变原顺序
     if((lpfn1->dwAttributes & SFGAO_FILESYSTEM) == 0 &&
      (lpfn2->dwAttributes & SFGAO_FILESYSTEM) == 0)
      return 0;
     else if((lpfn1->dwAttributes & SFGAO_FILESYSTEM) == 0)
      return -1;
     else if((lpfn2->dwAttributes & SFGAO_FILESYSTEM)==0)
      return 1;
     // 真正的文件夹排在虚拟文件夹之后,且按字母顺序(不区分大小写)
     else if((lpfn1->dwAttributes & SFGAO_FOLDER) &&
      (lpfn1->dwAttributes & SFGAO_FOLDER))
      return stricmp(lpfn1->cPath, lpfn2->cPath);
     else if(lpfn1->dwAttributes & SFGAO_FOLDER)
      return -1;
     else if(lpfn2->dwAttributes & SFGAO_FOLDER)
      return 1;
     else
      // 其它的排在最后面,按字母序(不区分大小写)
      return stricmp(lpfn1->cPath, lpfn2->cPath);
    }
    // AttachFolders unconditionally attaches child folders to the
    // node passed in Node. If there are any, it also starts a thread
    // to watch for changes in the tree structure.
    BOOL CShellFolderView::AttachFolders(HTREEITEM hNode)
    {
     CWaitCursor   cur; // 显示等待光标
     BOOL  bRet = FALSE; BOOL bChildren = TRUE;
     CTreeCtrl& tree = GetTreeCtrl();
     tree.SetRedraw(FALSE);    // 禁止控件更新窗口,以免插入时闪烁
     //tree.EnableWindow(FALSE); CString strPath = GetPath(hNode); // The full path for the node.
     LPFOLDERNODE  lpfn = (LPFOLDERNODE)tree.GetItemData(hNode); // If the node being expanded is a root, get the volume serial number
     // and lop off the backslash.
     if(strPath.GetLength() <= 3)
     {
      lpfn->Vol_Ser = GetVol_Ser(strPath);
     } // Enumerate the folder's contents.
     IEnumIDList*    pEnum = NULL;
     if(lpfn->pShellFolder->EnumObjects(m_hWnd, SHCONTF_FOLDERS | SHCONTF_INCLUDEHIDDEN,
      &pEnum) == NOERROR)
     {
      bChildren = FALSE;
      pEnum->Reset();      // Reset the enumerate interface
      ULONG   u = 1;       // Enumerate context.
      LPITEMIDLIST   lpidlChild = NULL;
      while(pEnum->Next(1, &lpidlChild, &u) == NOERROR)
      {
       // 为每个PIDL创建对应的树结点(包含其中的虚拟文件夹)
       HTREEITEM  hChild = CreateFolderNode(lpidlChild, hNode, TRUE);
       // 释放PIDL
       m_pMalloc->Free(lpidlChild);
       if(hChild != NULL)
       {
        // 调整结点的类型
        bChildren = TRUE;
        LPFOLDERNODE  lpfnChild = (LPFOLDERNODE)tree.GetItemData(hChild);
        switch(lpfnChild->fType)
        {
        case ftMyComputer:
        case ftNetNode:
         lpfnChild->Vol_Ser = GetVol_Ser(lpfnChild->cPath);
         break;
        default:
         lpfnChild->Vol_Ser = lpfn->Vol_Ser;
         bRet = TRUE;
         break;
        }
       }
      }
      // 释放枚举接口
      pEnum->Release();
     }
     // 调整父结点的属性
     TVITEM tvi;
     tvi.mask = TVIF_CHILDREN;
     tvi.cChildren = bChildren?1:0;
     tvi.hItem = hNode;
     tree.SetItem(&tvi); // Alphabetize the nodes, unless the parent is MyComputer or
     // Network Neighborhood.
     switch(lpfn->fType)
     {
     case ftMyComputer:
     case ftNetworkNeighborhood:
      break;
     default:{   // 排序所有子结点(根据给定的回调函数)
      TVSORTCB tvs;
      tvs.hParent = hNode;
      tvs.lpfnCompare = ShortItemProc;
      tvs.lParam = (LPARAM)&tree;
      tree.SortChildrenCB(&tvs);
      }break;
     } if(bRet)
     {
      // 如果还没有的话,则启动一个线程,开始监视该文件夹的变化
      if(lpfn->pMonitorThread == NULL)
      {
       lpfn->pMonitorThread = new CFolderChangeMonitor(strPath, m_hWnd, (WPARAM)hNode);
      }
     } // 可以更新窗口了
     tree.SetRedraw(TRUE);
    // tree.EnableWindow(TRUE);
     return bRet;
    }
    // This routine is used only for diskettes. It returns the volume serial
    // number of a disk in the drive--if there is no disk there, it returns
    // -1. It's used whenever the user clicks on a node, to determine whether
    // he has swapped diskettes, necessitating a discard of all nodes for the
    // affected drive.
    // For volumes other than diskettes, it returns a dummy value of 1,
    // as -1 is used to signify a diskette drive that isn't loaded.
    LONG CShellFolderView::GetVol_Ser(LPCTSTR strDriver)
    {
     LONG result = -1; // If there is no path it's My Computer -- exit now
     if(strlen(strDriver) == 0)
      return result; // If the driver isn't removeable, we dont have to do this
     CString strPath = strDriver[0] + ":\\";
     strPath.MakeLower(); if(::GetDriveType(strPath) != DRIVE_REMOVABLE)
     {
      result = 1;
      return result;
     }
     // I f there's no disk in the drive, bail out with a result = -1.
     DWORD dwVSN, dwMCL, dwFSF;
     if(GetVolumeInformation(strPath, NULL, 0, &dwVSN, &dwMCL, &dwFSF, NULL, 0))
     {
      result = (LONG) dwVSN;
     }
     else
      result = -1;
     return result;
    }
    // ShowDesktop is performed when Create posts a WM_SHOWDESKTOP
    // message, so we see the correct value of fFolderOptions.
    void CShellFolderView::ShowDesktop()
    {
     CTreeCtrl&  tree = GetTreeCtrl(); PATHINFO  PathInfo;
     GetSpecialFolder(CSIDL_DESKTOP, &PathInfo);
     //strDesktopPath = PathInfo.cPath; // 创建桌面这个根结点
     HTREEITEM hDesktop = CreateFolderNode(PathInfo.pidl, NULL, TRUE);
     LPFOLDERNODE lpfn = (LPFOLDERNODE)tree.GetItemData(hDesktop);
     FILEPATHLIST  fpl;
     GetLongFilePath(lpfn->pShellFolder, lpfn->pidl, &fpl);
     tree.SetItemText(hDesktop, fpl.cNormal);
     strcpy(lpfn->cPath , PathInfo.cPath);
     lpfn->fType = ftDesktop; // 展开桌面
     AttachFolders(hDesktop); tree.Expand(hDesktop, TVE_EXPAND);
    }// 展开指定的结点,如果需要的话,附加创建其下子结点
    int CShellFolderView::Expand(HTREEITEM hNode, UINT nCode)
    {
     if(GetTreeCtrl().GetChildItem(hNode) == NULL)
     {
      AttachFolders(hNode);
     }
     return GetTreeCtrl().Expand(hNode, nCode);
    }
      

  6.   


    // 将路径转化为PIDL,如果失败,返回NULL
    LPITEMIDLIST  PathToIDList(LPCTSTR lpszPath)
    {
     OLECHAR  szOleStr[MAX_PATH];
     IShellFolder* pDesktop = NULL;
     LPITEMIDLIST  lpidl = NULL;
     // 使用桌面的IShellFolder来转换,这将生成一个绝对的PIDL
     if(SHGetDesktopFolder(&pDesktop) != NOERROR)
      return NULL; // 将路径转换成宽字符格式
     MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED, lpszPath, -1,
         szOleStr, sizeof(szOleStr));
     // 转换路径
     pDesktop->ParseDisplayName(NULL, NULL, szOleStr, NULL, &lpidl, NULL); // 释放接口
     pDesktop->Release(); return lpidl;
    }// PIDL中的下一个结构
    inline LPITEMIDLIST PIDL_Next(LPITEMIDLIST p)
    {
     return (LPITEMIDLIST)((DWORD)p + p->mkid.cb);
    }// 求指定的PIDL的长度,不包括结尾的两个字节的0
    inline int PIDL_Len(LPITEMIDLIST p)
    {
     WORD  wLen = 0;
     // 累加所有的单元结构尺寸
     while(p && p->mkid.cb != 0)
     {
      wLen += p->mkid.cb;
      p = PIDL_Next(p);
     }
     return wLen;
    }
    // 设置当前目录
    BOOL CShellFolderView::SetCurPath(LPCTSTR lpszPath)
    {
     // 解释路径为PIDL
     LPITEMIDLIST lpidl = PathToIDList(lpszPath);
     if(lpidl == NULL) return FALSE; CTreeCtrl&  tree = GetTreeCtrl();
     // 从桌面结点开始搜索
     HTREEITEM  hDesktop = tree.GetRootItem();
     if(hDesktop == NULL){
      m_pMalloc->Free(lpidl);
      return FALSE;
     } HTREEITEM  hCur = hDesktop;
     WORD wPart = 0;
     LPITEMIDLIST  lpidlPart = lpidl;
     // 对于每一个存在的结点,搜索其下的子结点
     while(hCur != NULL && lpidlPart->mkid.cb != 0)
     {
      // 展开该结点
      Expand(hCur,TVE_EXPAND);
      wPart += lpidlPart->mkid.cb;
      HTREEITEM hChild = tree.GetChildItem(hCur);
      // 如果子结点存在,则:
      while (hChild != NULL)
      {
       // 取得结点数据
       LPFOLDERNODE lpfn = (LPFOLDERNODE)tree.GetItemData(hChild);
       // 如果PIDL的长度匹配的话,则比较这两个PIDL
       // PIDL的匹配长度按搜索深度层次而增加,即,第一次搜索匹配一个结构的长度,第二次
       // 则匹配两个结构的长...
       if(lpfn && PIDL_Len(lpfn->pidl) == wPart)
       {
        if(memcmp(lpidl, lpfn->pidl, wPart) == 0)
        {
         // 匹配成功
         break;
        }
       }
       hChild = tree.GetNextSiblingItem(hChild); // 下一个结点
      }
      hCur = hChild;
      lpidlPart = PIDL_Next(lpidlPart); // PIDL的下一个结构
     }
     // 如果子结点存在,则匹配成功. 选中该结点
     if(hCur != NULL)
     {
      tree.SelectItem(hCur);
      tree.EnsureVisible(hCur);
     } m_pMalloc->Free(lpidl); return hCur != NULL;
    }// 释放结点数据
    void CShellFolderView::FreeNodeData(LPFOLDERNODE lpfn)
    {
     if(lpfn)
     {
      m_pMalloc->Free(lpfn->pidl);  // 释放PIDL
      if(lpfn->pShellFolder)        // 释放IShellFolder接口
       lpfn->pShellFolder->Release();
      if(lpfn->pMonitorThread != NULL) // 删除监视线程
       delete lpfn->pMonitorThread;
      delete  lpfn;                 // 删除该结点
     }
    }void CShellFolderView::FreeNode(HTREEITEM hNode)
    {
     CTreeCtrl& tree = GetTreeCtrl();
     if(hNode == NULL)
     {
      // 空的参数指明,将删除所有结点的数据
      hNode = tree.GetRootItem();
     }
     else
     {
      // 删除本结点数据
      LPFOLDERNODE lpfn = (LPFOLDERNODE)tree.GetItemData(hNode);
      FreeNodeData(lpfn);
      // 结点指向子结点
      hNode = tree.GetChildItem(hNode);
     }
     // 删除所有子结点的数据
     while(hNode != NULL)
     {
      FreeNode(hNode);
      hNode = tree.GetNextSiblingItem(hNode);
     }
    }// 返回当前选中的路径(没有则返回空串)
    CString CShellFolderView::GetSelectedDir()
    {
     HTREEITEM hSel = GetTreeCtrl().GetSelectedItem();
     if(hSel == NULL) return "";
     return ((LPFOLDERNODE)GetTreeCtrl().GetItemData(hSel))->cPath;
    }
      

  7.   

    给个连接吧。 
    codeproject / shell 里面有个 explorer demo,也挺好的。不过是 WTL的