// Classic Shell (c) 2009-2017, Ivo Beltchev // Open-Shell (c) 2017-2018, The Open-Shell Team // Confidential information of Ivo Beltchev. Not for disclosure or distribution without prior written consent from the author // ExplorerBHO.cpp : Implementation of CExplorerBHO #include "stdafx.h" #include "ExplorerBHO.h" #include "Settings.h" #include "ResourceHelper.h" #include "resource.h" #include "SettingsUI.h" #include "Translations.h" #include "SettingsUIHelper.h" #include "DownloadHelper.h" #include "FNVHash.h" #include "dllmain.h" #include #include #include #include // CExplorerBHO - a browser helper object that implements Alt+Enter for the folder tree const UINT_PTR TIMER_NAVIGATE='CLSH'; int CExplorerBHO::s_AutoNavDelay; static void GetTreeItemPidl( HTREEITEM hItem, HWND hwndTree, PIDLIST_ABSOLUTE *result ) { // find the PIDL of the tree item (combine all child PIDLs from the current item and its parents) CAbsolutePidl pidl; while (hItem) { TVITEMEX info={TVIF_PARAM,hItem}; TreeView_GetItem(hwndTree,&info); PIDLIST_RELATIVE **pidl1=(PIDLIST_RELATIVE**)info.lParam; if (!pidl1 || !*pidl1 || !**pidl1) { pidl.Clear(); break; } pidl.Attach(pidl?ILCombine((PIDLIST_ABSOLUTE)**pidl1,pidl):(PIDLIST_ABSOLUTE)ILClone(**pidl1)); hItem=TreeView_GetParent(hwndTree,hItem); } *result=pidl.Detach(); } LRESULT CALLBACK CExplorerBHO::SubclassTreeParentProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) { // when the tree selection changes start a timer to navigate to the new folder in 100ms if (uMsg==WM_NOTIFY && ((NMHDR*)lParam)->code==TVN_SELCHANGED) { if (GetSettingInt(L"AutoNavigate")==2 || ((NMTREEVIEW*)lParam)->action==TVC_BYKEYBOARD) SetTimer(((NMHDR*)lParam)->hwndFrom,TIMER_NAVIGATE,s_AutoNavDelay,NULL); } return DefSubclassProc(hWnd,uMsg,wParam,lParam); } // Subclass the tree control to: // - support Alt+Enter // - navigate to the new folder when you go up/down with the keyboard // - fix the random scrolling of the tree when a folder is expanded // - change the tree styles to achieve different looks LRESULT CALLBACK CExplorerBHO::SubclassTreeProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) { if (uMsg==TVM_ENSUREVISIBLE && (dwRefData&1)) { // HACK! there is a bug in Win7 Explorer and when the selected folder is expanded for the first time it sends TVM_ENSUREVISIBLE for // the root tree item. This causes the navigation pane to scroll up. To work around the bug we ignore TVM_ENSUREVISIBLE if it tries // to show the root item and it is not selected HTREEITEM hItem=(HTREEITEM)lParam; if (!TreeView_GetParent(hWnd,hItem) && !(TreeView_GetItemState(hWnd,hItem,TVIS_SELECTED)&TVIS_SELECTED)) return 0; } if (uMsg==WM_TIMER && wParam==TIMER_NAVIGATE) { // time to navigate to the selected folder (only if different from the current folder) KillTimer(hWnd,TIMER_NAVIGATE); if (GetFocus()!=hWnd) return 0; CAbsolutePidl pidl; GetTreeItemPidl(TreeView_GetSelection(hWnd),hWnd,&pidl); if (pidl) { bool bSameFolder=false; CExplorerBHO *pThis=GetTlsData()->bho; CComPtr pView; if (pThis->m_pBrowser && SUCCEEDED(pThis->m_pBrowser->QueryActiveShellView(&pView))) { CComQIPtr pView2=pView; CComPtr pFolder; CAbsolutePidl pidl2; if (pView2 && SUCCEEDED(pView2->GetFolder(IID_IPersistFolder2,(void**)&pFolder)) && SUCCEEDED(pFolder->GetCurFolder(&pidl2)) && pidl2) { if (ILIsEqual(pidl,pidl2)) bSameFolder=true; } } if (!bSameFolder) SendMessage(hWnd,WM_KEYDOWN,VK_SPACE,0); } return 0; } // ignore the Alt+Enter syscharacter (to stop the tree view from beeping) if (uMsg==WM_SYSCHAR && wParam==VK_RETURN) return 0; if (uMsg==WM_SYSKEYDOWN && wParam==VK_RETURN) { // Alt+Enter is pressed // if this message was for the folder tree, show the properties of the selected item if (GetSettingBool(L"AltEnter") && ShowTreeProperties(hWnd)) return 0; } if ((uMsg==WM_LBUTTONDOWN || uMsg==WM_LBUTTONDBLCLK) && (wParam&MK_CONTROL)) { TVHITTESTINFO test; test.pt.x=(short)LOWORD(lParam); test.pt.y=(short)HIWORD(lParam); HTREEITEM hItem=TreeView_HitTest(hWnd,&test); if (test.flags&TVHT_ONITEM) { CAbsolutePidl pidl; GetTreeItemPidl(hItem,hWnd,&pidl); if (pidl) { CExplorerBHO *pThis=GetTlsData()->bho; if (pThis->m_pBrowser) { pThis->m_pBrowser->BrowseObject(pidl,SBSP_NEWBROWSER|SBSP_ABSOLUTE); } } } return 0; } if (uMsg==TVM_SETEXTENDEDSTYLE && wParam==(TVS_EX_FADEINOUTEXPANDOS|TVS_EX_AUTOHSCROLL|0x80000000) && lParam==0) { wParam&=0x7FFFFFFF; if (GetSettingInt(L"AutoNavigate")>0) SetWindowSubclass(GetParent(hWnd),SubclassTreeParentProc,'CLSH',0); if (!GetSettingBool(L"NoFadeButtons")) wParam&=~TVS_EX_FADEINOUTEXPANDOS; int indent=-1; if (GetSettingBool(L"FullIndent")) indent=0; int treeStyle=GetSettingInt(L"TreeStyle"); if (treeStyle==STYLE_CLASSIC && GetWinVersion()>=WIN_VER_WIN10) treeStyle=STYLE_VISTA; DWORD style=GetWindowLong(hWnd,GWL_STYLE); if (treeStyle!=STYLE_VISTA) { SetWindowTheme(hWnd,NULL,NULL); if (treeStyle==STYLE_SIMPLE) { style|=TVS_SINGLEEXPAND|TVS_TRACKSELECT; style&=~TVS_HASLINES; } else { style|=TVS_HASLINES; style&=~(TVS_SINGLEEXPAND|TVS_TRACKSELECT); wParam|=TVS_EX_FADEINOUTEXPANDOS; HIMAGELIST images=TreeView_GetImageList(hWnd,TVSIL_NORMAL); int cx, cy; ImageList_GetIconSize(images,&cx,&cy); indent=cx+3; } } int scroll=GetSettingInt(L"HScrollbar"); if ((scroll==0 && treeStyle==STYLE_VISTA) || scroll==1) wParam&=~TVS_EX_AUTOHSCROLL; if ((scroll==0 && treeStyle!=STYLE_VISTA) || scroll==2) style&=~TVS_NOHSCROLL; SetWindowLong(hWnd,GWL_STYLE,style); if (indent>=0) TreeView_SetIndent(hWnd,indent); int d=GetSettingInt(L"TreeItemSpacing"); if (d) { CExplorerBHO *pThis=GetTlsData()->bho; pThis->m_TreeItemHeight=TreeView_GetItemHeight(hWnd)+d; TreeView_SetItemHeight(hWnd,pThis->m_TreeItemHeight); } if (wParam==0) return 0; } if (uMsg==WM_SETTINGCHANGE) { LRESULT res=DefSubclassProc(hWnd,uMsg,wParam,lParam); int indent=-1; if (GetSettingBool(L"FullIndent")) indent=0; if (GetSettingInt(L"TreeStyle")==STYLE_CLASSIC && GetWinVersion()=0) TreeView_SetIndent(hWnd,indent); return res; } if (uMsg==TVM_SETITEMHEIGHT) { CExplorerBHO *pThis=GetTlsData()->bho; if (pThis->m_TreeItemHeight>0) wParam=pThis->m_TreeItemHeight; } return DefSubclassProc(hWnd,uMsg,wParam,lParam); } LRESULT CALLBACK CExplorerBHO::HookExplorer( int nCode, WPARAM wParam, LPARAM lParam ) { // wait for the tree control to be created and subclass it if (nCode==HCBT_CREATEWND) { HWND hWnd=(HWND)wParam; CBT_CREATEWND *pCreate=(CBT_CREATEWND*)lParam; if (pCreate->lpcs->lpszClass>(LPTSTR)0xFFFF && _wcsicmp(pCreate->lpcs->lpszClass,WC_TREEVIEW)==0) { HWND parent=GetAncestor(pCreate->lpcs->hwndParent,GA_ROOT); wchar_t name[256]; if (GetClassName(parent,name,_countof(name)) && _wcsicmp(name,L"CabinetWClass")==0) { DWORD_PTR settings=0; if (GetWinVersion()==WIN_VER_WIN7 && GetSettingBool(L"FixFolderScroll")) settings|=1; SetWindowSubclass(hWnd,SubclassTreeProc,'CLSH',settings); PostMessage(hWnd,TVM_SETEXTENDEDSTYLE,TVS_EX_FADEINOUTEXPANDOS|TVS_EX_AUTOHSCROLL|0x80000000,0); } } else if (pCreate->lpcs->lpszClass>(LPTSTR)0xFFFF && _wcsicmp(pCreate->lpcs->lpszClass,L"DUIViewWndClassName")==0) { TlsData *pTlsData=GetTlsData(); if (!pTlsData->bho->m_DUIView) { pTlsData->bho->m_DUIView=hWnd; SetWindowSubclass(pTlsData->bho->m_DUIView,SubclassDUIViewProc,(UINT_PTR)pTlsData->bho,'CLSH'); } } } return CallNextHookEx(NULL,nCode,wParam,lParam); } LRESULT CALLBACK CExplorerBHO::HookKeyboard( int nCode, WPARAM wParam, LPARAM lParam ) { // wait for the tree control to be created and subclass it if (nCode==HC_ACTION) { TlsData *pTlsData=GetTlsData(); if (wParam==pTlsData->bho->m_AltD && (lParam&0x20000000)) { if (lParam&0x80000000) { return 0; } else { if (IsWindow(pTlsData->bho->m_Breadcrumbs) && IsWindowVisible(pTlsData->bho->m_Breadcrumbs)) { SetFocus(pTlsData->bho->m_Breadcrumbs); SendMessage(pTlsData->bho->m_Breadcrumbs,WM_KEYDOWN,VK_SPACE,0); SendMessage(pTlsData->bho->m_Breadcrumbs,WM_KEYUP,VK_SPACE,0); return 0; } } } if (wParam==(pTlsData->bho->m_UpHotkey&255) && !(lParam&0x80000000)) { // Backspace goes to the parent folder, but only if no window has the caret GUITHREADINFO info={sizeof(info)}; if (GetGUIThreadInfo(GetCurrentThreadId(),&info) && !info.hwndCaret) { bool bShift1=(pTlsData->bho->m_UpHotkey&(HOTKEYF_SHIFT<<8))!=0; bool bCtrl1=(pTlsData->bho->m_UpHotkey&(HOTKEYF_CONTROL<<8))!=0; bool bAlt1=(pTlsData->bho->m_UpHotkey&(HOTKEYF_ALT<<8))!=0; bool bShift2=GetKeyState(VK_SHIFT)<0; bool bCtrl2=GetKeyState(VK_CONTROL)<0; bool bAlt2=GetKeyState(VK_MENU)<0; if (bShift1==bShift2 && bAlt1==bAlt2) { if (bCtrl1==bCtrl2) { pTlsData->bho->m_pBrowser->BrowseObject(NULL,SBSP_SAMEBROWSER|SBSP_PARENT); return 1; } else if (bCtrl2) { pTlsData->bho->m_pBrowser->BrowseObject(NULL,SBSP_NEWBROWSER|SBSP_PARENT); return 1; } } } } } return CallNextHookEx(NULL,nCode,wParam,lParam); } bool CExplorerBHO::GetStatusText( wchar_t *buf, int size, const wchar_t *oldText, bool bShowTip, bool bShowSpace ) { bool res=false; CComPtr pView; if (m_pBrowser && SUCCEEDED(m_pBrowser->QueryActiveShellView(&pView))) { CComQIPtr pView2=pView; CComPtr pFolder; if (pView2 && SUCCEEDED(pView2->GetFolder(IID_IPersistFolder2,(void**)&pFolder))) { int count; if (bShowTip && SUCCEEDED(pView2->ItemCount(SVGIO_SELECTION,&count)) && count==1) { // if only one item is selected, show its info in the status bar CComPtr pEnum; PITEMID_CHILD child; if (SUCCEEDED(pView2->Items(SVGIO_SELECTION,IID_IEnumIDList,(void**)&pEnum)) && pEnum && pEnum->Next(1,&child,NULL)==S_OK) { CComQIPtr pFolder2=pFolder; if (pFolder2) { CComPtr pQueryInfo; if (SUCCEEDED(pFolder2->GetUIObjectOf(NULL,1,(PCUITEMID_CHILD*)&child,IID_IQueryInfo,NULL,(void**)&pQueryInfo))) { CComString pTip; if (SUCCEEDED(pQueryInfo->GetInfoTip(QITIPF_DEFAULT|QITIPF_SINGLELINE,&pTip)) && pTip) { Strcpy(buf,size,pTip); for (wchar_t *p=buf;*p;p++) if (*p=='\t') *p=' '; res=true; } } } } } if (!res) { wchar_t buf2[256]; if (!oldText) { int count=-1; const wchar_t *fmt=NULL; if (SUCCEEDED(pView2->ItemCount(SVGIO_SELECTION,&count)) && count>0) { fmt=(count==1?FindTranslation(L"Status.ItemSelected",L"%s item selected"):FindTranslation(L"Status.ItemsSelected",L"%s items selected")); } else if (SUCCEEDED(pView2->ItemCount(SVGIO_ALLVIEW,&count)) && count>=0) { fmt=(count==1?FindTranslation(L"Status.Item",L"%s item"):FindTranslation(L"Status.Items",L"%s items")); } else buf2[0]=0; if (fmt) { wchar_t str1[100]; wchar_t str2[100]; Sprintf(str1,_countof(str1),L"%d",count); NUMBERFMT numFmt; wchar_t sep1[10], sep2[10]; numFmt.NumDigits=0; GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_ILZERO,sep1,10); numFmt.LeadingZero=_wtol(buf); GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_INEGNUMBER,sep1,10); numFmt.NegativeOrder=_wtol(buf); GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SGROUPING,sep1,10); numFmt.Grouping=0; for (const wchar_t *p=sep1;*p;p++) if (*p>='1' && *p<='9') numFmt.Grouping=numFmt.Grouping*10+(*p-'0'); GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SDECIMAL,sep1,10); numFmt.lpDecimalSep=sep1; GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_STHOUSAND,sep2,10); numFmt.lpThousandSep=sep2; if (GetNumberFormat(LOCALE_USER_DEFAULT,0,str1,&numFmt,str2,_countof(str2))) Sprintf(buf2,_countof(buf2),fmt,str2); else Sprintf(buf2,_countof(buf2),fmt,str1); } res=true; } if (bShowSpace) { // show the free space of the drive containing the current folder // also works for network locations CAbsolutePidl pidl; wchar_t path[_MAX_PATH]; ULARGE_INTEGER diskSize; if (SUCCEEDED(pFolder->GetCurFolder(&pidl)) && SHGetPathFromIDList(pidl,path) && GetDiskFreeSpaceEx(path,NULL,NULL,&diskSize)) { wchar_t str[100]; StrFormatByteSize64(diskSize.QuadPart,str,_countof(str)); Sprintf(buf,size,FindTranslation(L"Status.FreeSpace",L"%s (Disk free space: %s)"),oldText?oldText:buf2,str); res=true; } else if (!oldText) Strcpy(buf,size,buf2); } else if (!oldText) Strcpy(buf,size,buf2); } } } return res; } void CExplorerBHO::GetFileSize( wchar_t *buf, int size ) { __int64 fileSize=-1; CComPtr pView; bool bMore=false; int time0=GetTickCount(); if (m_pBrowser && SUCCEEDED(m_pBrowser->QueryActiveShellView(&pView))) { CComQIPtr pView2=pView; CComPtr pFolder; CAbsolutePidl pidl; if (pView2 && SUCCEEDED(pView2->GetFolder(IID_IPersistFolder2,(void**)&pFolder)) && SUCCEEDED(pFolder->GetCurFolder(&pidl))) { CComQIPtr pFolder2=pFolder; UINT type=SVGIO_SELECTION; int count, selCount; if (SUCCEEDED(pView2->ItemCount(SVGIO_ALLVIEW,&count))) { if ((FAILED(pView2->ItemCount(SVGIO_SELECTION,&selCount)) || selCount==0)) type=SVGIO_ALLVIEW; CComPtr pEnum; if ((count<10000 || selCount<1000) && SUCCEEDED(pView2->Items(type,IID_IEnumIDList,(void**)&pEnum)) && pEnum) { PITEMID_CHILD child; SHCOLUMNID column={PSGUID_STORAGE,PID_STG_SIZE}; int index=0; while (pEnum->Next(1,&child,NULL)==S_OK) { index++; if ((index%100)==0 && (GetTickCount()-time0)>500) { ILFree(child); bMore=true; break; } CComVariant var; if (SUCCEEDED(pFolder2->GetDetailsEx(child,&column,&var)) && var.vt==VT_UI8) { if (fileSize<0) fileSize=var.ullVal; else fileSize+=var.ullVal; } ILFree(child); } } } } } if (fileSize>=0) { // format the file size as KB, MB, etc StrFormatByteSize64(fileSize,buf,size); if (bMore) Strcat(buf,size,L"+"); } else buf[0]=0; } // Subclass the statusbar to: // - show free disk space // - show the total size of the selected files LRESULT CALLBACK CExplorerBHO::SubclassStatusProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) { wchar_t buf[1024]; if (uMsg==WM_CLEAR) { // recalculate the selection size on a timer. this way if the status text is changed frequently // the recalculation will not happen every time SetTimer(hWnd,uIdSubclass,10,NULL); } if (uMsg==WM_PAINT && ((CExplorerBHO*)uIdSubclass)->m_bForceRefresh) { // sometimes Explorer doesn't fully initialize the status bar on Windows 7 and leaves it with 1 part // in such case force the view to refresh after the status bar is fully visible ((CExplorerBHO*)uIdSubclass)->m_bForceRefresh=false; if (SendMessage(hWnd,SB_GETPARTS,0,0)<=1) PostMessage(GetParent(hWnd),WM_COMMAND,41504,0); // Refresh command } if (uMsg==SB_SETTEXT && LOWORD(wParam)==0) { // when the text of the first part is changing if (dwRefData&SHOW_FREE_SPACE) { if (((CExplorerBHO*)uIdSubclass)->m_bResetStatus && SendMessage(hWnd,SB_GETPARTS,0,0)<=1) { // HACK! there is a bug in Win7 and when the Explorer window is created it doesn't correctly // initialize the status bar to have 3 parts. as soon as the user resizes the window the // 3 parts appear. so here we resize the parent of the status bar to create the 3 parts. HWND parent=GetParent(hWnd); RECT rc; GetWindowRect(parent,&rc); SetWindowPos(parent,NULL,0,0,rc.right-rc.left+1,rc.bottom-rc.top,SWP_NOZORDER|SWP_NOMOVE); SetWindowPos(parent,NULL,0,0,rc.right-rc.left,rc.bottom-rc.top,SWP_NOZORDER|SWP_NOMOVE); // the first time the status text is set it is too early. so we do this until we get at lest 2 parts if (SendMessage(hWnd,SB_GETPARTS,0,0)>1) ((CExplorerBHO*)uIdSubclass)->m_bResetStatus=false; } } if (((CExplorerBHO*)uIdSubclass)->GetStatusText(buf,_countof(buf),(wchar_t*)lParam,(dwRefData&SHOW_INFOTIP)!=0,(dwRefData&SHOW_FREE_SPACE)!=0)) lParam=(LPARAM)buf; } if (uMsg==SB_SETTEXT && LOWORD(wParam)==1) { return 0; } if (uMsg==WM_TIMER && wParam==uIdSubclass) { // recalculate the total size of the selected files and show it in part 2 of the status bar KillTimer(hWnd,wParam); ((CExplorerBHO*)uIdSubclass)->GetFileSize(buf,_countof(buf)); DefSubclassProc(hWnd,SB_SETTEXT,1,(LPARAM)buf); } return DefSubclassProc(hWnd,uMsg,wParam,lParam); } LRESULT CALLBACK CExplorerBHO::SubclassStatusProc8( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) { wchar_t buf[1024]; if (uMsg==WM_CLEAR) { // recalculate the selection size on a timer. this way if the status text is changed frequently // the recalculation will not happen every time SetTimer(hWnd,uIdSubclass,10,NULL); if (!((CExplorerBHO*)uIdSubclass)->GetStatusText(buf,_countof(buf),NULL,(dwRefData&SHOW_INFOTIP)!=0,(dwRefData&SHOW_FREE_SPACE)!=0)) buf[0]=0; SendMessage(hWnd,SB_SETTEXT,PART_TEXT,(LPARAM)buf); return 0; } if (uMsg==WM_TIMER && wParam==uIdSubclass) { // recalculate the total size of the selected files and show it in part 2 of the status bar KillTimer(hWnd,wParam); ((CExplorerBHO*)uIdSubclass)->GetFileSize(buf,_countof(buf)); SendMessage(hWnd,SB_SETTEXT,PART_SIZE,(LPARAM)buf); return 0; } if (uMsg==WM_LBUTTONDBLCLK) { POINT pt={(short)LOWORD(lParam),(short)HIWORD(lParam)}; RECT rc; DefSubclassProc(hWnd,SB_GETRECT,PART_ZONE,(LPARAM)&rc); if (PtInRect(&rc,pt)) { CExplorerBHO *pThis=(CExplorerBHO*)uIdSubclass; CComBSTR url; if (pThis->m_pWebBrowser && SUCCEEDED(pThis->m_pWebBrowser->get_LocationURL(&url))) { // use undocumented function 383 from shlwapi typedef void (WINAPI* FZoneConfigureW)(HWND,LPCWSTR); FZoneConfigureW ZoneConfigureW; HMODULE hShlwapi=LoadLibrary(L"shlwapi.dll"); if(hShlwapi) { ZoneConfigureW=(FZoneConfigureW)GetProcAddress(hShlwapi,MAKEINTRESOURCEA(383)); if(ZoneConfigureW) ZoneConfigureW(GetAncestor(hWnd,GA_ROOT),url); FreeLibrary(hShlwapi); } } return 0; } } return DefSubclassProc(hWnd,uMsg,wParam,lParam); } // Subclass the DUIView to reduce its size and reposition the taskbar LRESULT CALLBACK CExplorerBHO::SubclassDUIViewProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) { CExplorerBHO *pThis=(CExplorerBHO*)uIdSubclass; if (uMsg==WM_WINDOWPOSCHANGING && pThis->m_Status8) { WINDOWPOS *pPos=(WINDOWPOS*)lParam; if (!(pPos->flags&SWP_NOSIZE)) { RECT rc; GetWindowRect(pThis->m_Status8,&rc); int height=rc.bottom-rc.top; pPos->cy-=height; SetWindowPos(pThis->m_Status8,NULL,pPos->x,pPos->y+pPos->cy,pPos->cx,rc.bottom-rc.top,SWP_NOZORDER); int parts[]={pPos->cx-height-pThis->m_FileSizeWidth-pThis->m_ZoneWidth,pPos->cx-height-pThis->m_ZoneWidth,-1}; SendMessage(pThis->m_Status8,SB_SETPARTS,pThis->m_ZoneWidth?3:2,(LPARAM)parts); } } return DefSubclassProc(hWnd,uMsg,wParam,lParam); } // Subclass the rebar in the title bar to handle the title bar Up button LRESULT CALLBACK CExplorerBHO::SubclassRebarProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) { if (uMsg==WM_NOTIFY && ((NMHDR*)lParam)->hwndFrom==(HWND)dwRefData && ((NMHDR*)lParam)->code==NM_CUSTOMDRAW) { // custom-draw the toolbar. just draw the correct icon and nothing else NMTBCUSTOMDRAW *pDraw=(NMTBCUSTOMDRAW*)lParam; if (pDraw->nmcd.dwDrawStage==CDDS_PREPAINT) return CDRF_NOTIFYITEMDRAW; if (pDraw->nmcd.dwDrawStage==CDDS_ITEMPREPAINT) { CExplorerBHO *pThis=(CExplorerBHO*)uIdSubclass; BOOL comp; if (SUCCEEDED(DwmIsCompositionEnabled(&comp)) && comp) FillRect(pDraw->nmcd.hdc,&pDraw->nmcd.rc,(HBRUSH)GetStockObject(BLACK_BRUSH)); if (pDraw->nmcd.uItemState&CDIS_DISABLED) { if (pThis->m_IconDisabled) DrawIconEx(pDraw->nmcd.hdc,0,0,pThis->m_IconDisabled,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR); else DrawIconEx(pDraw->nmcd.hdc,0,0,pThis->m_IconNormal,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR); } else if (pDraw->nmcd.uItemState&CDIS_SELECTED) { if (pThis->m_IconPressed) DrawIconEx(pDraw->nmcd.hdc,0,0,pThis->m_IconPressed,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR); else DrawIconEx(pDraw->nmcd.hdc,1,1,pThis->m_IconNormal,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR); } else if (pDraw->nmcd.uItemState&CDIS_HOT) { if (pThis->m_IconHot) DrawIconEx(pDraw->nmcd.hdc,0,0,pThis->m_IconHot,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR); else DrawIconEx(pDraw->nmcd.hdc,0,0,pThis->m_IconNormal,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR); } else DrawIconEx(pDraw->nmcd.hdc,0,0,pThis->m_IconNormal,0,0,0,NULL,DI_NORMAL|DI_NOMIRROR); return CDRF_SKIPDEFAULT; } } if (uMsg==WM_THEMECHANGED) { // the button size is reset when the theme changes. force the correct size again HWND toolbar=(HWND)dwRefData; RECT rc; GetClientRect(toolbar,&rc); PostMessage(toolbar,TB_SETBUTTONSIZE,0,MAKELONG(rc.right,rc.bottom)); } if (uMsg==WM_NOTIFY && ((NMHDR*)lParam)->hwndFrom==(HWND)dwRefData && ((NMHDR*)lParam)->code==TBN_GETINFOTIP) { // show the tip for the up button NMTBGETINFOTIP *pTip=(NMTBGETINFOTIP*)lParam; Strcpy(pTip->pszText,pTip->cchTextMax,FindTranslation(L"Toolbar.GoUp",L"Up One Level")); return 0; } if (uMsg==WM_NOTIFY && ((NMHDR*)lParam)->hwndFrom==(HWND)dwRefData && ((NMHDR*)lParam)->code==NM_RCLICK) { NMMOUSE *pInfo=(NMMOUSE*)lParam; POINT pt=pInfo->pt; ClientToScreen(pInfo->hdr.hwndFrom,&pt); ShowSettingsMenu(hWnd,pt.x,pt.y); return TRUE; } if (uMsg==WM_COMMAND && wParam==1) { UINT flags=(GetKeyState(VK_CONTROL)<0?SBSP_NEWBROWSER:SBSP_SAMEBROWSER); ((CExplorerBHO*)uIdSubclass)->m_pBrowser->BrowseObject(NULL,flags|SBSP_PARENT); } CExplorerBHO *pThis=(CExplorerBHO*)uIdSubclass; if (pThis->m_bRemapBands) { // HACK! Explorer doesn't use RB_IDTOINDEX every time it needs to access a particular band. Since we insert the Up button in the second // position, the rest of the bands get offset and comedy ensues (the search box is not sized properly). // To fix the issue, we renumber the bands so that from the outside ours appears to be last. static int remapNewOld[2][4]={{3,0,1,2},{0,3,1,2}}; static int remapOldNew[2][4]={{1,2,3,0},{0,2,3,1}}; if (uMsg==RB_IDTOINDEX || uMsg==RB_HITTEST) { // remap the result from RB_IDTOINDEX and RB_HITTEST LRESULT res=DefSubclassProc(hWnd,uMsg,wParam,lParam); if (res<0 || res>3) return res; res=remapNewOld[pThis->m_UpButtonIndex-1][res]; if (lParam && uMsg==RB_HITTEST) ((RBHITTESTINFO*)lParam)->iBand=(int)res; return res; } if (uMsg==RB_GETBANDBORDERS || uMsg==RB_GETBANDINFO || uMsg==RB_GETRECT || uMsg==RB_SETBANDINFO || uMsg==RB_SETBANDWIDTH) { // remap wParam for all GET/SET messages if (wParam>=0 && wParam<=3) wParam=remapOldNew[pThis->m_UpButtonIndex-1][wParam]; } } return DefSubclassProc(hWnd,uMsg,wParam,lParam); } // Subclass the breadcrumbs to make them show the full path LRESULT CALLBACK CExplorerBHO::SubclassBreadcrumbProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) { CExplorerBHO *pThis=(CExplorerBHO*)uIdSubclass; if (*pThis->m_CurPath) { if (uMsg==WM_SETFOCUS) { if (wParam) { // see if the focus comes from the combo box. if so, most likely Escape was pressed, so just focus the main frame HWND from=(HWND)wParam; HWND combo=FindChildWindow(GetParent(GetParent(hWnd)),WC_COMBOBOXEX); if (combo && (combo==from || IsChild(combo,from))) { SetFocus(GetAncestor(hWnd,GA_ROOT)); return 0; } } // when the breadcrumbs are focused, switch to the combobox by simulating a mouse click RECT rc; GetClientRect(hWnd,&rc); LPARAM pos=MAKELONG(rc.right-1,rc.bottom/2); DefSubclassProc(hWnd,WM_LBUTTONDOWN,MK_LBUTTON,pos); DefSubclassProc(hWnd,WM_LBUTTONUP,0,pos); return 0; } if (uMsg==WM_LBUTTONDOWN || uMsg==WM_LBUTTONDBLCLK || uMsg==WM_LBUTTONUP) { // unless the mouse is clicked on the icon, replace the mouse position with a point on the far right. // this will cause Explorer to switch to the combobox even when a breadcrumb is clicked int iconSize=GetSystemMetrics(SM_CXSMICON); if (!pThis->m_CurIcon || (short)LOWORD(lParam)>iconSize+3) { RECT rc; GetClientRect(hWnd,&rc); lParam=MAKELONG(rc.right-1,rc.bottom/2); } } if (uMsg==WM_PAINT) { // make the breadcrumbs control draw the full path like the XP address bar RECT rc; GetClientRect(hWnd,&rc); PAINTSTRUCT ps; HDC hdc=BeginPaint(hWnd,&ps); // we need to use buffered painting because DrawThemeTextEx with DTT_COMPOSITED requires it // on Vista DTT_COMPOSITED is required so that the black text doesn't get transparent. On Windows 7 regular DrawText seems to work fine BP_PAINTPARAMS paintParams={sizeof(paintParams)}; paintParams.dwFlags=BPPF_ERASE; HDC hdcPaint=NULL; HPAINTBUFFER hBufferedPaint=BeginBufferedPaint(hdc,&rc,BPBF_TOPDOWNDIB,&paintParams,&hdcPaint); if (hdcPaint) { rc.top++; SendMessage(GetParent(GetParent(hWnd)),WM_PRINTCLIENT,(WPARAM)hdcPaint,PRF_CLIENT); // draw icon int iconSize=GetSystemMetrics(SM_CXSMICON); if (pThis->m_CurIcon) DrawIconEx(hdcPaint,rc.left+3,(rc.top+rc.bottom-iconSize)/2,pThis->m_CurIcon,iconSize,iconSize,0,NULL,DI_NORMAL); rc.left+=iconSize+8; // Not a good idea to hard-code number of pixels, but seems to work fine for different DPI settings // draw path HFONT font=(HFONT)SendMessage(hWnd,WM_GETFONT,0,0); HGDIOBJ font0=SelectObject(hdcPaint,font); SetBkMode(hdcPaint,TRANSPARENT); SetTextColor(hdcPaint,GetSysColor(COLOR_WINDOWTEXT)); HTHEME theme=GetWindowTheme(hWnd); BOOL dwm; if (theme && SUCCEEDED(DwmIsCompositionEnabled(&dwm)) && dwm) { DTTOPTS opts={sizeof(opts),DTT_COMPOSITED|DTT_TEXTCOLOR}; opts.crText=GetSysColor(COLOR_WINDOWTEXT); DrawThemeTextEx(theme,hdcPaint,0,0,pThis->m_CurPath,-1,DT_NOPREFIX|DT_VCENTER|DT_SINGLELINE,&rc,&opts); } else { DrawText(hdcPaint,pThis->m_CurPath,-1,&rc,DT_NOPREFIX|DT_VCENTER|DT_SINGLELINE); } SelectObject(hdcPaint,font0); EndBufferedPaint(hBufferedPaint,TRUE); } EndPaint(hWnd,&ps); return 0; } } return DefSubclassProc(hWnd,uMsg,wParam,lParam); } // Subclass the progress bar behind the address bar to remove the history and replace it with a list of parent folders LRESULT CALLBACK CExplorerBHO::SubclassProgressProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) { CExplorerBHO *pThis=(CExplorerBHO*)uIdSubclass; if (uMsg==WM_PARENTNOTIFY && LOWORD(wParam)==WM_CREATE && !pThis->m_ComboBox.m_hWnd) { // on Windows 7 the combobox is not created at startup. so listen for child windows being created and update pThis->m_ComboBox HWND combo=(HWND)lParam; wchar_t className[256]; GetClassName(combo,className,_countof(className)); if (_wcsicmp(className,WC_COMBOBOXEX)==0) pThis->m_ComboBox=combo; } if (uMsg==WM_COMMAND && (HWND)lParam==pThis->m_ComboBox.m_hWnd) { if (HIWORD(wParam)==CBN_DROPDOWN) { // on drop down refresh the list pThis->ClearComboItems(); CComPtr pDesktop; SHGetDesktopFolder(&pDesktop); { // add desktop ITEMIDLIST shEmpty={{0}}; CComString pName; if (SUCCEEDED(SHGetNameFromIDList((PIDLIST_ABSOLUTE)&shEmpty,SIGDN_DESKTOPABSOLUTEEDITING,&pName))) { ComboItem item={ILCloneFull((PIDLIST_ABSOLUTE)&shEmpty),0,CString(pName)}; pThis->m_ComboItems.push_back(item); } } if (dwRefData==2) { // enumerate all desktop items CComPtr pEnum; if (SUCCEEDED(pDesktop->EnumObjects(NULL,SHCONTF_FOLDERS,&pEnum)) && pEnum) { PITEMID_CHILD child; while (pEnum->Next(1,&child,NULL)==S_OK) { STRRET str; if (SUCCEEDED(pDesktop->GetDisplayNameOf(child,SHGDN_INFOLDER|SHGDN_NORMAL,&str))) { CComString pName; StrRetToStr(&str,child,&pName); ComboItem item={(PIDLIST_ABSOLUTE)child,1,CString(pName)}; item.sortName=item.name; pThis->m_ComboItems.push_back(item); } else ILFree(child); } } // sort desktop items std::sort(pThis->m_ComboItems.begin()+1,pThis->m_ComboItems.end()); } if (dwRefData==2) { // enumerate all computer items CAbsolutePidl pidlComp; SHGetKnownFolderIDList(FOLDERID_ComputerFolder,0,NULL,&pidlComp); int index=1; for (int i=1;i<(int)pThis->m_ComboItems.size();i++) if (ILIsEqual(pidlComp,pThis->m_ComboItems[i].pidl)) { index=i+1; break; } int index0=index; CComPtr pComputer; pDesktop->BindToObject(pidlComp,NULL,IID_IShellFolder,(void**)&pComputer); CComPtr pEnum; if (pComputer && SUCCEEDED(pComputer->EnumObjects(NULL,SHCONTF_FOLDERS,&pEnum)) && pEnum) { PITEMID_CHILD child; while (pEnum->Next(1,&child,NULL)==S_OK) { STRRET str; if (SUCCEEDED(pComputer->GetDisplayNameOf(child,SHGDN_INFOLDER|SHGDN_NORMAL,&str))) { CComString pName; StrRetToStr(&str,child,&pName); ComboItem item={ILCombine(pidlComp,child),2,CString(pName)}; pThis->m_ComboItems.insert(pThis->m_ComboItems.begin()+index,1,item); index++; } ILFree(child); } } //sort computer items std::sort(pThis->m_ComboItems.begin()+index0,pThis->m_ComboItems.begin()+index); } if (pThis->m_CurPidl) { // enumerate all parent items CAbsolutePidl pidl(pThis->m_CurPidl); PIDLIST_ABSOLUTE pidlStart=pidl; int index=0; for (int i=0;i<(int)pThis->m_ComboItems.size();i++) { PIDLIST_ABSOLUTE p=(PIDLIST_ABSOLUTE)ILFindChild(pThis->m_ComboItems[i].pidl,pidl); if (p) { index=i; pidlStart=p; } } int n=0; for (PUIDLIST_RELATIVE child=pidl;!ILIsEmpty(child);child=ILGetNext(child)) n++; int start=n; for (PUIDLIST_RELATIVE child=pidlStart;!ILIsEmpty(child);child=ILGetNext(child)) start--; for (int i=start;i pFolder; PCITEMID_CHILD child; if (SUCCEEDED(SHBindToParent(pidl,IID_IShellFolder,(void**)&pFolder,&child))) { STRRET str; if (SUCCEEDED(pFolder->GetDisplayNameOf(child,SHGDN_INFOLDER|SHGDN_NORMAL,&str))) { CComString pName; StrRetToStr(&str,child,&pName); ComboItem item={ILCloneFull(pidl),n-(i-start),CString(pName)}; pThis->m_ComboItems.insert(pThis->m_ComboItems.begin()+index+1,1,item); } } ILRemoveLastID(pidl); } } // add all sorted items to the combobox COMBOBOXEXITEM item={CBEIF_TEXT|CBEIF_IMAGE|CBEIF_SELECTEDIMAGE|CBEIF_INDENT|CBEIF_LPARAM}; item.iItem=-1; for (std::vector::const_iterator it=pThis->m_ComboItems.begin();it!=pThis->m_ComboItems.end();++it) { item.iImage=item.iSelectedImage=-1; item.pszText=(LPWSTR)(LPCWSTR)it->name; item.iIndent=it->indent; item.lParam=(LPARAM)it->pidl; SHFILEINFO info; if (SHGetFileInfo((LPCTSTR)it->pidl,0,&info,sizeof(info),SHGFI_PIDL|SHGFI_SYSICONINDEX|SHGFI_SMALLICON)) item.iImage=item.iSelectedImage=info.iIcon; int idx=(int)pThis->m_ComboBox.SendMessage(CBEM_INSERTITEM,'CLSH',(LPARAM)&item); if (pThis->m_CurPidl && ILIsEqual(it->pidl,pThis->m_CurPidl)) pThis->m_ComboBox.SendMessage(CB_SETCURSEL,idx); } return 0; } if (HIWORD(wParam)==CBN_CLOSEUP || HIWORD(wParam)==CBN_SELENDCANCEL) { // on close clear the list if (!pThis->m_ComboItems.empty()) { pThis->m_ComboBox.SetWindowText(pThis->m_CurPath); pThis->ClearComboItems(); } if (HIWORD(wParam)==CBN_SELENDCANCEL) PostMessage(hWnd,pThis->m_NavigateMsg,1,0); return 0; } if (HIWORD(wParam)==CBN_SELENDOK) { // when an item is selected, go to it and clear the list (selection with mouse) int index=(int)pThis->m_ComboBox.SendMessage(CB_GETCURSEL); if (index>=0) { COMBOBOXEXITEM item={CBEIF_LPARAM,index}; pThis->m_ComboBox.SendMessage(CBEM_GETITEM,0,(LPARAM)&item); pThis->m_NavigatePidl.Clone((PIDLIST_ABSOLUTE)item.lParam); PostMessage(hWnd,pThis->m_NavigateMsg,0,0); } pThis->ClearComboItems(); return 0; } } if (uMsg==WM_NOTIFY && ((NMHDR*)lParam)->code==CBEN_BEGINEDIT) { // ignore this, so Explorer doesn't add its history to the list return 0; } if (uMsg==WM_NOTIFY && ((NMHDR*)lParam)->code==CBEN_ENDEDIT) { if (pThis->m_ComboBox.SendMessage(CB_GETDROPPEDSTATE)) { // when an item is selected, go to it and clear the list (selection with keyboard) NMCBEENDEDIT *pEdit=(NMCBEENDEDIT*)lParam; int index=pEdit->iNewSelection; if (index>=0) { COMBOBOXEXITEM item={CBEIF_LPARAM,index}; pThis->m_ComboBox.SendMessage(CBEM_GETITEM,0,(LPARAM)&item); pThis->m_NavigatePidl.Clone((PIDLIST_ABSOLUTE)item.lParam); pThis->ClearComboItems(); PostMessage(hWnd,pThis->m_NavigateMsg,0,0); pThis->m_ComboBox.SendMessage(CB_SHOWDROPDOWN,FALSE); } return 0; } } if (uMsg==pThis->m_NavigateMsg) { // navigate to the selected item if (wParam==0 && pThis->m_NavigatePidl && !(pThis->m_CurPidl && ILIsEqual(pThis->m_NavigatePidl,pThis->m_CurPidl))) pThis->m_pBrowser->BrowseObject(pThis->m_NavigatePidl,SBSP_SAMEBROWSER|SBSP_ABSOLUTE); else SetFocus(hWnd); pThis->m_NavigatePidl.Clear(); } return DefSubclassProc(hWnd,uMsg,wParam,lParam); } static void NewVersionCallback( VersionData &data ) { wchar_t path[_MAX_PATH]; GetModuleFileName(g_Instance,path,_countof(path)); PathRemoveFileSpec(path); PathAppend(path,L"Update.exe"); wchar_t cmdLine[1024]; Sprintf(cmdLine,_countof(cmdLine),L"\"%s\" -popup",path); STARTUPINFO startupInfo={sizeof(startupInfo)}; PROCESS_INFORMATION processInfo; memset(&processInfo,0,sizeof(processInfo)); if (CreateProcess(path,cmdLine,NULL,NULL,TRUE,0,NULL,NULL,&startupInfo,&processInfo)) { CloseHandle(processInfo.hThread); CloseHandle(processInfo.hProcess); } } HRESULT STDMETHODCALLTYPE CExplorerBHO::SetSite( IUnknown *pUnkSite ) { IObjectWithSiteImpl::SetSite(pUnkSite); if (pUnkSite) { // hook GetTlsData()->bho=this; if (!m_Hook) { m_Hook=SetWindowsHookEx(WH_CBT,HookExplorer,NULL,GetCurrentThreadId()); } CComQIPtr pProvider=pUnkSite; if (pProvider) { pProvider->QueryService(SID_SShellBrowser,IID_IShellBrowser,(void**)&m_pBrowser); // listen for web browser notifications. we only care about DISPID_NAVIGATECOMPLETE2 and DISPID_ONQUIT pProvider->QueryService(SID_SWebBrowserApp,IID_IWebBrowser2,(void**)&m_pWebBrowser); if (m_pWebBrowser) { if (DispEvent1::m_dwEventCookie==0xFEFEFEFE) // ATL's event cookie is 0xFEFEFEFE when the sink is not advised DispEvent1::DispEventAdvise(m_pWebBrowser,&DIID_DWebBrowserEvents2); CRegKey regKey; if (regKey.Open(HKEY_CURRENT_USER,GetSettingsRegPath())!=ERROR_SUCCESS) regKey.Create(HKEY_CURRENT_USER,GetSettingsRegPath()); DWORD val; if (regKey.QueryDWORDValue(L"ShowedToolbar",val)!=ERROR_SUCCESS || !val) { CComVariant name(L"{553891B7-A0D5-4526-BE18-D3CE461D6310}"); CComVariant show(true); if (GetSettingBool(L"NoInitialToolbar") || SUCCEEDED(m_pWebBrowser->ShowBrowserBar(&name,&show,NULL))) regKey.SetDWORDValue(L"ShowedToolbar",1); } } HWND status; if (m_pBrowser && SUCCEEDED(m_pBrowser->GetControlWindow(FCW_STATUS,&status))) { m_TopWindow=GetAncestor(status,GA_ROOT); if (!GetProp(m_TopWindow,g_LoadedSettingsAtom)) { SetProp(m_TopWindow,g_LoadedSettingsAtom,(HANDLE)1); LoadSettings(); } bool bWin8=(GetWinVersion()>=WIN_VER_WIN8); m_UpButtonIndex=bWin8?0:GetSettingInt(L"ShowUpButton"); bool bShowCaption=!bWin8 && GetSettingBool(L"ShowCaption"); bool bShowIcon=!bWin8 && GetSettingBool(L"ShowIcon"); if (m_TopWindow && (bShowCaption || bShowIcon)) { // show the title and the icon for the main Explorer window DWORD flags=WTNCA_NODRAWCAPTION|WTNCA_NODRAWICON; if (bShowCaption) flags&=~WTNCA_NODRAWCAPTION; if (bShowIcon) flags&=~WTNCA_NODRAWICON; SetWindowThemeNonClientAttributes(m_TopWindow,WTNCA_NODRAWCAPTION|WTNCA_NODRAWICON,flags); } // find the TravelBand and the rebar HWND band=NULL, rebar=NULL; if (m_TopWindow) band=FindChildWindow(m_TopWindow,L"TravelBand"); if (band) rebar=GetParent(band); bool bRedrawRebar=false; m_Rebar=rebar; m_AltD=0; if (rebar && GetSettingBool(L"HideSearch")) { // to remove the Search box, first find the band with ID=2 (or 4 for Win8). Then disable the child control and hide the band int idx=(int)SendMessage(rebar,RB_IDTOINDEX,bWin8?4:2,0); if (idx>=0) { REBARBANDINFO info={sizeof(info),RBBIM_CHILD}; SendMessage(rebar,RB_GETBANDINFO,idx,(LPARAM)&info); if (info.hwndChild) EnableWindow(info.hwndChild,FALSE); SendMessage(rebar,RB_SHOWBAND,idx,FALSE); bRedrawRebar=true; } } if (rebar && m_UpButtonIndex) { // find the toolbar HWND toolbar=FindWindowEx(band,NULL,TOOLBARCLASSNAME,NULL); RECT rc; GetClientRect(toolbar,&rc); bool bDef; int size=GetSettingInt(L"UpIconSize",bDef); if (bDef) size=rc.bottom; m_Toolbar.Create(rebar,NULL,NULL,WS_CHILD|TBSTYLE_TOOLTIPS|TBSTYLE_FLAT|TBSTYLE_CUSTOMERASE|CCS_NODIVIDER|CCS_NOPARENTALIGN|CCS_NORESIZE); m_Toolbar.SendMessage(TB_SETEXTENDEDSTYLE,0,TBSTYLE_EX_MIXEDBUTTONS); m_Toolbar.SendMessage(TB_BUTTONSTRUCTSIZE,sizeof(TBBUTTON)); m_Toolbar.SendMessage(TB_SETMAXTEXTROWS,1); std::vector modules; CString str=GetSettingString(L"UpIconNormal"); m_IconNormal=str?LoadIcon(size,str,modules):NULL; if (m_IconNormal) { str=GetSettingString(L"UpIconHot"); m_IconHot=LoadIcon(size,str,modules); str=GetSettingString(L"UpIconPressed"); m_IconPressed=LoadIcon(size,str,modules); str=GetSettingString(L"UpIconDisabled"); m_IconDisabled=LoadIcon(size,str,modules); if (!m_IconDisabled) m_IconDisabled=CreateDisabledIcon(m_IconNormal,size); } else { m_IconNormal=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(IDI_UP2NORMAL),IMAGE_ICON,size,size,LR_DEFAULTCOLOR); m_IconHot=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(IDI_UP2HOT),IMAGE_ICON,size,size,LR_DEFAULTCOLOR); m_IconPressed=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(IDI_UP2PRESSED),IMAGE_ICON,size,size,LR_DEFAULTCOLOR); m_IconDisabled=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(IDI_UP2DISABLED),IMAGE_ICON,size,size,LR_DEFAULTCOLOR); } for (std::vector::const_iterator it=modules.begin();it!=modules.end();++it) FreeLibrary(*it); TBBUTTON button={I_IMAGENONE,1,TBSTATE_ENABLED}; m_Toolbar.SendMessage(TB_ADDBUTTONS,1,(LPARAM)&button); m_Toolbar.SendMessage(TB_SETBUTTONSIZE,0,MAKELONG(size,size)); m_bRemapBands=true; SetWindowSubclass(rebar,SubclassRebarProc,(UINT_PTR)this,(DWORD_PTR)m_Toolbar.m_hWnd); REBARBANDINFO info={sizeof(info),RBBIM_CHILD|RBBIM_ID|RBBIM_CHILDSIZE|RBBIM_IDEALSIZE|RBBIM_SIZE|RBBIM_STYLE}; info.fStyle=RBBS_HIDETITLE|RBBS_NOGRIPPER|RBBS_FIXEDSIZE; info.hwndChild=m_Toolbar.m_hWnd; info.cxIdeal=info.cx=info.cxMinChild=size; info.cyMinChild=size; info.wID='UBTN'; SendMessage(rebar,RB_INSERTBAND,m_UpButtonIndex-1,(LPARAM)&info); bRedrawRebar=true; } if (rebar) { int AddressBarHistory=GetSettingInt(L"AddressBarHistory"); HWND progress=NULL; HWND breadcrumbs=FindChildWindow(rebar,L"Breadcrumb Parent"); if (breadcrumbs) { progress=GetParent(breadcrumbs); breadcrumbs=FindWindowEx(breadcrumbs,NULL,TOOLBARCLASSNAME,NULL); m_AltD=(char)GetSettingString(L"AddressAltD")[0]; if (m_AltD>='a' && m_AltD<='z') m_AltD+='A'-'a'; if (m_AltD<'A' || m_AltD>'Z') m_AltD=0; } m_Breadcrumbs=breadcrumbs; if (GetSettingBool(L"DisableBreadcrumbs")) { // "hide" the breadcrumbs. no, not really. instead of hiding the breadcrumbs we just make them show the full path as text if (breadcrumbs) { m_bNoBreadcrumbs=true; SetWindowSubclass(breadcrumbs,SubclassBreadcrumbProc,(UINT_PTR)this,0); } } if (progress && AddressBarHistory) { m_Progress=progress; m_ComboBox=FindWindowEx(progress,NULL,WC_COMBOBOXEX,NULL); SetWindowSubclass(progress,SubclassProgressProc,(UINT_PTR)this,AddressBarHistory); m_NavigateMsg=RegisterWindowMessage(L"OpenShell.Navigate"); } } if (bRedrawRebar) RedrawWindow(rebar,NULL,NULL,RDW_UPDATENOW|RDW_ALLCHILDREN); m_UpHotkey=GetSettingInt(L"UpHotkey"); if ((m_AltD || m_UpHotkey) && !m_HookKbd) { m_HookKbd=SetWindowsHookEx(WH_KEYBOARD,HookKeyboard,NULL,GetCurrentThreadId()); } if ((bWin8 && GetSettingBool(L"ShowStatusBar")) || (!bWin8 && GetSettingBool(L"ShowFreeSpace"))) { DWORD flags=(GetSettingBool(L"ShowInfoTip")?SHOW_INFOTIP:0)|(GetSettingBool(L"ShowFreeSpace")?SHOW_FREE_SPACE:0); if (bWin8) { RECT rc; GetWindowRect(status,&rc); m_Status8=CreateWindow(STATUSCLASSNAME,NULL,WS_CHILD|WS_VISIBLE|SBARS_SIZEGRIP|SBARS_TOOLTIPS,0,0,rc.right-rc.left,rc.bottom-rc.top,GetParent(status),NULL,g_Instance,NULL); HDC hdc=CreateCompatibleDC(NULL); HGDIOBJ font0=SelectObject(hdc,(HFONT)SendMessage(m_Status8,WM_GETFONT,0,0)); HFONT font=CreateFontSetting(GetSettingString(L"StatusBarFont"),GetDeviceCaps(hdc,LOGPIXELSY)); if (font) { SendMessage(m_Status8,WM_SETFONT,(WPARAM)font,0); SelectObject(hdc,font); TEXTMETRIC met; GetTextMetrics(hdc,&met); int height=GetSystemMetrics(SM_CXSMICON); if (heightGetZoneAttributes(zone,&attributes))) { if (GetTextExtentPoint32(hdc,attributes.szDisplayName,Strlen(attributes.szDisplayName),&size) && m_ZoneWidthbho=NULL; if (m_Hook) UnhookWindowsHookEx(m_Hook); m_Hook=NULL; if (m_HookKbd) UnhookWindowsHookEx(m_HookKbd); m_HookKbd=NULL; if (m_Status) RemoveWindowSubclass(m_Status,SubclassStatusProc,(UINT_PTR)this); m_Status=NULL; if (m_Status8) RemoveWindowSubclass(m_Status8,SubclassStatusProc8,(UINT_PTR)this); m_Status8=NULL; if (m_DUIView) RemoveWindowSubclass(m_DUIView,SubclassDUIViewProc,(UINT_PTR)this); m_DUIView=NULL; if (m_Progress) RemoveWindowSubclass(m_Progress,SubclassProgressProc,(UINT_PTR)this); m_Progress=NULL; if (m_bNoBreadcrumbs) RemoveWindowSubclass(m_Breadcrumbs,SubclassBreadcrumbProc,(UINT_PTR)this); m_Breadcrumbs=NULL; m_bNoBreadcrumbs=false; if (m_Rebar) RemoveWindowSubclass(m_Rebar,SubclassRebarProc,(UINT_PTR)this); m_pBrowser=NULL; if (m_pWebBrowser && DispEvent1::m_dwEventCookie!=0xFEFEFEFE) DispEvent1::DispEventUnadvise(m_pWebBrowser,&DIID_DWebBrowserEvents2); m_pWebBrowser=NULL; if (m_pWebDoc && DispEvent2::m_dwEventCookie!=0xFEFEFEFE) DispEvent2::DispEventUnadvise(m_pWebDoc,&DIID_DShellFolderViewEvents); m_pWebDoc=NULL; if (m_Toolbar.m_hWnd) m_Toolbar.DestroyWindow(); if (m_IconNormal) DestroyIcon(m_IconNormal); m_IconNormal=NULL; if (m_IconHot) DestroyIcon(m_IconHot); m_IconHot=NULL; if (m_IconPressed) DestroyIcon(m_IconPressed); m_IconPressed=NULL; if (m_IconDisabled) DestroyIcon(m_IconDisabled); m_IconDisabled=NULL; if (m_CurIcon) DestroyIcon(m_CurIcon); m_CurIcon=NULL; m_CurPidl.Clear(); m_NavigatePidl.Clear(); if (m_TopWindow) RemoveProp(m_TopWindow,g_LoadedSettingsAtom); m_TopWindow=NULL; if (m_Balloon) { DestroyWindow(m_Balloon); m_Balloon=NULL; } for (std::map::iterator it=m_ZoneIconCache.begin();it!=m_ZoneIconCache.end();++it) DestroyIcon(it->second); } ClearComboItems(); return S_OK; } void CExplorerBHO::ClearComboItems( void ) { for (std::vector::iterator it=m_ComboItems.begin();it!=m_ComboItems.end();++it) ILFree(it->pidl); m_ComboItems.clear(); if (m_ComboBox.m_hWnd) { for (int i=(int)m_ComboBox.SendMessage(CB_GETCOUNT)-1;i>=0;i--) m_ComboBox.SendMessage(CBEM_DELETEITEM,i); } } STDMETHODIMP CExplorerBHO::OnDocumentComplete( IDispatch *pDisp, VARIANT *URL ) { // this is called when the current folder changes. disable the Up button if this is the desktop folder bool bDesktop=false; m_CurPath[0]=0; if (m_CurIcon) DestroyIcon(m_CurIcon); m_CurIcon=NULL; m_CurPidl.Clear(); if (m_pWebBrowser && (m_Status || m_Status8)) { if (m_pWebDoc && DispEvent2::m_dwEventCookie!=0xFEFEFEFE) DispEvent2::DispEventUnadvise(m_pWebDoc,&DIID_DShellFolderViewEvents); m_pWebDoc=NULL; m_pWebBrowser->get_Document(&m_pWebDoc); if (m_pWebDoc && DispEvent2::m_dwEventCookie==0xFEFEFEFE) // ATL's event cookie is 0xFEFEFEFE when the sink is not advised DispEvent2::DispEventAdvise(m_pWebDoc,&DIID_DShellFolderViewEvents); if (DispEvent2::m_dwEventCookie!=0xFEFEFEFE) { if (m_Status) SendMessage(m_Status,WM_CLEAR,0,0); if (m_Status8) SendMessage(m_Status8,WM_CLEAR,0,0); } } if (m_pBrowser) { CComPtr pView; m_pBrowser->QueryActiveShellView(&pView); if (pView) { CComQIPtr pFolderView=pView; if (pFolderView) { if (GetSettingBool(L"ShowHeaders")) { // *********************************************************************** // The code to turn on the headers is borrowed from the Explorer7Fixes project under the terms of the MIT license: // http://github.com/ijprest/Explorer7Fixes - Copyright (c) 2010 Ian Prest CComQIPtr pView2=pFolderView; if (pView2) { // Turn on the sort header! pView2->SetCurrentFolderFlags(FWF_NOHEADERINALLVIEWS,0); // It seems the ItemsView doesn't respect the FWF_NOHEADERINALLVIEWS flag // until the view has been refreshed. Rather than call Refresh, we just // briefly change the view mode and change it back. FOLDERVIEWMODE viewMode; int itemSize=0; pView2->GetViewModeAndIconSize(&viewMode,&itemSize); if (viewMode!=FVM_DETAILS) { pView2->SetViewModeAndIconSize(viewMode==FVM_LIST?FVM_SMALLICON:FVM_LIST,itemSize); pView2->SetViewModeAndIconSize(viewMode,itemSize); } } // *********************************************************************** } CComPtr pFolder; pFolderView->GetFolder(IID_IPersistFolder2,(void**)&pFolder); if (pFolder && SUCCEEDED(pFolder->GetCurFolder(&m_CurPidl)) && m_CurPidl) { if (ILIsEmpty(m_CurPidl)) bDesktop=true; // only the top level has empty PIDL // find path and icon // it is possible to get the path and icon from the caption of the main window, but there are 2 problems: // 1) on Vista the icon is wrong. after you navigate to a new folder the icon switches to some default image and doesn't change any more // 2) if the user has not checked "display full path in title bar", the caption of the main window is just the current folder name and not the full path // so do it the hard way and grab it from SHGetNameFromIDList and SHGetFileInfo if (m_bNoBreadcrumbs) { CComString pPath; if (SUCCEEDED(SHGetNameFromIDList(m_CurPidl,SIGDN_DESKTOPABSOLUTEEDITING,&pPath))) Strcpy(m_CurPath,_countof(m_CurPath),pPath); else if (SUCCEEDED(SHGetNameFromIDList(m_CurPidl,SIGDN_FILESYSPATH,&pPath))) Strcpy(m_CurPath,_countof(m_CurPath),pPath); // just in case DESKTOPABSOLUTE fails let's try the FILESYSPATH. probably not needed SHFILEINFO info; if (SUCCEEDED(SHGetFileInfo((LPCTSTR)(PIDLIST_ABSOLUTE)m_CurPidl,0,&info,sizeof(info),SHGFI_ICON|SHGFI_SMALLICON|SHGFI_PIDL))) m_CurIcon=info.hIcon; } } } } if (GetSettingBool(L"HideScrollTip")) { CComQIPtr pOptions=m_pBrowser; if (pOptions) pOptions->SetFolderViewOptions(FVO_NOSCROLLTIPS,FVO_NOSCROLLTIPS); } } if (m_Toolbar.m_hWnd) m_Toolbar.SendMessage(TB_ENABLEBUTTON,1,bDesktop?0:1); if (m_pZoneManager && m_pSecurityManager) { wchar_t text[256]; text[0]=0; HICON hIcon=NULL; if (URL && URL->vt==VT_BSTR) { DWORD zone; if (FAILED(m_pSecurityManager->MapUrlToZone(URL->bstrVal,&zone,0))) zone=URLZONE_LOCAL_MACHINE; ZONEATTRIBUTES attributes={sizeof(attributes)}; if (SUCCEEDED(m_pZoneManager->GetZoneAttributes(zone,&attributes))) { Strcpy(text,_countof(text),attributes.szDisplayName); unsigned int key=CalcFNVHash(attributes.szIconPath); std::map::const_iterator it=m_ZoneIconCache.find(key); if (it!=m_ZoneIconCache.end()) hIcon=it->second; else { wchar_t path[_MAX_PATH]; wchar_t *str=wcschr(attributes.szIconPath,'#'); if (!str) str=wcschr(attributes.szIconPath,','); int index=0; if (str) { index=_wtol(str+1); *str=0; } int iconSize=GetSystemMetrics(SM_CXSMICON); Strcpy(path,_countof(path),attributes.szIconPath); if (PathIsRelative(path)) PathFindOnPath(path,NULL); if (index==0) hIcon=(HICON)LoadImage(NULL,path,IMAGE_ICON,iconSize,iconSize,LR_LOADFROMFILE); else { HMODULE hModule=LoadLibraryEx(path,NULL,LOAD_LIBRARY_AS_DATAFILE|LOAD_LIBRARY_AS_IMAGE_RESOURCE); if (hModule) { hIcon=(HICON)LoadImage(hModule,MAKEINTRESOURCE(index),IMAGE_ICON,iconSize,iconSize,0); FreeLibrary(hModule); } } m_ZoneIconCache[key]=hIcon; } } } SendMessage(m_Status8,SB_SETTEXT,PART_ZONE,(LPARAM)text); SendMessage(m_Status8,SB_SETICON,PART_ZONE,(LPARAM)hIcon); } return S_OK; } STDMETHODIMP CExplorerBHO::OnQuit( void ) { if (m_pWebBrowser && DispEvent1::m_dwEventCookie!=0xFEFEFEFE) // ATL's event cookie is 0xFEFEFEFE, when the sink is not advised DispEvent1::DispEventUnadvise(m_pWebBrowser,&DIID_DWebBrowserEvents2); if (m_pWebDoc && DispEvent2::m_dwEventCookie!=0xFEFEFEFE) DispEvent2::DispEventUnadvise(m_pWebDoc,&DIID_DShellFolderViewEvents); return S_OK; } STDMETHODIMP CExplorerBHO::OnSelChanged( void ) { if (m_Status) SendMessage(m_Status,WM_CLEAR,0,0); if (m_Status8) SendMessage(m_Status8,WM_CLEAR,0,0); return S_OK; } bool ShowTreeProperties( HWND hwndTree ) { CAbsolutePidl pidl; GetTreeItemPidl(TreeView_GetSelection(hwndTree),hwndTree,&pidl); if (pidl) { // show properties SHELLEXECUTEINFO execute={sizeof(execute),SEE_MASK_IDLIST|SEE_MASK_INVOKEIDLIST,NULL,L"properties"}; execute.lpIDList=pidl; execute.nShow=SW_SHOWNORMAL; ShellExecuteEx(&execute); return true; } return false; }