From 7d59f5ebfbe748b954d23e03014033af69b22972 Mon Sep 17 00:00:00 2001 From: ge0rdi Date: Fri, 18 Sep 2020 12:16:03 +0200 Subject: [PATCH] Support for modern app jump-list tasks Task jump-list items for modern apps require special handling. First of all they don't provide icon directly. Icon location is stored in `PKEY_AppUserModel_DestListLogoUri` property. And then has to be resolved for given application package. Next, context menu of provided `IShellLink` item doesn't seem to be able to start actual link target. Thus we need to obtain context menu of target item. Fixes #375. --- Src/StartMenu/StartMenuDLL/ItemManager.cpp | 61 ++++++++- Src/StartMenu/StartMenuDLL/ItemManager.h | 1 + Src/StartMenu/StartMenuDLL/JumpLists.cpp | 132 +++++++------------ Src/StartMenu/StartMenuDLL/MenuContainer.cpp | 8 +- 4 files changed, 108 insertions(+), 94 deletions(-) diff --git a/Src/StartMenu/StartMenuDLL/ItemManager.cpp b/Src/StartMenu/StartMenuDLL/ItemManager.cpp index 4ecf2f0..0530070 100644 --- a/Src/StartMenu/StartMenuDLL/ItemManager.cpp +++ b/Src/StartMenu/StartMenuDLL/ItemManager.cpp @@ -55,8 +55,8 @@ GUID IID_IApplicationResolver8={0xde25675a,0x72de,0x44b4,{0x93,0x73,0x05,0x17,0x interface IResourceContext; -const GUID IID_IResourceMap={0x6e21e72b, 0xb9b0, 0x42ae, {0xa6, 0x86, 0x98, 0x3c, 0xf7, 0x84, 0xed, 0xcd}}; -interface IResourceMap : public IUnknown +MIDL_INTERFACE("6e21e72b-b9b0-42ae-a686-983cf784edcd") +IResourceMap : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetUri(const wchar_t **pUri ) = 0; virtual HRESULT STDMETHODCALLTYPE GetSubtree(const wchar_t *propName, IResourceMap **pSubTree ) = 0; @@ -76,8 +76,8 @@ enum RESOURCE_SCALE RES_SCALE_80 =3, }; -const GUID IID_ResourceContext={0xe3c22b30, 0x8502, 0x4b2f, {0x91, 0x33, 0x55, 0x96, 0x74, 0x58, 0x7e, 0x51}}; -interface IResourceContext : public IUnknown +MIDL_INTERFACE("e3c22b30-8502-4b2f-9133-559674587e51") +IResourceContext : public IUnknown { virtual HRESULT STDMETHODCALLTYPE GetLanguage( void ) = 0; virtual HRESULT STDMETHODCALLTYPE GetHomeRegion( wchar_t *pRegion ) = 0; @@ -299,7 +299,7 @@ static HBITMAP BitmapFromMetroBitmap( HBITMAP hBitmap, int bitmapSize, DWORD met /////////////////////////////////////////////////////////////////////////////// -static HBITMAP LoadMetroBitmap0( const wchar_t *path, int bitmapSize, DWORD metroColor ) +static HBITMAP LoadMetroBitmap0(const wchar_t *path, int bitmapSize, DWORD metroColor = 0xFFFFFFFF) { SIZE size={-bitmapSize,bitmapSize}; HBITMAP hBitmap=LoadImageFile(path,&size,true,true,NULL); @@ -1104,6 +1104,49 @@ const CItemManager::ItemInfo *CItemManager::GetCustomIcon( const wchar_t *path, return GetCustomIcon(text,index,iconSizeType,false); } +const CItemManager::ItemInfo* CItemManager::GetLinkIcon(IShellLink* link, TIconSizeType iconSizeType) +{ + wchar_t location[_MAX_PATH]; + int index; + + if (link->GetIconLocation(location, _countof(location), &index) == S_OK && location[0]) + return GetCustomIcon(location, index, iconSizeType, (index == 0)); // assuming that if index!=0 the icon comes from a permanent location like a dll or exe + + CComQIPtr store(link); + if (store) + { + // Name: System.AppUserModel.DestListLogoUri -- PKEY_AppUserModel_DestListLogoUri + // Type: String -- VT_LPWSTR + // FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 29 + static const PROPERTYKEY PKEY_AppUserModel_DestListLogoUri = { {0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 29 }; + + auto logoUri = GetPropertyStoreString(store, PKEY_AppUserModel_DestListLogoUri); + if (!logoUri.IsEmpty()) + { + auto appId = GetPropertyStoreString(store, PKEY_AppUserModel_ID); + if (!appId.IsEmpty()) + { + CComPtr resManager; + if (SUCCEEDED(resManager.CoCreateInstance(CLSID_ResourceManager))) + { + if (SUCCEEDED(resManager->InitializeForPackage(GetPackageFullName(appId)))) + { + CComPtr resMap; + if (SUCCEEDED(resManager->GetMainResourceMap(IID_PPV_ARGS(&resMap)))) + { + CComString location; + if (SUCCEEDED(resMap->GetFilePath(logoUri, &location))) + return GetCustomIcon(location, -65536, iconSizeType, true); + } + } + } + } + } + } + + return nullptr; +} + const CItemManager::ItemInfo *CItemManager::GetMetroAppInfo10( const wchar_t *appid ) { wchar_t APPID[256]; @@ -2424,10 +2467,10 @@ void CItemManager::LoadMetroIcon( IShellItem *pItem, int &refreshFlags, const Ic if (FAILED(pResManager->InitializeForPackage(packageName))) return; CComPtr pResMap; - if (FAILED(pResManager->GetMainResourceMap(IID_IResourceMap,(void**)&pResMap))) + if (FAILED(pResManager->GetMainResourceMap(IID_PPV_ARGS(&pResMap)))) return; CComPtr pResContext; - if (FAILED(pResManager->GetDefaultContext(IID_ResourceContext,(void**)&pResContext))) + if (FAILED(pResManager->GetDefaultContext(IID_PPV_ARGS(&pResContext)))) return; int iconFlags=0; if ((refreshFlags&INFO_SMALL_ICON) && SetResContextTargetSize(pResContext,SMALL_ICON_SIZE)) @@ -2604,6 +2647,10 @@ void CItemManager::LoadCustomIcon(const wchar_t *iconPath, int iconIndex, int re return; auto ExtractIconAsBitmap = [&](int iconSize) -> HBITMAP { + + if (iconIndex == -65536) + return LoadMetroBitmap0(iconPath, iconSize); + HICON hIcon; if (!*iconPath) diff --git a/Src/StartMenu/StartMenuDLL/ItemManager.h b/Src/StartMenu/StartMenuDLL/ItemManager.h index 42c6250..a4795e0 100644 --- a/Src/StartMenu/StartMenuDLL/ItemManager.h +++ b/Src/StartMenu/StartMenuDLL/ItemManager.h @@ -173,6 +173,7 @@ public: const ItemInfo *GetItemInfo( CString path, int refreshFlags, TLocation location=LOCATION_UNKNOWN ); const ItemInfo *GetCustomIcon( const wchar_t *location, int index, TIconSizeType iconSizeType, bool bTemp ); const ItemInfo *GetCustomIcon( const wchar_t *path, TIconSizeType iconSizeType ); + 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 ); diff --git a/Src/StartMenu/StartMenuDLL/JumpLists.cpp b/Src/StartMenu/StartMenuDLL/JumpLists.cpp index e03a0ca..6a91b7d 100644 --- a/Src/StartMenu/StartMenuDLL/JumpLists.cpp +++ b/Src/StartMenu/StartMenuDLL/JumpLists.cpp @@ -322,6 +322,9 @@ static void AddJumpItem( CJumpGroup &group, IUnknown *pUnknown, std::vector=WIN_VER_WIN7); - if (!item.pItem) return false; + if (!item.pItem) + return false; + if (item.type==CJumpItem::TYPE_ITEM) { -/* CString appid; - { - CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS); - appid=pAppInfo->GetAppid(); - } - LOG_MENU(LOG_OPEN,L"Execute Item: name=%s, appid=%s",item.name,appid);*/ CComQIPtr pItem(item.pItem); if (!pItem) return false; -/* CComString pName; - if (FAILED(pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName))) - return false; - wchar_t ext[_MAX_EXT]; - Strcpy(ext,_countof(ext),PathFindExtension(pName)); - // find the correct association handler by appid and invoke it on the item - CComPtr pEnumHandlers; - if (ext[0] && SUCCEEDED(SHAssocEnumHandlers(ext,ASSOC_FILTER_RECOMMENDED,&pEnumHandlers))) - { - CComPtr pHandler; - ULONG count; - while (SUCCEEDED(pEnumHandlers->Next(1,&pHandler,&count)) && count==1) - { - CComQIPtr pObject=pHandler; - if (pObject) - { - CComString pID; - if (SUCCEEDED(pObject->GetAppID(&pID))) - { - // found explicit appid - if (_wcsicmp(appid,pID)==0) - { - LOG_MENU(LOG_OPEN,L"Found handler appid"); - CComPtr pDataObject; - if (SUCCEEDED(pItem->BindToHandler(NULL,BHID_DataObject,IID_IDataObject,(void**)&pDataObject)) && SUCCEEDED(pHandler->Invoke(pDataObject))) - return true; - break; - } - } - } - pHandler=NULL; - } - pEnumHandlers=NULL; - - // find the correct association handler by exe name and invoke it on the item - wchar_t targetPath[_MAX_PATH]; - targetPath[0]=0; - { - CComPtr pItem; - SHCreateItemFromIDList(pAppInfo->GetPidl(),IID_IShellItem,(void**)&pItem); - CComPtr pLink; - if (pItem) - pItem->BindToHandler(NULL,BHID_SFUIObject,IID_IShellLink,(void**)&pLink); - CAbsolutePidl target; - if (pLink && SUCCEEDED(pLink->Resolve(NULL,SLR_INVOKE_MSI|SLR_NO_UI|SLR_NOUPDATE)) && SUCCEEDED(pLink->GetIDList(&target))) - { - if (FAILED(SHGetPathFromIDList(target,targetPath))) - targetPath[0]=0; - } - } - if (targetPath[0] && SUCCEEDED(SHAssocEnumHandlers(ext,ASSOC_FILTER_RECOMMENDED,&pEnumHandlers))) - { - while (SUCCEEDED(pEnumHandlers->Next(1,&pHandler,&count)) && count==1) - { - CComString pExe; - if (SUCCEEDED(pHandler->GetName(&pExe))) - { - if (_wcsicmp(targetPath,pExe)==0) - { - LOG_MENU(LOG_OPEN,L"Found handler appexe %s",targetPath); - CComPtr pDataObject; - if (SUCCEEDED(pItem->BindToHandler(NULL,BHID_DataObject,IID_IDataObject,(void**)&pDataObject)) && SUCCEEDED(pHandler->Invoke(pDataObject))) - return true; - break; - } - } - pHandler=NULL; - } - } - } -*/ - // couldn't find a handler, execute the old way SHELLEXECUTEINFO execute={sizeof(execute),SEE_MASK_IDLIST|SEE_MASK_FLAG_LOG_USAGE}; execute.nShow=SW_SHOWNORMAL; CAbsolutePidl pidl; @@ -617,9 +544,50 @@ bool ExecuteJumpItem( const CItemManager::ItemInfo *pAppInfo, const CJumpItem &i if (item.type==CJumpItem::TYPE_LINK) { - // invoke the link through its context menu + // Name: System.AppUserModel.HostEnvironment -- PKEY_AppUserModel_HostEnvironment + // Type: UInt32 -- VT_UI4 + // FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 14 + static const PROPERTYKEY PKEY_AppUserModel_HostEnvironment = { {0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 14 }; + + // Name: System.AppUserModel.ActivationContext -- PKEY_AppUserModel_ActivationContext + // Type: String -- VT_LPWSTR + // FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 20 + static const PROPERTYKEY PKEY_AppUserModel_ActivationContext = { {0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 20 }; + CComQIPtr pMenu(item.pItem); - if (!pMenu) return false; + CStringA params; + + CComQIPtr pLink(item.pItem); + if (pLink) + { + CComQIPtr store(pLink); + if (store) + { + auto appId = GetPropertyStoreString(store, PKEY_AppUserModel_ID); + if (!appId.IsEmpty()) + { + CComPtr target; + if (SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder, 0, appId, IID_PPV_ARGS(&target)))) + { + ULONG modern = 0; + if (SUCCEEDED(target->GetUInt32(PKEY_AppUserModel_HostEnvironment, &modern)) && modern) + { + CComQIPtr targetMenu; + if (SUCCEEDED(target->BindToHandler(nullptr, BHID_SFUIObject, IID_PPV_ARGS(&targetMenu)))) + { + pMenu = targetMenu; + params = CT2CA(GetPropertyStoreString(store, PKEY_AppUserModel_ActivationContext)); + } + } + } + } + } + } + + // invoke the link through its context menu + if (!pMenu) + return false; + HRESULT hr; HMENU menu=CreatePopupMenu(); hr=pMenu->QueryContextMenu(menu,0,1,1000,CMF_DEFAULTONLY); @@ -633,6 +601,8 @@ bool ExecuteJumpItem( const CItemManager::ItemInfo *pAppInfo, const CJumpItem &i { CMINVOKECOMMANDINFO command={sizeof(command),CMIC_MASK_FLAG_LOG_USAGE}; command.lpVerb=MAKEINTRESOURCEA(id-1); + if (!params.IsEmpty()) + command.lpParameters = params; wchar_t path[_MAX_PATH]; GetModuleFileName(NULL,path,_countof(path)); if (_wcsicmp(PathFindFileName(path),L"explorer.exe")==0) diff --git a/Src/StartMenu/StartMenuDLL/MenuContainer.cpp b/Src/StartMenu/StartMenuDLL/MenuContainer.cpp index 857561a..a27f49a 100644 --- a/Src/StartMenu/StartMenuDLL/MenuContainer.cpp +++ b/Src/StartMenu/StartMenuDLL/MenuContainer.cpp @@ -2203,10 +2203,7 @@ void CMenuContainer::AddJumpListItems( std::vector &items ) if (pLink) { pLink->GetIDList(&item.pItem1); - wchar_t location[_MAX_PATH]; - int index; - if (pLink->GetIconLocation(location,_countof(location),&index)==S_OK && location[0]) - item.pItemInfo=g_ItemManager.GetCustomIcon(location,index,CItemManager::ICON_SIZE_TYPE_SMALL,(index==0)); // assuming that if index!=0 the icon comes from a permanent location like a dll or exe + item.pItemInfo = g_ItemManager.GetLinkIcon(pLink, CItemManager::ICON_SIZE_TYPE_SMALL); } } else if (jumpItem.type==CJumpItem::TYPE_ITEM) @@ -6668,8 +6665,7 @@ bool CMenuContainer::GetDescription( int index, wchar_t *text, int size ) { if (SUCCEEDED(pLink->GetDescription(text,size)) && text[0]) return true; - wchar_t args[256]; - if (SUCCEEDED(pLink->GetArguments(args,_countof(args))) && args[0]) + if (jumpItem.bHasArguments) { // don't use default tip for items with arguments Strcpy(text,size,item.name);