From aa2238803d83b0be0843da86d12a500e46faf2ad Mon Sep 17 00:00:00 2001 From: Lello <111449126+lellomele@users.noreply.github.com> Date: Wed, 4 Nov 2026 11:40:57 +0100 Subject: [PATCH] Improve Start Menu responsiveness and cache resilience Defer new-program highlighting until shortcuts are ready and allocate temporary icon lists on demand. Validate cached records, preserve the last complete cache on write failures, and leave live entries intact when clearing the disk cache. Make worker startup and shutdown tolerate partial initialization and cancellation. --- Src/StartMenu/StartMenuDLL/ItemManager.cpp | 340 ++++++++++--------- Src/StartMenu/StartMenuDLL/ItemManager.h | 11 +- Src/StartMenu/StartMenuDLL/MenuContainer.cpp | 74 ++-- Src/StartMenu/StartMenuDLL/MenuContainer.h | 3 + Src/StartMenu/StartMenuDLL/ProgramsTree.cpp | 26 ++ Src/StartMenu/StartMenuDLL/ProgramsTree.h | 1 + Src/StartMenu/StartMenuDLL/SearchManager.cpp | 23 +- Src/StartMenu/StartMenuDLL/SettingsUI.cpp | 4 +- Src/StartMenu/StartMenuDLL/StartMenuDLL.rc | 2 + Src/StartMenu/StartMenuDLL/resource.h | 2 + 10 files changed, 300 insertions(+), 186 deletions(-) diff --git a/Src/StartMenu/StartMenuDLL/ItemManager.cpp b/Src/StartMenu/StartMenuDLL/ItemManager.cpp index 27b2d17ed..ce62d87e7 100644 --- a/Src/StartMenu/StartMenuDLL/ItemManager.cpp +++ b/Src/StartMenu/StartMenuDLL/ItemManager.cpp @@ -482,17 +482,23 @@ void CItemManager::LoadIconData::Init( void ) m_IconSizes[4]=LARGE_ICON_SIZE-2; m_IconSizes[5]=EXTRA_LARGE_ICON_SIZE-2; - for (int i=0;i<_countof(m_TempLists);i++) + if (FAILED(m_pFactory.CoCreateInstance(CLSID_WICImagingFactory))) + m_pFactory.CoCreateInstance(CLSID_WICImagingFactory1); +} + +IImageList2 *CItemManager::LoadIconData::GetTempList( int index ) +{ + if (!m_TempLists[index]) { - m_TempLists[i]=ImageList_Create(m_IconSizes[i],m_IconSizes[i],ILC_COLOR32,0,1); - if (m_TempLists[i]) + const int size=m_IconSizes[index]; + m_TempLists[index]=ImageList_Create(size,size,ILC_COLOR32,0,1); + if (m_TempLists[index]) { - ImageList_SetImageCount(m_TempLists[i],1); - HIMAGELIST_QueryInterface(m_TempLists[i],IID_IImageList2,(void**)&m_pTempLists[i]); + ImageList_SetImageCount(m_TempLists[index],1); + HIMAGELIST_QueryInterface(m_TempLists[index],IID_IImageList2,(void**)&m_pTempLists[index]); } } - if (FAILED(m_pFactory.CoCreateInstance(CLSID_WICImagingFactory))) - m_pFactory.CoCreateInstance(CLSID_WICImagingFactory1); + return m_pTempLists[index]; } void CItemManager::LoadIconData::Close( void ) @@ -502,6 +508,7 @@ void CItemManager::LoadIconData::Close( void ) m_pTempLists[i]=NULL; if (m_TempLists[i]) ImageList_Destroy(m_TempLists[i]); + m_TempLists[i]=NULL; } m_pFactory=NULL; } @@ -673,6 +680,11 @@ void CItemManager::Close( void ) } m_LoadingStage=LOAD_STOPPED; + // No worker can replace icons after the joins above. + for (HBITMAP bitmap : m_OldBitmaps) + DeleteObject(bitmap); + m_OldBitmaps.clear(); + for (std::multimap::const_iterator it=m_IconInfos.begin();it!=m_IconInfos.end();++it) { if (it->second.bitmap) @@ -1254,28 +1266,6 @@ void CItemManager::QueueItemInfo( ItemInfo *pInfo, int refreshFlags ) } } -void CItemManager::WaitForShortcuts( const POINT &balloonPos ) -{ - if (m_PreloadItemsThreadId) - { - if (WaitForSingleObject(m_DoneEvent,1000)!=WAIT_TIMEOUT) - return; - CWindow tooltip=CreateWindowEx(WS_EX_TOPMOST|WS_EX_TOOLWINDOW|(IsLanguageRTL()?WS_EX_LAYOUTRTL:0),TOOLTIPS_CLASS,NULL,WS_POPUP|TTS_BALLOON|TTS_NOPREFIX,0,0,0,0,NULL,NULL,g_Instance,NULL); - tooltip.SendMessage(TTM_SETMAXTIPWIDTH,0,500); - TOOLINFO tool={sizeof(tool),TTF_TRANSPARENT|TTF_TRACK|(IsLanguageRTL()?TTF_RTLREADING:0U)}; - tool.uId=1; - CString message=LoadStringEx(IDS_MENU_BUSY); - tool.lpszText=(LPWSTR)(const wchar_t*)message; - tooltip.SendMessage(TTM_ADDTOOL,0,(LPARAM)&tool); - tooltip.SendMessage(TTM_SETTITLE,TTI_INFO,(LPARAM)(const wchar_t*)LoadStringEx(IDS_APP_TITLE)); - tooltip.SendMessage(TTM_TRACKPOSITION,0,MAKELONG(balloonPos.x,balloonPos.y)); - tooltip.SendMessage(TTM_TRACKACTIVATE,TRUE,(LPARAM)&tool); - tooltip.SendMessage(TTM_UPDATE); - WaitForSingleObject(m_DoneEvent,INFINITE); - tooltip.DestroyWindow(); - } -} - bool CItemManager::IsTaskbarPinned( const wchar_t *appid ) { Assert(GetCurrentThreadId()==m_MainThreadId); @@ -1389,12 +1379,22 @@ bool CItemManager::IsPathUsed( CRegKey ®Key, const wchar_t *path, const FILET return false; } -void CItemManager::UpdateNewPrograms( const POINT &balloonPos ) +void CItemManager::ResetNewPrograms( void ) { Assert(GetCurrentThreadId()==m_MainThreadId); m_NewPrograms.clear(); m_NewProgramRoots.clear(); m_bHasNewPrograms[0]=m_bHasNewPrograms[1]=m_bHasNewApps[0]=m_bHasNewApps[1]=false; +} + +bool CItemManager::AreShortcutsReady( void ) const +{ + return !m_PreloadItemsThread || !m_DoneEvent || WaitForSingleObject(m_DoneEvent,0)!=WAIT_TIMEOUT; +} + +void CItemManager::UpdateNewPrograms( void ) +{ + ResetNewPrograms(); #ifdef FORCE_ALL_NEW m_bHasNewPrograms[0]=m_bHasNewPrograms[1]=m_bHasNewApps[0]=m_bHasNewApps[1]=true; #endif @@ -1456,7 +1456,6 @@ void CItemManager::UpdateNewPrograms( const POINT &balloonPos ) LONGLONG curTime; GetSystemTimeAsFileTime((FILETIME*)&curTime); - WaitForShortcuts(balloonPos); LARGE_INTEGER newestProgram={0}, newestApp={0}; { RWLock lock(this,true,RWLOCK_ITEMS); @@ -1596,7 +1595,7 @@ void CItemManager::UpdateNewPrograms( const POINT &balloonPos ) if (newestProgram.QuadPart || newestApp.QuadPart) { CRegKey regKey; - ULONGLONG val1, val2; + ULONGLONG val1=0, val2=0; if (regKey.Open(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu",KEY_READ)==ERROR_SUCCESS) { if (regKey.QueryQWORDValue(L"LastProgramsTime",val1)!=ERROR_SUCCESS) @@ -2207,9 +2206,9 @@ HICON CItemManager::LoadShellIcon( int index, int iconSize ) return NULL; HICON hIcon; - if (data.m_pTempLists[sizeIndex]) + if (IImageList2 *tempList=data.GetTempList(sizeIndex)) { - if (SUCCEEDED(data.m_pTempLists[sizeIndex]->ReplaceFromImageList(0,pImageList,index,NULL,0)) && SUCCEEDED(data.m_pTempLists[sizeIndex]->GetIcon(0,ILD_TRANSPARENT,&hIcon))) + if (SUCCEEDED(tempList->ReplaceFromImageList(0,pImageList,index,NULL,0)) && SUCCEEDED(tempList->GetIcon(0,ILD_TRANSPARENT,&hIcon))) { AddTrackedIcon(hIcon); return hIcon; @@ -2725,7 +2724,7 @@ void CItemManager::LoadFolderItems( IShellItem *pFolder, int refreshFlags, int l refreshFlags&=~INFO_ICON; CComPtr pChild; CAbsolutePidl childPidl; - while (enumerator.GetNext(pChild,childPidl)) + while (m_LoadingStage==LOAD_LOADING && enumerator.GetNext(pChild,childPidl)) { if (location==CItemManager::LOCATION_DESKTOP) { @@ -2944,7 +2943,7 @@ void CItemManager::PreloadItemsThread( void ) if (!FindNextChangeNotification(handles[dir])) ResetEvent(handles[dir]); // so we don't wake on this event again - Sleep(1000); // hopefully whatever file operation is in progress will finish in a second + if (WaitForSingleObject(m_ExitEvent,1000)!=WAIT_TIMEOUT) break; } for (int i=0;i1024*1024) return false; if (size>0) { PIDLIST_ABSOLUTE pidl=(PIDLIST_ABSOLUTE)CoTaskMemAlloc(size); @@ -3114,13 +3116,30 @@ static bool ReadCacheFile( HANDLE file, CAbsolutePidl &data, int size ) ILFree(pidl); return false; } - data.Attach(pidl); + // Validate the item lengths and the final zero before passing the list to the shell. + const BYTE *bytes=reinterpret_cast(pidl); + int offset=0; + while (size-offset>=sizeof(USHORT)) + { + USHORT itemSize; + memcpy(&itemSize,bytes+offset,sizeof(itemSize)); + if (!itemSize && size-offset==sizeof(USHORT)) + { + data.Attach(pidl); + return true; + } + if (itemSizesize-offset) break; + offset+=itemSize; + } + ILFree(pidl); + return false; } return true; } static bool ReadCacheFile( HANDLE file, CString &data, int len ) { + if (len<0 || len>32767) return false; if (len>0) { DWORD q; @@ -3134,10 +3153,13 @@ static bool ReadCacheFile( HANDLE file, CString &data, int len ) return true; } +static bool ValidCacheBitmapSize( int width, int height ) +{ + return width>0 && width<=1024 && height>0 && height<=1024; +} + static bool ReadCacheFile( HANDLE file, HDC hdc, HBITMAP &data, int width, int height ) { - std::vector bits(width*height); - if (bits.empty()) return false; BITMAPINFO bi={0}; bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth=width; @@ -3146,66 +3168,74 @@ static bool ReadCacheFile( HANDLE file, HDC hdc, HBITMAP &data, int width, int h bi.bmiHeader.biBitCount=32; DWORD q; int size=width*height*4; - if (!ReadFile(file,&bits[0],size,&q,NULL) || q!=size) - return false; unsigned int *pBits; data=CreateDIBSection(hdc,&bi,DIB_RGB_COLORS,(void**)&pBits,NULL,0); if (!data) return false; - memcpy(pBits,&bits[0],size); + if (!ReadFile(file,pBits,size,&q,NULL) || q!=size) + { + DeleteObject(data); + data=NULL; + return false; + } return true; } -static void WriteCacheFile( HANDLE file, DWORD data ) +class CacheWriter { - DWORD q; - WriteFile(file,&data,sizeof(data),&q,NULL); -} + HANDLE m_File; + bool m_Ok=true; -static void WriteCacheFile( HANDLE file, const IconData &data ) -{ - DWORD q; - WriteFile(file,&data,sizeof(data),&q,NULL); -} + void Write( const void *data, DWORD size ) + { + if (!m_Ok || !size) return; + DWORD written=0; + m_Ok=WriteFile(m_File,data,size,&written,NULL) && written==size; + } -static void WriteCacheFile( HANDLE file, const ItemData &data ) -{ - DWORD q; - WriteFile(file,&data,sizeof(data),&q,NULL); -} +public: + explicit CacheWriter( HANDLE file ) : m_File(file) {} -static void WriteCacheFile( HANDLE file, PIDLIST_ABSOLUTE data, int size ) -{ - if (size>0) + template void WriteValue( const T &data ) { - DWORD q; - WriteFile(file,data,size,&q,NULL); + Write(&data,sizeof(data)); } -} -static void WriteCacheFile( HANDLE file, const CString &data ) -{ - if (!data.IsEmpty()) + void WriteValue( const CString &data ) { - DWORD q; - WriteFile(file,(const wchar_t*)data,data.GetLength()*2,&q,NULL); + Write((const wchar_t*)data,data.GetLength()*sizeof(wchar_t)); } -} -static void WriteCacheFile( HANDLE file, HDC hdc, HBITMAP data, int width, int height ) -{ - std::vector bits(width*height); - if (bits.empty()) return; - BITMAPINFO bi={0}; - bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); - bi.bmiHeader.biWidth=width; - bi.bmiHeader.biHeight=height; - bi.bmiHeader.biPlanes=1; - bi.bmiHeader.biBitCount=32; - GetDIBits(hdc,data,0,height,&bits[0],&bi,DIB_RGB_COLORS); - DWORD q; - WriteFile(file,&bits[0],width*height*4,&q,NULL); -} + void WriteValue( PIDLIST_ABSOLUTE data, int size ) + { + Write(data,size); + } + + void WriteValue( HDC hdc, HBITMAP data, int width, int height ) + { + if (!m_Ok) return; + if (!hdc || !ValidCacheBitmapSize(width,height)) + { + m_Ok=false; + return; + } + std::vector bits(width*height); + BITMAPINFO bi={0}; + bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); + bi.bmiHeader.biWidth=width; + bi.bmiHeader.biHeight=height; + bi.bmiHeader.biPlanes=1; + bi.bmiHeader.biBitCount=32; + m_Ok=GetDIBits(hdc,data,0,height,bits.data(),&bi,DIB_RGB_COLORS)==height; + Write(bits.data(),width*height*sizeof(DWORD)); + } + + bool Finish( void ) + { + bool complete=m_Ok && FlushFileBuffers(m_File); + return CloseHandle(m_File) && complete; + } +}; bool CItemManager::CompareModuleTimeStamp( const CString &PATH, const FILETIME ×tamp, std::vector &modules ) { @@ -3282,6 +3312,11 @@ void CItemManager::LoadCacheFile( void ) bError=true; break; } + if (!ValidCacheBitmapSize(data.bitmapW,data.bitmapH)) + { + bError=true; + break; + } if (size1==SMALL_ICON_SIZE && size2==LARGE_ICON_SIZE && size3==EXTRA_LARGE_ICON_SIZE && CompareModuleTimeStamp(info.PATH,info.timestamp,modules)) { if (!ReadCacheFile(file,hdc,info.bitmap,data.bitmapW,data.bitmapH)) @@ -3367,21 +3402,26 @@ void CItemManager::LoadCacheFile( void ) tag=ReadCacheFile(file); } - if (tag=='BLAK') + if (!bError && tag=='BLAK') { - int count=ReadCacheFile(file); - for (int i=0;i::const_iterator it=m_IconInfos.begin();it!=m_IconInfos.end();++it) { @@ -3396,25 +3436,31 @@ void CItemManager::LoadCacheFile( void ) DWORD CALLBACK CItemManager::SaveCacheFileThread( void *param ) { CItemManager *pThis=(CItemManager*)param; - wchar_t path[_MAX_PATH]=L"%LOCALAPPDATA%\\OpenShell"; - DoEnvironmentSubst(path,_MAX_PATH); - SHCreateDirectory(NULL,path); - Strcat(path,_countof(path),L"\\DataCache.tmp"); - HANDLE file=CreateFile(path,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); - if (file==INVALID_HANDLE_VALUE) return 0; - - WriteCacheFile(file,'CLSH'); - WriteCacheFile(file,GetVersionEx(g_Instance)); - WriteCacheFile(file,CACHE_FILE_VERSION); - WriteCacheFile(file,SMALL_ICON_SIZE); - WriteCacheFile(file,LARGE_ICON_SIZE); - WriteCacheFile(file,EXTRA_LARGE_ICON_SIZE); + wchar_t directory[_MAX_PATH]=L"%LOCALAPPDATA%\\OpenShell"; + DoEnvironmentSubst(directory,_MAX_PATH); + SHCreateDirectory(NULL,directory); + wchar_t path[_MAX_PATH]; + if (!GetTempFileName(directory,L"OSC",0,path)) return 0; + HANDLE file=CreateFile(path,GENERIC_WRITE,0,NULL,TRUNCATE_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); + if (file==INVALID_HANDLE_VALUE) + { + DeleteFile(path); + return 0; + } + + CacheWriter writer(file); + writer.WriteValue('CLSH'); + writer.WriteValue(GetVersionEx(g_Instance)); + writer.WriteValue(CACHE_FILE_VERSION); + writer.WriteValue(SMALL_ICON_SIZE); + writer.WriteValue(LARGE_ICON_SIZE); + writer.WriteValue(EXTRA_LARGE_ICON_SIZE); { wchar_t languages[100]; DWORD size=0; DWORD len=_countof(languages); GetUserPreferredUILanguages(MUI_LANGUAGE_ID,&size,languages,&len); - WriteCacheFile(file,CalcFNVHash(languages,len*2,FNV_HASH0)); + writer.WriteValue(CalcFNVHash(languages,len*2,FNV_HASH0)); } std::vector*> iconInfos; @@ -3422,7 +3468,7 @@ DWORD CALLBACK CItemManager::SaveCacheFileThread( void *param ) RWLock lock(pThis,false,RWLOCK_ICONS); for (std::multimap::const_iterator it=pThis->m_IconInfos.begin();it!=pThis->m_IconInfos.end();++it) { - if (!it->second.bTemp && !it->second.bMetro && !it->second.PATH.IsEmpty() && it->second.PATH[1]!='#' && it->first!=0) + if (!it->second.bTemp && !it->second.bMetro && it->second.PATH.GetLength()>1 && it->second.PATH[1]!='#' && it->first!=0) iconInfos.push_back(&*it); } } @@ -3454,15 +3500,15 @@ DWORD CALLBACK CItemManager::SaveCacheFileThread( void *param ) data.sizeType=(*it)->second.sizeType; data.timestamp=(*it)->second.timestamp; data.PATHLen=(*it)->second.PATH.GetLength(); - BITMAP bmp; + BITMAP bmp={0}; GetObject((*it)->second.bitmap,sizeof(bmp),&bmp); data.bitmapW=bmp.bmWidth; data.bitmapH=bmp.bmHeight; - WriteCacheFile(file,'ICON'); - WriteCacheFile(file,data); - WriteCacheFile(file,(*it)->second.PATH); - WriteCacheFile(file,hdc,(*it)->second.bitmap,data.bitmapW,data.bitmapH); + writer.WriteValue('ICON'); + writer.WriteValue(data); + writer.WriteValue((*it)->second.PATH); + writer.WriteValue(hdc,(*it)->second.bitmap,data.bitmapW,data.bitmapH); } DeleteDC(hdc); @@ -3513,35 +3559,37 @@ DWORD CALLBACK CItemManager::SaveCacheFileThread( void *param ) data.iconColor=(*it)->second.iconColor; data.iconIndex=(*it)->second.iconIndex; - WriteCacheFile(file,'ITEM'); - WriteCacheFile(file,data); - WriteCacheFile(file,(*it)->second.GetLatestPidl(),data.pidlSize); - WriteCacheFile(file,(*it)->second.path); - WriteCacheFile(file,(*it)->second.PATH); - WriteCacheFile(file,(*it)->second.targetPidl,data.targetPidlSize); - WriteCacheFile(file,(*it)->second.targetPATH); - WriteCacheFile(file,(*it)->second.appid); - WriteCacheFile(file,(*it)->second.metroName); - WriteCacheFile(file,(*it)->second.iconPath); + writer.WriteValue('ITEM'); + writer.WriteValue(data); + writer.WriteValue((*it)->second.GetLatestPidl(),data.pidlSize); + writer.WriteValue((*it)->second.path); + writer.WriteValue((*it)->second.PATH); + writer.WriteValue((*it)->second.targetPidl,data.targetPidlSize); + writer.WriteValue((*it)->second.targetPATH); + writer.WriteValue((*it)->second.appid); + writer.WriteValue((*it)->second.metroName); + writer.WriteValue((*it)->second.iconPath); if (log) fwprintf(log,L"0x%08X - %s\r\n",(*it)->first,(const wchar_t*)(*it)->second.PATH); } { - WriteCacheFile(file,'BLAK'); - WriteCacheFile(file,(DWORD)blackList.size()); + writer.WriteValue('BLAK'); + writer.WriteValue((DWORD)blackList.size()); for (std::vector::const_iterator it=blackList.begin();it!=blackList.end();++it) - WriteCacheFile(file,*it); + writer.WriteValue(*it); } if (log) fclose(log); - CloseHandle(file); + bool complete=writer.Finish(); wchar_t path2[_MAX_PATH]=L"%LOCALAPPDATA%\\OpenShell\\DataCache.db"; DoEnvironmentSubst(path2,_MAX_PATH); - MoveFileEx(path,path2,MOVEFILE_REPLACE_EXISTING); + if (!complete || !MoveFileEx(path,path2,MOVEFILE_REPLACE_EXISTING)) + DeleteFile(path); return 0; } void CItemManager::SaveCacheFile( void ) { + if (m_CacheCleared) return; #ifdef DISABLE_CACHE return; #endif @@ -3564,40 +3612,22 @@ void CItemManager::SaveCacheFile( void ) m_SaveCacheThread=CreateThread(NULL,0,SaveCacheFileThread,this,0,NULL); } -void CItemManager::ClearCache( void ) +bool CItemManager::ClearCache( void ) { - // The save thread keeps pointers to persistent cache entries while serializing them. - // Let it finish before clearing the containers those pointers refer to. - if (m_SaveCacheThread) - { - WaitForSingleObject(m_SaveCacheThread,INFINITE); - CloseHandle(m_SaveCacheThread); - m_SaveCacheThread=NULL; - } - - Lock cleanupLock(this,LOCK_CLEANUP); - RWLock itemLock(this,true,RWLOCK_ITEMS); - RWLock iconLock(this,true,RWLOCK_ICONS); - - wchar_t path[_MAX_PATH]=L"%LOCALAPPDATA%\\OpenShell\\DataCache.db"; - DoEnvironmentSubst(path,_MAX_PATH); - DeleteFile(path); - - m_BlackListInfos10.clear(); - m_ItemInfos.clear(); - for (std::multimap::const_iterator it=m_IconInfos.begin();it!=m_IconInfos.end();++it) + if (m_SaveCacheThread && WaitForSingleObject(m_SaveCacheThread,INFINITE)!=WAIT_OBJECT_0) + return false; + wchar_t path[_MAX_PATH]; + DWORD length=ExpandEnvironmentStrings(L"%LOCALAPPDATA%\\OpenShell\\DataCache.db",path,_countof(path)); + if (!length || length>_countof(path) || path[0]=='%') return false; + if (!DeleteFile(path)) { - if (it->second.bitmap) - DeleteObject(it->second.bitmap); + DWORD error=GetLastError(); + if (error!=ERROR_FILE_NOT_FOUND && error!=ERROR_PATH_NOT_FOUND) return false; } - m_IconInfos.clear(); - m_MetroItemInfos10.clear(); - CreateDefaultIcons(); - ItemInfo &item=m_ItemInfos.emplace(0,ItemInfo())->second; - item.bIconOnly=true; - item.smallIcon=m_DefaultSmallIcon; - item.largeIcon=m_DefaultLargeIcon; - item.extraLargeIcon=m_DefaultExtraLargeIcon; + // Menu items and workers may still reference the in-memory cache. + // Let the next launch rebuild it rather than saving these entries again. + m_CacheCleared=true; + return true; } int CItemManager::GetIconSize(TIconSizeType iconSizeType) const diff --git a/Src/StartMenu/StartMenuDLL/ItemManager.h b/Src/StartMenu/StartMenuDLL/ItemManager.h index e10775bdf..a8fbf2753 100644 --- a/Src/StartMenu/StartMenuDLL/ItemManager.h +++ b/Src/StartMenu/StartMenuDLL/ItemManager.h @@ -34,7 +34,7 @@ class CItemManager void ResetTempIcons( void ); void LoadCacheFile( void ); void SaveCacheFile( void ); - void ClearCache( void ); + bool ClearCache( void ); static int GetDPI( bool bOverride ) { return (bOverride && s_DPIOverride)?s_DPIOverride:s_DPI; } static bool GetDPIOverride( void ) { return s_DPIOverride!=0; } @@ -178,9 +178,10 @@ class CItemManager const ItemInfo* GetLinkIcon(IShellLink* link, TIconSizeType iconSizeType); const ItemInfo *GetMetroAppInfo10( const wchar_t *appid ); void UpdateItemInfo( const ItemInfo *pInfo, int refreshFlags, bool bHasWriteLock=false ); - void WaitForShortcuts( const POINT &balloonPos ); bool IsTaskbarPinned( const wchar_t *appid ); - void UpdateNewPrograms( const POINT &balloonPos ); + void ResetNewPrograms( void ); + bool AreShortcutsReady( void ) const; + void UpdateNewPrograms( void ); bool IsNewProgram( PIDLIST_ABSOLUTE pidl, bool bFolder, bool bMetroApp ); bool HasNewPrograms( bool bReal ) { return m_bHasNewPrograms[bReal?0:1]; } bool HasNewApps( bool bReal ) { return m_bHasNewApps[bReal?0:1]; } @@ -270,14 +271,16 @@ class CItemManager HANDLE m_PreloadItemsThread; HANDLE m_RefreshInfoThread; HANDLE m_SaveCacheThread; + bool m_CacheCleared=false; DWORD m_MainThreadId, m_PreloadItemsThreadId, m_RefreshInfoThreadId; // per-thread info used to load icons struct LoadIconData { int m_IconSizes[ICON_SIZE_COUNT]; - HIMAGELIST m_TempLists[ICON_SIZE_COUNT]; + HIMAGELIST m_TempLists[ICON_SIZE_COUNT]={}; CComPtr m_pTempLists[ICON_SIZE_COUNT]; + IImageList2 *GetTempList( int index ); CComPtr m_pFactory; void Init( void ); diff --git a/Src/StartMenu/StartMenuDLL/MenuContainer.cpp b/Src/StartMenu/StartMenuDLL/MenuContainer.cpp index 3337b66a8..0375cc542 100644 --- a/Src/StartMenu/StartMenuDLL/MenuContainer.cpp +++ b/Src/StartMenu/StartMenuDLL/MenuContainer.cpp @@ -2015,8 +2015,6 @@ void CMenuContainer::GetRecentPrograms( std::vector &items, int maxCou // sort by rank std::sort(uaItems.begin(),uaItems.end()); - g_ItemManager.WaitForShortcuts(CPoint((s_StartRect.left+s_StartRect.right)/2,(s_StartRect.top+s_StartRect.bottom)/2)); - // create a menu item for each program for (int i=0;i<(int)uaItems.size();i++) { @@ -3284,22 +3282,8 @@ void CMenuContainer::InitWindowInternal( bool bDontShrink, const POINT &corner, if (column==1 && columnWidths.size()==1) columnWidths.push_back(0); } - if (item.id!=MENU_RECENT && !(m_Options&CONTAINER_SEARCH) && s_Skin.bHasNewItem && i=WIN_VER_WIN8 && GetSettingBool(L"AllProgramsMetro")) - item.bNew=g_ItemManager.HasNewPrograms(false) || g_ItemManager.HasNewApps(false); - else - item.bNew=g_ItemManager.HasNewPrograms(false); - } - else if (item.id==MENU_APPS) - item.bNew=g_ItemManager.HasNewApps(false); - else if (item.bFolder) - item.bNew=(item.pItem1 && g_ItemManager.IsNewProgram(item.pItem1,true,false)) || (item.pItem2 && g_ItemManager.IsNewProgram(item.pItem2,true,false)); - else if (item.pItemInfo) - item.bNew=(item.pItem1 && g_ItemManager.IsNewProgram(item.pItem1,false,item.bMetroApp)); - } + if (i=WIN_VER_WIN8 && GetSettingBool(L"AllProgramsMetro") && g_ItemManager.HasNewApps(false)); + if (item.id==MENU_APPS) + return g_ItemManager.HasNewApps(false); + if (item.bFolder) + return (item.pItem1 && g_ItemManager.IsNewProgram(item.pItem1,true,false)) || + (item.pItem2 && g_ItemManager.IsNewProgram(item.pItem2,true,false)); + return item.pItemInfo && item.pItem1 && g_ItemManager.IsNewProgram(item.pItem1,false,item.bMetroApp); +} + +void CMenuContainer::RefreshNewPrograms( void ) +{ + for (int i=0;iIsWindow()) + m_pProgramsTree->RefreshNewPrograms(); + Invalidate(FALSE); +} + LRESULT CMenuContainer::OnTimer( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { + if (wParam==TIMER_NEW_PROGRAMS) + { + if (!g_ItemManager.AreShortcutsReady()) return 0; + KillTimer(TIMER_NEW_PROGRAMS); + CComPtr keepAlive(static_cast(this)); + g_ItemManager.UpdateNewPrograms(); + if (!m_bDestroyed) + { + for (CMenuContainer *menu : s_Menus) + if (!menu->m_bDestroyed) menu->RefreshNewPrograms(); + } + return 0; + } + if (wParam==TIMER_HOVER) { LOG_MENU(LOG_MOUSE,L"End Hover, hover=%d%s, hot=%d, submenu=%d",m_HoverItem,m_bHoverArrow?L"(arrow)":L"",m_HotItem,m_Submenu); @@ -8559,7 +8593,7 @@ HWND CMenuContainer::ToggleStartMenu( int taskbarId, bool bKeyboard, bool bAllPr pStartMenu->InitItems(); pStartMenu->m_MaxWidth=s_MainMenuLimits.right-s_MainMenuLimits.left; - g_ItemManager.UpdateNewPrograms(CPoint((s_StartRect.left+s_StartRect.right)/2,(s_StartRect.top+s_StartRect.bottom)/2)); + g_ItemManager.ResetNewPrograms(); pStartMenu->InitWindow(); pStartMenu->SetHotItem((bKeyboard && bAllPrograms)?0:-1); @@ -8615,6 +8649,8 @@ HWND CMenuContainer::ToggleStartMenu( int taskbarId, bool bKeyboard, bool bAllPr if (!bAllPrograms) pStartMenu->SetHotItem(-1); } + if (GetSettingBool(L"HighlightNew")) + pStartMenu->SetTimer(TIMER_NEW_PROGRAMS,100); SetForegroundWindow(pStartMenu->m_hWnd); SwitchToThisWindow(pStartMenu->m_hWnd,FALSE); // just in case if (s_TaskBar && s_bBehindTaskbar) diff --git a/Src/StartMenu/StartMenuDLL/MenuContainer.h b/Src/StartMenu/StartMenuDLL/MenuContainer.h index 057982104..08cba98f3 100644 --- a/Src/StartMenu/StartMenuDLL/MenuContainer.h +++ b/Src/StartMenu/StartMenuDLL/MenuContainer.h @@ -763,6 +763,7 @@ class CMenuContainer: public IDropTarget, public IFrameworkInputPaneHandler, pub TIMER_BALLOON_HIDE=6, TIMER_SEARCH=7, TIMER_DRAG=8, + TIMER_NEW_PROGRAMS=9, MCM_REFRESH=WM_USER+10, // posted to force the container to refresh its contents MCM_SETCONTEXTITEM=WM_USER+11, // sets the item for the context menu. wParam is the nameHash of the item @@ -801,6 +802,8 @@ class CMenuContainer: public IDropTarget, public IFrameworkInputPaneHandler, pub void InitItems( void ); bool InitSearchItems( void ); void InitWindow( bool bDontShrink=false ); + bool IsNewMenuItem( const MenuItem &item ) const; + void RefreshNewPrograms( void ); void InitWindowInternal( bool bDontShrink, const POINT &corner, RECT &menuRect ); void InitWindowFinalize( const RECT &menuRect ); diff --git a/Src/StartMenu/StartMenuDLL/ProgramsTree.cpp b/Src/StartMenu/StartMenuDLL/ProgramsTree.cpp index 2e55c32b5..1f3af87c0 100644 --- a/Src/StartMenu/StartMenuDLL/ProgramsTree.cpp +++ b/Src/StartMenu/StartMenuDLL/ProgramsTree.cpp @@ -22,6 +22,32 @@ bool CProgramsTree::s_bFoldersFirst; const wchar_t *ORDER_PREFIX=L"#"; +void CProgramsTree::RefreshNewPrograms( void ) +{ + for (HTREEITEM node=TreeView_GetRoot(m_hWnd);node;) + { + TVITEM item={TVIF_PARAM}; + item.hItem=node; + if (TreeView_GetItem(m_hWnd,&item) && item.lParam) + { + CTreeItem *data=reinterpret_cast(item.lParam); + data->bNew=data->bApps?g_ItemManager.HasNewApps(false): + (data->pItemInfo1 && g_ItemManager.IsNewProgram(data->pItemInfo1->GetPidl(),data->bFolder,data->bApp)) || + (data->pItemInfo2 && g_ItemManager.IsNewProgram(data->pItemInfo2->GetPidl(),data->bFolder,false)); + } + HTREEITEM child=TreeView_GetChild(m_hWnd,node); + if (child) + node=child; + else + { + while (node && !TreeView_GetNextSibling(m_hWnd,node)) + node=TreeView_GetParent(m_hWnd,node); + if (node) node=TreeView_GetNextSibling(m_hWnd,node); + } + } + Invalidate(FALSE); +} + CProgramsTree::CProgramsTree( void ) { m_RefCount=1; diff --git a/Src/StartMenu/StartMenuDLL/ProgramsTree.h b/Src/StartMenu/StartMenuDLL/ProgramsTree.h index 6abb77bc2..a63f69f7d 100644 --- a/Src/StartMenu/StartMenuDLL/ProgramsTree.h +++ b/Src/StartMenu/StartMenuDLL/ProgramsTree.h @@ -93,6 +93,7 @@ class CProgramsTree: public IDropTarget, public CWindowImpl Assert(m_RefCount==0); } void Create( CMenuContainer *pOwner ); + void RefreshNewPrograms( void ); virtual WNDPROC GetWindowProc( void ) { return CustomWindowProc; } static LRESULT CALLBACK CustomWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); diff --git a/Src/StartMenu/StartMenuDLL/SearchManager.cpp b/Src/StartMenu/StartMenuDLL/SearchManager.cpp index 037794941..455687163 100644 --- a/Src/StartMenu/StartMenuDLL/SearchManager.cpp +++ b/Src/StartMenu/StartMenuDLL/SearchManager.cpp @@ -58,8 +58,11 @@ void CSearchManager::Init( void ) InitializeCriticalSection(&m_CriticalSections[i]); m_SearchEvent=CreateEvent(NULL,FALSE,FALSE,NULL); m_ExitEvent=CreateEvent(NULL,TRUE,FALSE,NULL); - for (int i=0;i<_countof(m_SearchThreads);i++) - m_SearchThreads[i]=CreateThread(NULL,0,StaticSearchThread,this,0,NULL); + if (m_SearchEvent && m_ExitEvent) + { + for (int i=0;i<_countof(m_SearchThreads);i++) + m_SearchThreads[i]=CreateThread(NULL,0,StaticSearchThread,this,0,NULL); + } m_MainThreadId=GetCurrentThreadId(); LoadItemRanks(); } @@ -67,11 +70,17 @@ void CSearchManager::Init( void ) void CSearchManager::Close( void ) { if (!m_bInitialized) return; - SetEvent(m_ExitEvent); - WaitForMultipleObjects(_countof(m_SearchThreads),m_SearchThreads,TRUE,INFINITE); - CloseHandle(m_SearchEvent); - for (int i=0;i<_countof(m_SearchThreads);i++) - CloseHandle(m_SearchThreads[i]); + if (m_ExitEvent) SetEvent(m_ExitEvent); + for (HANDLE &thread : m_SearchThreads) + { + if (!thread) continue; + WaitForSingleObject(thread,INFINITE); + CloseHandle(thread); + thread=NULL; + } + if (m_SearchEvent) CloseHandle(m_SearchEvent); + if (m_ExitEvent) CloseHandle(m_ExitEvent); + m_SearchEvent=m_ExitEvent=NULL; for (int i=0;i