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) ; }