www.pudn.com > sharewareluncher.zip > BROWSER.C


// BROWSER.C 
 
// Stefano Maruzzi 1995 
// LIST16-xx 
 
 
#include  
#include  
#include  
#include  
#include  
#include "globals.h" 
#include "resource.h" 
#include "treeview.h" 
#include "listview.h" 
 
 
 
// function prototypes 
HWND CreateTreeView(HWND hwndParent, HINSTANCE hInstance, int iID, DWORD dwStyle) ; 
HWND WINAPI CreateListView(HWND hwndParent, HINSTANCE hInstance) ; 
void FillTreeView( HWND hwndTreeView, LPSHELLFOLDER lpsf, LPITEMIDLIST  lpiFullQualified, HTREEITEM hParent) ; 
BOOL PopulateListView( HWND hwndLV, LPTVITEMDATA lptvid, LPSHELLFOLDER lpsf) ; 
BOOL GetName( LPSHELLFOLDER lpsf, LPITEMIDLIST  lpi, DWORD dwFlags, LPSTR lpFriendlyName) ; 
void SwitchView( HWND hwndLV, DWORD dwView) ; 
LPITEMIDLIST GetFullyQualPidl( LPSHELLFOLDER lpsf, LPITEMIDLIST lpi) ; 
LPDROPSOURCE CreateDropSource( void) ; 
LRESULT DispDefault( EDWP, HWND, UINT, WPARAM, LPARAM) ; 
BOOL GetName( LPSHELLFOLDER lpsf, LPITEMIDLIST lpi, DWORD dwFlags, LPSTR lpFriendlyName) ; 
int GetIcon( LPITEMIDLIST lpi, UINT uFlags) ; 
LPITEMIDLIST ConcatPidls( LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) ; 
LPITEMIDLIST CopyITEMID( LPMALLOC lpMalloc, LPITEMIDLIST lpi) ; 
LPITEMIDLIST ConcatPidls( LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) ; 
LPITEMIDLIST GetFullyQualPidl( LPSHELLFOLDER lpsf, LPITEMIDLIST lpi) ; 
LPITEMIDLIST CopyITEMID( LPMALLOC lpMalloc, LPITEMIDLIST lpi) ; 
LRESULT WINAPI WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) ; 
BOOL DoTheMenuThing( HWND hwnd, LPSHELLFOLDER lpsfParent, LPITEMIDLIST lpi, LPPOINT lppt) ; 
 
LRESULT WINAPI WndProc(	HWND hwnd, 
						UINT msg, 
						WPARAM wParam, 
						LPARAM lParam) 
{ 
	static HINSTANCE hInstance ; 
	static HWND hwndTreeView ; 
	static HWND hwndLV ; 
	static int cxSplitter ; 
	static BOOL bOutaHere = FALSE ; 
	static RECT rcSplit ; 
	static HDC hdcSplit = NULL ; 
 
 
	switch( msg) 
	{ 
		case WM_CREATE: 
		{ 
				// Save this value for later 
				cxSplitter = GetSystemMetrics(SM_CXEDGE) ; 
				if( !cxSplitter) 
					cxSplitter = 2; 
 
				hwndLV = CreateListView(hwnd, hInstance) ; 
 
				// Post a message to fill the control, or nix app if the creation failed. 
				if( !hwndTreeView || !hwndLV) 
					PostMessage(hwndTreeView, WM_COMMAND, MN_EXIT, 0 ) ; 
				else 
					PostMessage( hwndTreeView, WM_COMMAND, MN_FILL, 0 ) ; 
			} 
				break ; 
 
		case WM_LBUTTONDOWN: 
		{ 
			#define xPos ((int)(short)LOWORD(lParam)) 
 
			SetCapture(hwnd) ; 
 
			GetClientRect(hwnd, &rcSplit) ; 
 
			// Calculate initial position 
			rcSplit.left = min(max(50, xPos - cxSplitter/2), rcSplit.right - 50) + 1; 
 
			// Get a DC (also used as a flag indicating we have capture) 
			if( hdcSplit) 
				ReleaseDC( hwnd, hdcSplit) ; 
			hdcSplit = GetDC( hwnd) ; 
 
			// draw splitter bar in initial position 
			PatBlt( hdcSplit, rcSplit.left, 0, cxSplitter, rcSplit.bottom, DSTINVERT) ; 
		} 
			break ; 
 
		case WM_NOTIFY: 
		{ 
		   POINT pt; 
		   NM_TREEVIEW *pnmtv = (NM_TREEVIEW *)lParam; 
		   NM_LISTVIEW *pnmlv = (NM_LISTVIEW *)lParam; 
		   TV_HITTESTINFO tvhti ; 
		   LV_HITTESTINFO lvhti ; 
		   LPTVITEMDATA lptvid ; 
		   LPLVITEMDATA lplvid ; 
		   LPDATAOBJECT lpdo ; 
		   HRESULT hr ; 
		   LPMALLOC lpMalloc ; 
		   LPSHELLFOLDER lpsf2 = 0 ; 
		   TV_ITEM tvi ; 
		   LV_ITEM lvi ; 
		   DWORD dwEffect ; 
		   static char szBuff[ MAX_PATH] ; 
 
		   switch( pnmtv -> hdr.idFrom) 
		   { 
					case TVN_SELCHANGED: 
					{ 
					   // do this only if we are not exiting the application... 
					   if( !bOutaHere) 
					   { 
						  lptvid = (LPTVITEMDATA)pnmtv -> itemNew.lParam; 
						  hr = lptvid -> lpsfParent -> lpVtbl -> BindToObject(	lptvid -> lpsfParent, 
																				lptvid -> lpi, 
																				0, 
																				&IID_IShellFolder, 
																				(LPVOID *)&lpsf2) ; 
 
						  if( SUCCEEDED(hr)) 
						  { 
							 PopulateListView( hwndLV, lptvid, lpsf2) ; 
 
							 lpsf2 -> lpVtbl -> Release( lpsf2) ; 
						  } 
					   } 
					} 
					   break ; 
 
					case TVN_ITEMEXPANDING: 
					{ 
					   if( ( pnmtv -> itemNew.state & TVIS_EXPANDEDONCE)) 
						  break ; 
 
					   lptvid = (LPTVITEMDATA)pnmtv -> itemNew.lParam ; 
 
					   hr=lptvid -> lpsfParent -> lpVtbl -> BindToObject(lptvid -> lpsfParent, 
																   lptvid -> lpi, 
																   0, 
																   &IID_IShellFolder, 
																   (LPVOID *)&lpsf2) ; 
 
					   if( SUCCEEDED(hr)) 
					   { 
						  FillTreeView(hwndTreeView, 
									   lpsf2, 
									   lptvid -> lpifq, 
									   pnmtv -> itemNew.hItem) ; 
					   } 
 
					   TreeView_SortChildren(hwndTreeView, pnmtv -> itemNew.hItem, FALSE) ; 
					} 
					   break; 
 
					   default: 
						   break; 
				 } 
				 break ; 
 
			  case IDD_LISTVIEW: 
				 switch( pnmlv -> hdr.code) 
				 { 
					case NM_RCLICK: 
					case NM_DBLCLK: 
 
					   GetCursorPos((LPPOINT)&pt) ; 
					   ScreenToClient(hwndLV, &pt) ; 
					   lvhti.pt=pt; 
					   ListView_HitTest(hwndLV, &lvhti) ; 
					   if( lvhti.flags & LVHT_ONITEM) 
					   { 
						   ClientToScreen(hwndLV, &pt) ; 
						   lvi.mask = LVIF_PARAM; 
						   lvi.iItem = lvhti.iItem; 
						   lvi.iSubItem = 0; 
 
						   if( !ListView_GetItem(hwndLV, &lvi)) 
							  return 0; 
 
						   lplvid=(LPLVITEMDATA)lvi.lParam; 
 
						   if( pnmlv -> hdr.code==NM_RCLICK) 
							  DoTheMenuThing(hwnd, lplvid -> lpsfParent, lplvid -> lpi, &pt) ; 
						   else 
						   { 
							  if( !(lplvid -> ulAttribs & SFGAO_FOLDER)) 
							  { 
								 SHELLEXECUTEINFO sei = 
								 { 
									sizeof(SHELLEXECUTEINFO), 
									SEE_MASK_INVOKEIDLIST,	// fMask 
									hwnd,                  // hwnd of parent 
									"Open",                // lpVerb 
									NULL,	                // lpFile 
									"", 
									"",                     // lpDirectory 
									SW_SHOWNORMAL,	         // nShow 
									hInstance,                  // hInstApp 
									(LPVOID)NULL,           // lpIDList...will set below 
									NULL,                   // lpClass 
									0,			             // hkeyClass 
									0,				         // dwHotKey 
									NULL			         // hIcon 
								 }; 
 
								 sei.lpIDList=GetFullyQualPidl(lplvid -> lpsfParent, lplvid -> lpi) ; 
 
								 ShellExecuteEx(&sei) ; 
							  } 
							  else 
							  { 
								 MessageBox(hwnd, "Clicked on folder", "ENUMDESK", MB_OK) ; 
							  } 
						   } 
					   } 
					   break; 
 
					case LVN_DELETEITEM: 
					   // let's free the memory for the ListView item data... 
 
					   hr=SHGetMalloc(&lpMalloc) ; 
					   if( FAILED(hr)) 
						  break; 
 
					   lvi.mask = LVIF_PARAM; 
					   lvi.iItem = pnmlv -> iItem; 
					   lvi.iSubItem = 0; 
 
					   if( !ListView_GetItem(hwndLV, &lvi)) 
						  return 0; 
					   lplvid=(LPLVITEMDATA)lvi.lParam; 
 
					   lplvid -> lpsfParent -> lpVtbl -> Release(lplvid -> lpsfParent) ; 
					   lpMalloc -> lpVtbl -> Free(lpMalloc, lplvid -> lpi) ; 
					   lpMalloc -> lpVtbl -> Free(lpMalloc, lplvid) ; 
					   lpMalloc -> lpVtbl -> Release(lpMalloc) ; 
					   break; 
					default: 
					   break; 
				 } 
 
				 break; 
 
			  default: 
				 break; 
   } 
 
 
		case WM_MOUSEMOVE: 
		{ 
			int cx = (int)LOWORD( lParam) ; 
 
			if( hdcSplit) 
			{ 
				// Erase previous bar 
				PatBlt( hdcSplit, rcSplit.left, 0, cxSplitter, rcSplit.bottom, DSTINVERT) ; 
 
				// Calculate new position 
				rcSplit.left = min( max( 50, cx - cxSplitter / 2), rcSplit.right - 50) + 1 ; 
 
				// Draw bar in new position 
				PatBlt(hdcSplit, rcSplit.left, 0, cxSplitter, rcSplit.bottom, DSTINVERT) ; 
			} 
		} 
			break ; 
 
		case WM_LBUTTONUP: 
		{ 
			int cx = ((int)(short)LOWORD( lParam)) ; 
 
			if( hdcSplit) 
			{ 
				// Erase previous bar 
				PatBlt( hdcSplit, rcSplit.left, 0, cxSplitter, rcSplit.bottom, DSTINVERT) ; 
 
				// Calculate new position 
				rcSplit.left = min( max( 50, cx - cxSplitter / 2), rcSplit.right - 50) + 1 ; 
 
				// Clean up 
				ReleaseCapture() ; 
				ReleaseDC( hwnd, hdcSplit) ; 
				hdcSplit = NULL ; 
 
				rcSplit.left -= cxSplitter / 2 ; 
 
				if( hwndTreeView) 
					SetWindowPos(	hwndTreeView, HWND_TOP, 
									0, 0, 
									rcSplit.left, rcSplit.bottom, 
									SWP_NOZORDER) ; 
 
				rcSplit.left += cxSplitter ; 
 
				if( hwndLV) 
					SetWindowPos(	hwndLV, HWND_TOP, 
									rcSplit.left, 0, 
									rcSplit.right - rcSplit.left, rcSplit.bottom, 
									SWP_NOZORDER) ; 
			} 
		} 
			break ; 
 
		case WM_COMMAND: 
			switch( LOWORD( wParam)) 
			{ 
				case MN_FILL: 
				{ 
					LPSHELLFOLDER lpsf = 0 ; 
					LPITEMIDLIST lpi = 0 ; 
					HRESULT hr ; 
 
					hr=SHGetDesktopFolder( &lpsf) ; 
 
					if( SUCCEEDED(hr)) 
					{ 
					   TreeView_DeleteAllItems( hwndTreeView) ; 
 
					   FillTreeView( hwndTreeView, lpsf, NULL, TVI_ROOT) ; 
					} 
 
					if( lpsf) 
						lpsf -> lpVtbl -> Release( lpsf) ; 
				} 
					break ; 
				case MN_LIST: 
				{ 
					SwitchView( CTRL( hwnd, IDD_LISTVIEW), LVS_LIST) ; 
				} 
					break ; 
			} 
			break ; 
 
	} 
} 
	return DefWindowProc( hwnd, msg, wParam, lParam) ; 
} 
 
 
 
 
BOOL DoTheMenuThing(	HWND hwnd, 
						LPSHELLFOLDER lpsfParent, 
						LPITEMIDLIST lpi, 
						LPPOINT lppt) 
{ 
    LPCONTEXTMENU lpcm ; 
    HRESULT hr ; 
    char szTemp[ 64] ; 
    CMINVOKECOMMANDINFO cmi ; 
    DWORD dwAttribs = 0 ; 
    int idCmd ; 
    HMENU hMenu ; 
    BOOL bSuccess = TRUE ; 
 
    hr=lpsfParent -> lpVtbl -> GetUIObjectOf(	lpsfParent, hwnd, 
												1,  //Number of objects to get attributes of 
												&lpi, 
												&IID_IContextMenu, 
												0, 
												(LPVOID *)&lpcm) ; 
    if( SUCCEEDED( hr)) 
    { 
       hMenu = CreatePopupMenu() ; 
 
       if( hMenu) 
       { 
          hr=lpcm -> lpVtbl -> QueryContextMenu(	lpcm, hMenu, 
													0, 
													1, 
													0x7fff, 
													CMF_EXPLORE) ; 
          if( SUCCEEDED( hr)) 
          { 
             idCmd = TrackPopupMenu(	hMenu, 
										TPM_LEFTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON, 
										lppt -> x, 
										lppt -> y, 
										0, 
										hwnd, 
										NULL) ; 
 
             if( idCmd) 
             { 
                cmi.cbSize = sizeof( CMINVOKECOMMANDINFO) ; 
                cmi.fMask = 0 ; 
                cmi.hwnd = hwnd ; 
                cmi.lpVerb = MAKEINTRESOURCE( idCmd - 1) ; 
                cmi.lpParameters = NULL ; 
      	         cmi.lpDirectory = NULL ; 
                cmi.nShow = SW_SHOWNORMAL ; 
                cmi.dwHotKey = 0 ; 
                cmi.hIcon = NULL ; 
                hr=lpcm -> lpVtbl -> InvokeCommand( lpcm, &cmi) ; 
                if( SUCCEEDED(hr)) 
                    ;  //Do nothing 
                else 
                { 
                   MessageBox( hwnd, "InvokeCommand failed!!", "Shell Extension Sample", MB_OK) ; 
                   wsprintf( szTemp, "hr=%lx", hr) ; 
                   MessageBox( hwnd, szTemp, "Shell Extension Sample", MB_OK) ; 
                } 
             } 
 
          } 
          else 
             bSuccess = FALSE ; 
 
          DestroyMenu( hMenu) ; 
       } 
       else 
          bSuccess = FALSE ; 
 
       lpcm -> lpVtbl -> Release( lpcm) ; 
    } 
    else 
    { 
       MessageBox( hwnd, "GetUIObjectOf failed!!", "Shell Extension Sample", MB_OK) ; 
       wsprintf(szTemp, "hr=%lx", hr) ; 
       MessageBox( hwnd, szTemp, "Shell Extension Sample", MB_OK) ; 
 
       bSuccess = FALSE ; 
    } 
    return bSuccess; 
} 
 
 
BOOL WINAPI InitListViewImageLists( HWND hwndLV) 
{ 
    HIMAGELIST himlSmall ; 
    HIMAGELIST himlLarge ; 
    SHFILEINFO sfi ; 
    BOOL bSuccess = TRUE ; 
 
    himlSmall = (HIMAGELIST)SHGetFileInfo((LPCSTR)"C:\\", 
                                           0, 
                                           &sfi, 
                                           sizeof(SHFILEINFO), 
                                           SHGFI_SYSICONINDEX | SHGFI_SMALLICON) ; 
 
    himlLarge = (HIMAGELIST)SHGetFileInfo((LPCSTR)"C:\\", 
                                           0, 
                                           &sfi, 
                                           sizeof(SHFILEINFO), 
                                           SHGFI_SYSICONINDEX | SHGFI_LARGEICON) ; 
 
    if( himlSmall && himlLarge) 
    { 
       ListView_SetImageList(hwndLV, himlSmall, LVSIL_SMALL) ; 
       ListView_SetImageList(hwndLV, himlLarge, LVSIL_NORMAL) ; 
    } 
    else 
       bSuccess = FALSE; 
 
    return bSuccess; 
} 
 
 
BOOL WINAPI InitListViewItems(	HWND hwndLV, 
								LPTVITEMDATA lptvid, 
								LPSHELLFOLDER lpsf) 
{ 
    LV_ITEM lvi ; 
    int iCtr ; 
    HRESULT hr ; 
    LPMALLOC lpMalloc ; 
    LPITEMIDLIST lpifqThisItem ; 
    LPITEMIDLIST lpi = 0 ; 
    LPENUMIDLIST lpe = 0 ; 
    LPLVITEMDATA lplvid ; 
    ULONG ulFetched, ulAttrs ; 
    HWND hwnd=GetParent( hwndLV) ; 
    char szBuff[ MAX_PATH] ; 
 
    lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM ; 
 
    hr=SHGetMalloc( &lpMalloc) ; 
    if( FAILED(hr)) 
       return FALSE ; 
 
    // Hourglass on! 
 
    SetCapture( GetParent( hwndLV)) ; 
    SetCursor( LoadCursor( NULL, IDC_WAIT)) ; 
 
    if( SUCCEEDED( hr)) 
    { 
        hr = lpsf -> lpVtbl -> EnumObjects( lpsf, hwnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &lpe) ; 
 
        if( SUCCEEDED( hr)) 
        { 
            iCtr = 0 ; 
 
            while( S_OK==lpe -> lpVtbl -> Next( lpe, 1, &lpi, &ulFetched)) 
            { 
                // OK, let's get some memory for our ITEMDATA struct 
                lplvid = (LPLVITEMDATA)lpMalloc -> lpVtbl -> Alloc( lpMalloc, sizeof(LVITEMDATA)) ; 
                if( !lplvid) 
                   goto Done ; 
 
                // now get the friendly name that we'll put in the treeview... 
                GetName( lpsf, lpi, SHGDN_NORMAL, szBuff) ; 
 
                lpsf -> lpVtbl -> GetAttributesOf( lpsf, 1, &lpi, &ulAttrs) ; 
                lplvid -> ulAttribs=ulAttrs; 
 
                lpifqThisItem=ConcatPidls(lptvid -> lpifq, lpi) ; 
 
                lvi.iItem = iCtr++ ; 
                lvi.iSubItem = 0 ; 
                lvi.pszText = szBuff ; 
                lvi.cchTextMax = MAX_PATH ; 
                lvi.iImage = GetIcon( lpifqThisItem, SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_SMALLICON) ; 
 
                lplvid -> lpsfParent = lpsf ; 
                lpsf -> lpVtbl -> AddRef( lpsf) ; 
 
                // now, make a copy of the ITEMIDLIST 
                lplvid -> lpi=CopyITEMID( lpMalloc, lpi) ; 
 
                lvi.lParam = (LPARAM)lplvid ; 
 
                // add the item to the listview 
				if( ListView_InsertItem( hwndLV, &lvi) == -1) 
                   return FALSE ; 
 
                lpMalloc -> lpVtbl -> Free( lpMalloc, lpifqThisItem) ; 
                lpifqThisItem = 0 ; 
                lpMalloc -> lpVtbl -> Free( lpMalloc, lpi) ; 
                lpi = 0 ; 
            } 
        } 
 
    } 
 
Done: 
 
    // Hourglass off! 
    ReleaseCapture() ; 
    SetCursor(LoadCursor(NULL, IDC_ARROW)) ; 
 
    if( lpe) 
		lpe -> lpVtbl -> Release( lpe) ; 
 
    //The following 2 if statements will only be TRUE if we got here on an 
    //error condition from the "goto" statement.  Otherwise, we free this memory 
    //at the end of the while loop above. 
    if(  lpi && lpMalloc) 
		lpMalloc -> lpVtbl -> Free(lpMalloc, lpi) ; 
    if(  lpifqThisItem && lpMalloc) 
		lpMalloc -> lpVtbl -> Free(lpMalloc, lpifqThisItem) ; 
 
    if(  lpMalloc) 
		lpMalloc -> lpVtbl -> Release(lpMalloc) ; 
 
    return TRUE ; 
} 
 
 
HWND WINAPI CreateListView( HWND hwndParent, HINSTANCE hInstance) 
{ 
    HWND hwndLV ; 
    RECT rc ; 
 
    // Force the common controls DLL to be loaded. 
    InitCommonControls() ; 
 
    // Create the control. 
    GetClientRect(hwndParent, &rc) ; 
 
    hwndLV = CreateWindowEx(WS_EX_CLIENTEDGE, 
    	                    WC_LISTVIEW, "", 
                            WS_VISIBLE | WS_CHILD | WS_BORDER | LVS_EDITLABELS | 
                            LVS_LIST | LVS_AUTOARRANGE | LVS_SHAREIMAGELISTS, 
                            0, 0,  
                            rc.right-rc.left, rc.bottom-rc.top, 
                            hwndParent, 
                            (HMENU)IDD_LISTVIEW,  
                            hInstance,  
                            NULL) ; 
 
    return hwndLV ; 
} 
 
void SwitchView(HWND hwndLV, DWORD dwView) 
{ 
    DWORD dwStyle = GetWindowLong(hwndLV, GWL_STYLE) ; 
 
    if( (dwStyle & LVS_TYPEMASK) != dwView) 
        SetWindowLong(hwndLV, GWL_STYLE, (dwStyle & ~LVS_TYPEMASK) | dwView) ; 
} 
 
 
int CALLBACK ListViewCompareProc(LPARAM lParam1,  
                                 LPARAM lParam2, 
                                 LPARAM lParamSort) 
{ 
    LPLVITEMDATA lplvid1=(LPLVITEMDATA)lParam1; 
    LPLVITEMDATA lplvid2=(LPLVITEMDATA)lParam2; 
    char      szTemp1[MAX_PATH]; 
    char      szTemp2[MAX_PATH]; 
    int       iResult = 1; 
 
    if( lplvid1 && lplvid2) 
    { 
       if(  (lplvid1 -> ulAttribs & SFGAO_FOLDER) &&  
           !(lplvid2 -> ulAttribs & SFGAO_FOLDER) ) 
              return -1; 
 
       if( !(lplvid1 -> ulAttribs & SFGAO_FOLDER) &&  
            (lplvid2 -> ulAttribs & SFGAO_FOLDER) ) 
              return 1; 
 
       GetName(lplvid1 -> lpsfParent, lplvid1 -> lpi, SHGDN_NORMAL, szTemp1) ; 
       GetName(lplvid2 -> lpsfParent, lplvid2 -> lpi, SHGDN_NORMAL, szTemp2) ; 
 
       iResult = lstrcmpi(szTemp1, szTemp2) ; 
    } 
 
    return iResult; 
} 
 
 
BOOL PopulateListView(HWND hwndLV,  
                      LPTVITEMDATA lptvid, 
                      LPSHELLFOLDER lpsf) 
{ 
    ListView_DeleteAllItems(hwndLV) ; 
 
    if( !InitListViewImageLists(hwndLV) || 
        !InitListViewItems(hwndLV, lptvid, lpsf)) 
    { 
        return FALSE; 
    } 
 
    ListView_SortItems(hwndLV, 
                       ListViewCompareProc, 
                       0) ; 
} 
 
 
LPITEMIDLIST Next(LPCITEMIDLIST pidl) 
{ 
   LPSTR lpMem=(LPSTR)pidl; 
 
   lpMem+=pidl -> mkid.cb; 
 
   return (LPITEMIDLIST)lpMem; 
} 
 
UINT GetSize(LPCITEMIDLIST pidl) 
{ 
    UINT cbTotal = 0; 
    if( pidl) 
    { 
        cbTotal += sizeof(pidl -> mkid.cb) ;       // Null terminator 
        while (pidl -> mkid.cb) 
        { 
            cbTotal += pidl -> mkid.cb; 
            pidl = Next(pidl) ; 
        } 
    } 
 
    return cbTotal; 
} 
 
LPITEMIDLIST Create(UINT cbSize) 
{ 
    LPMALLOC lpMalloc; 
    HRESULT  hr; 
    LPITEMIDLIST pidl=0; 
 
    hr=SHGetMalloc(&lpMalloc) ; 
 
    if( FAILED(hr)) 
       return 0; 
 
    pidl=(LPITEMIDLIST)lpMalloc -> lpVtbl -> Alloc(lpMalloc, cbSize) ; 
 
    if( pidl) 
        _fmemset(pidl, 0, cbSize) ;      // zero-init for external task   alloc 
 
    if( lpMalloc) lpMalloc -> lpVtbl -> Release(lpMalloc) ; 
 
    return pidl; 
} 
 
LPITEMIDLIST ConcatPidls(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2) 
{ 
    LPITEMIDLIST pidlNew; 
    UINT cb1; 
    UINT cb2; 
 
    if( pidl1)  //May be NULL 
       cb1 = GetSize(pidl1) - sizeof(pidl1 -> mkid.cb) ; 
    else 
       cb1 = 0; 
 
    cb2 = GetSize(pidl2) ; 
 
    pidlNew = Create(cb1 + cb2) ; 
    if( pidlNew) 
    { 
        if( pidl1) 
           hmemcpy(pidlNew, pidl1, cb1) ; 
        hmemcpy(((LPSTR)pidlNew) + cb1, pidl2, cb2) ; 
    } 
    return pidlNew; 
} 
 
LPITEMIDLIST CopyITEMID(LPMALLOC lpMalloc, LPITEMIDLIST lpi) 
{ 
   LPITEMIDLIST lpiTemp; 
 
   lpiTemp=(LPITEMIDLIST)lpMalloc -> lpVtbl -> Alloc(lpMalloc, 
                                                 lpi -> mkid.cb+sizeof(lpi -> mkid.cb)) ; 
   CopyMemory((PVOID)lpiTemp, (CONST VOID *)lpi, lpi -> mkid.cb+sizeof(lpi -> mkid.cb)) ; 
 
   return lpiTemp; 
} 
 
BOOL GetName(LPSHELLFOLDER lpsf, 
             LPITEMIDLIST  lpi, 
			 DWORD         dwFlags, 
             LPSTR         lpFriendlyName) 
{ 
   BOOL   bSuccess=TRUE; 
   STRRET str; 
 
   if( NOERROR==lpsf -> lpVtbl -> GetDisplayNameOf(lpsf, 
                                               lpi, 
                                               dwFlags, 
                                               &str)) 
   { 
      switch (str.uType) 
      { 
         case STRRET_WSTR: 
 
            WideCharToMultiByte(CP_ACP,                 // CodePage 
                                0,		               // dwFlags 
                                str.pOleStr,            // lpWideCharStr 
                                -1,                     // cchWideChar 
                                lpFriendlyName,         // lpMultiByteStr 
                                sizeof(lpFriendlyName), // cchMultiByte, 
                                NULL,                   // lpDefaultChar, 
                                NULL) ;                  // lpUsedDefaultChar 
 
             break; 
 
         case STRRET_OFFSET: 
 
             lstrcpy(lpFriendlyName, (LPSTR)lpi+str.uOffset) ; 
             break; 
 
         case STRRET_CSTR: 
              
             lstrcpy(lpFriendlyName, (LPSTR)str.cStr) ; 
             break; 
 
         default: 
             bSuccess = FALSE; 
             break; 
      } 
   } 
   else 
      bSuccess = FALSE; 
 
   return bSuccess; 
} 
 
LPITEMIDLIST GetFullyQualPidl(LPSHELLFOLDER lpsf, LPITEMIDLIST lpi) 
{ 
   char szBuff[MAX_PATH]; 
   OLECHAR szOleChar[MAX_PATH]; 
   LPSHELLFOLDER lpsfDeskTop; 
   LPITEMIDLIST  lpifq; 
   ULONG ulEaten, ulAttribs; 
   HRESULT hr; 
 
   if( !GetName(lpsf, lpi, SHGDN_FORPARSING, szBuff)) 
      return NULL; 
 
   hr=SHGetDesktopFolder(&lpsfDeskTop) ; 
 
   if( FAILED(hr)) 
      return NULL; 
 
   MultiByteToWideChar(CP_ACP, 
					   MB_PRECOMPOSED, 
					   szBuff, 
					   -1, 
					   szOleChar, 
					   sizeof(szOleChar)) ; 
 
   hr=lpsfDeskTop -> lpVtbl -> ParseDisplayName(lpsfDeskTop, 
											NULL, 
											NULL, 
											szOleChar, 
											&ulEaten, 
											&lpifq, 
											&ulAttribs) ; 
 
   lpsfDeskTop -> lpVtbl -> Release(lpsfDeskTop) ; 
 
   if( FAILED(hr)) 
      return NULL; 
 
   return lpifq; 
} 
 
HWND CreateTreeView(HWND hwndParent, HINSTANCE hInstance, int iID, DWORD dwStyle) 
{ 
    RECT rc;      // Client rect of parent 
    HWND hwnd;    // Handle to TreeView 
    SHFILEINFO    sfi; 
    HIMAGELIST    hImageList;         // Handle to systemn ImageList. 
 
    // This registers the TreeView class. 
 
    InitCommonControls() ; 
 
    // Get the client area of the parent. 
 
    GetClientRect(hwndParent, &rc) ; 
 
    // Create the TreeView control. 
 
    hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,         // Ex style 
                          WC_TREEVIEW,              // Class 
                          "",                       // Dummy Text 
                          dwStyle,                  // Style 
                          0, 0,                     // Use all of 
                          rc.right/2, rc.bottom,      // client area. 
                          hwndParent,               // Parent 
                          (HMENU)iID,               // ID 
                          hInstance,                    // Instance 
                          NULL) ;                    // No extra 
 
    // Grab a handle to the system image list, for our icons 
 
    hImageList = (HIMAGELIST)SHGetFileInfo((LPCSTR)"C:\\",  
                                           0, 
                                           &sfi,  
                                           sizeof(SHFILEINFO),  
                                           SHGFI_SYSICONINDEX | SHGFI_SMALLICON) ; 
 
    // Attach ImageList to TreeView 
 
    if( hImageList && hwnd) 
        TreeView_SetImageList(hwnd, hImageList, 0) ; 
 
    // Return the window handle 
 
    return hwnd; 
} 
 
 
int GetIcon(LPITEMIDLIST lpi, UINT uFlags) 
{ 
   SHFILEINFO    sfi; 
 
   SHGetFileInfo((LPCSTR)lpi,  
                 0, 
                 &sfi,  
                 sizeof(SHFILEINFO),  
                 uFlags) ; 
 
   return sfi.iIcon; 
} 
 
 
void GetNormalAndSelectedIcons(LPITEMIDLIST lpifq, 
                               LPTV_ITEM lptvitem) 
{ 
   //Note that we don't check the return value here because if GetIcon() 
   //fails, then we're in big trouble... 
 
   lptvitem -> iImage = GetIcon(lpifq, SHGFI_PIDL |  
                              SHGFI_SYSICONINDEX |  
                              SHGFI_SMALLICON) ; 
    
   lptvitem -> iSelectedImage = GetIcon(lpifq, SHGFI_PIDL |  
                                      SHGFI_SYSICONINDEX |  
                                      SHGFI_SMALLICON | 
                                      SHGFI_OPENICON) ; 
    
   return; 
} 
 
 
void FillTreeView(	HWND hwndTreeView,  
					LPSHELLFOLDER lpsf, 
					LPITEMIDLIST lpifq, 
					HTREEITEM hParent) 
{ 
    TV_ITEM         tvi;                          // TreeView Item. 
    TV_INSERTSTRUCT tvins;                        // TreeView Insert Struct. 
    HTREEITEM       hPrev = NULL;                 // Previous Item Added. 
    LPSHELLFOLDER   lpsf2=0; 
    LPENUMIDLIST    lpe=0; 
    LPITEMIDLIST    lpi=0, lpiTemp=0, lpifqThisItem; 
    LPTVITEMDATA    lptvid=0; 
    LPMALLOC        lpMalloc=0; 
    ULONG           ulFetched; 
    UINT            uCount=0; 
    HRESULT         hr; 
    char            szBuff[256]; 
    HWND            hwnd=GetParent(hwndTreeView) ; 
 
    hr=SHGetMalloc(&lpMalloc) ; 
    if( FAILED(hr)) 
       return; 
 
    // Hourglass on! 
 
    SetCapture(GetParent(hwndTreeView)) ; 
    SetCursor(LoadCursor(NULL, IDC_WAIT)) ; 
 
    if( SUCCEEDED(hr)) 
    { 
        hr=lpsf -> lpVtbl -> EnumObjects(lpsf, 
                                     hwnd,  
                                     SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, 
                                     &lpe) ; 
 
        if( SUCCEEDED(hr)) 
        { 
            while (S_OK==lpe -> lpVtbl -> Next(lpe, 
                                           1, 
                                           &lpi, 
                                           &ulFetched)) 
            { 
                //Create a fully qualified path to the current item 
                //The SH* shell api's take a fully qualified path pidl, 
                //(see GetIcon above where I call SHGetFileInfo) whereas the 
                //interface methods take a relative path pidl. 
 
                ULONG ulAttrs = SFGAO_HASSUBFOLDER | SFGAO_FOLDER; 
 
                lpsf -> lpVtbl -> GetAttributesOf(lpsf, 1, &lpi, &ulAttrs) ; 
 
                if( ulAttrs & (SFGAO_HASSUBFOLDER | SFGAO_FOLDER)) 
                { 
                   //We need this next if statement so that we don't add things like 
                   //the MSN to our tree.  MSN is not a folder, but according to the 
                   //shell is has subfolders.... 
 
                   if( ulAttrs & SFGAO_FOLDER) 
                   { 
                      tvi.mask            = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | 
                                            TVIF_PARAM; 
 
                      if( ulAttrs & SFGAO_HASSUBFOLDER) 
                      { 
                         //This item has sub-folders, so let's put the + in the TreeView. 
                         //The first time the user clicks on the item, we'll populate the 
                         //sub-folders then. 
 
                         tvi.cChildren=1; 
                         tvi.mask |= TVIF_CHILDREN; 
                      } 
                         
                      //OK, let's get some memory for our ITEMDATA struct 
 
                      lptvid = (LPTVITEMDATA)lpMalloc -> lpVtbl -> Alloc(lpMalloc, sizeof(TVITEMDATA)) ; 
                      if( !lptvid) 
                         goto Done; 
    
                      //Now get the friendly name that we'll put in the treeview... 
 
                      if( !GetName(lpsf, lpi, SHGDN_NORMAL, szBuff)) 
                         goto Done; 
                      tvi.pszText    = szBuff; 
                      tvi.cchTextMax = MAX_PATH; 
     
                      lpifqThisItem=ConcatPidls(lpifq, lpi) ; 
       
                      //Now, make a copy of the ITEMIDLIST 
       
                      lptvid -> lpi=CopyITEMID(lpMalloc, lpi) ; 
    
                      GetNormalAndSelectedIcons(lpifqThisItem, &tvi) ; 
    
                      lptvid -> lpsfParent=lpsf;    //Store the parent folders SF 
                      lpsf -> lpVtbl -> AddRef(lpsf) ; 
 
                      //So, you ask, what's the story here?  Why to we have 2 
                      //functions that apparently do the same thing?  Well,  
                      //ParseDisplayName() (used in GetFullyQualPidl) does not  
                      //work well for non-file system objects such as the My  
                      //Computer and Net Hood.  So, since we know that these  
                      //guy will only show up when we are enumerating the root  
                      //of the namespace (as identified by an hParent of TVI_ROOT),  
                      //we special case this here.  You *could* use ConcatPidls()  
                      //in ALL cases, but the GetFullyQualPidl() is shown for  
                      //demonstration purposes, since it's the more intuative way. 
 
                      if( hParent==TVI_ROOT) 
                         lptvid -> lpifq=ConcatPidls(lpifq, lpi) ; 
                      else 
                         lptvid -> lpifq=GetFullyQualPidl(lpsf, lpi) ; 
    
                      tvi.lParam = (LPARAM)lptvid; 
    
                      // Populate the TreeVeiw Insert Struct 
                      // The item is the one filled above. 
                      // Insert it after the last item inserted at this level. 
                      // And indicate this is a root entry. 
   				    
                      tvins.item         = tvi; 
                      tvins.hInsertAfter = hPrev; 
                      tvins.hParent      = hParent; 
    
                      // Add the item to the tree 
    
                      hPrev = TreeView_InsertItem(hwndTreeView, &tvins) ; 
                   } 
 
                   lpMalloc -> lpVtbl -> Free(lpMalloc, lpifqThisItem) ;   
                   lpifqThisItem=0; 
                } 
 
                lpMalloc -> lpVtbl -> Free(lpMalloc, lpi) ;  //Finally, free the pidl that the shell gave us... 
                lpi=0; 
            } 
        } 
 
    } 
    else 
       return; 
 
Done: 
  
    // Hourglass off! 
    ReleaseCapture() ; 
    SetCursor( LoadCursor( NULL, IDC_ARROW)) ; 
 
    if( lpe) 
		lpe -> lpVtbl -> Release( lpe) ; 
 
    //The following 2 if statements will only be TRUE if we got here on an 
    //error condition from the "goto" statement.  Otherwise, we free this memory 
    //at the end of the while loop above. 
    if( lpi && lpMalloc) 
		lpMalloc -> lpVtbl -> Free(lpMalloc, lpi) ; 
    if( lpifqThisItem && lpMalloc) 
		lpMalloc -> lpVtbl -> Free(lpMalloc, lpifqThisItem) ;   
 
    if( lpMalloc) 
		lpMalloc -> lpVtbl -> Release(lpMalloc) ; 
}