Skip to content

Commit 7d59f5e

Browse files
committed
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.
1 parent 95f3a4b commit 7d59f5e

File tree

4 files changed

+108
-94
lines changed

4 files changed

+108
-94
lines changed

Src/StartMenu/StartMenuDLL/ItemManager.cpp

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ GUID IID_IApplicationResolver8={0xde25675a,0x72de,0x44b4,{0x93,0x73,0x05,0x17,0x
5555

5656
interface IResourceContext;
5757

58-
const GUID IID_IResourceMap={0x6e21e72b, 0xb9b0, 0x42ae, {0xa6, 0x86, 0x98, 0x3c, 0xf7, 0x84, 0xed, 0xcd}};
59-
interface IResourceMap : public IUnknown
58+
MIDL_INTERFACE("6e21e72b-b9b0-42ae-a686-983cf784edcd")
59+
IResourceMap : public IUnknown
6060
{
6161
virtual HRESULT STDMETHODCALLTYPE GetUri(const wchar_t **pUri ) = 0;
6262
virtual HRESULT STDMETHODCALLTYPE GetSubtree(const wchar_t *propName, IResourceMap **pSubTree ) = 0;
@@ -76,8 +76,8 @@ enum RESOURCE_SCALE
7676
RES_SCALE_80 =3,
7777
};
7878

79-
const GUID IID_ResourceContext={0xe3c22b30, 0x8502, 0x4b2f, {0x91, 0x33, 0x55, 0x96, 0x74, 0x58, 0x7e, 0x51}};
80-
interface IResourceContext : public IUnknown
79+
MIDL_INTERFACE("e3c22b30-8502-4b2f-9133-559674587e51")
80+
IResourceContext : public IUnknown
8181
{
8282
virtual HRESULT STDMETHODCALLTYPE GetLanguage( void ) = 0;
8383
virtual HRESULT STDMETHODCALLTYPE GetHomeRegion( wchar_t *pRegion ) = 0;
@@ -299,7 +299,7 @@ static HBITMAP BitmapFromMetroBitmap( HBITMAP hBitmap, int bitmapSize, DWORD met
299299

300300
///////////////////////////////////////////////////////////////////////////////
301301

302-
static HBITMAP LoadMetroBitmap0( const wchar_t *path, int bitmapSize, DWORD metroColor )
302+
static HBITMAP LoadMetroBitmap0(const wchar_t *path, int bitmapSize, DWORD metroColor = 0xFFFFFFFF)
303303
{
304304
SIZE size={-bitmapSize,bitmapSize};
305305
HBITMAP hBitmap=LoadImageFile(path,&size,true,true,NULL);
@@ -1104,6 +1104,49 @@ const CItemManager::ItemInfo *CItemManager::GetCustomIcon( const wchar_t *path,
11041104
return GetCustomIcon(text,index,iconSizeType,false);
11051105
}
11061106

1107+
const CItemManager::ItemInfo* CItemManager::GetLinkIcon(IShellLink* link, TIconSizeType iconSizeType)
1108+
{
1109+
wchar_t location[_MAX_PATH];
1110+
int index;
1111+
1112+
if (link->GetIconLocation(location, _countof(location), &index) == S_OK && location[0])
1113+
return GetCustomIcon(location, index, iconSizeType, (index == 0)); // assuming that if index!=0 the icon comes from a permanent location like a dll or exe
1114+
1115+
CComQIPtr<IPropertyStore> store(link);
1116+
if (store)
1117+
{
1118+
// Name: System.AppUserModel.DestListLogoUri -- PKEY_AppUserModel_DestListLogoUri
1119+
// Type: String -- VT_LPWSTR
1120+
// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 29
1121+
static const PROPERTYKEY PKEY_AppUserModel_DestListLogoUri = { {0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 29 };
1122+
1123+
auto logoUri = GetPropertyStoreString(store, PKEY_AppUserModel_DestListLogoUri);
1124+
if (!logoUri.IsEmpty())
1125+
{
1126+
auto appId = GetPropertyStoreString(store, PKEY_AppUserModel_ID);
1127+
if (!appId.IsEmpty())
1128+
{
1129+
CComPtr<IResourceManager> resManager;
1130+
if (SUCCEEDED(resManager.CoCreateInstance(CLSID_ResourceManager)))
1131+
{
1132+
if (SUCCEEDED(resManager->InitializeForPackage(GetPackageFullName(appId))))
1133+
{
1134+
CComPtr<IResourceMap> resMap;
1135+
if (SUCCEEDED(resManager->GetMainResourceMap(IID_PPV_ARGS(&resMap))))
1136+
{
1137+
CComString location;
1138+
if (SUCCEEDED(resMap->GetFilePath(logoUri, &location)))
1139+
return GetCustomIcon(location, -65536, iconSizeType, true);
1140+
}
1141+
}
1142+
}
1143+
}
1144+
}
1145+
}
1146+
1147+
return nullptr;
1148+
}
1149+
11071150
const CItemManager::ItemInfo *CItemManager::GetMetroAppInfo10( const wchar_t *appid )
11081151
{
11091152
wchar_t APPID[256];
@@ -2424,10 +2467,10 @@ void CItemManager::LoadMetroIcon( IShellItem *pItem, int &refreshFlags, const Ic
24242467
if (FAILED(pResManager->InitializeForPackage(packageName)))
24252468
return;
24262469
CComPtr<IResourceMap> pResMap;
2427-
if (FAILED(pResManager->GetMainResourceMap(IID_IResourceMap,(void**)&pResMap)))
2470+
if (FAILED(pResManager->GetMainResourceMap(IID_PPV_ARGS(&pResMap))))
24282471
return;
24292472
CComPtr<IResourceContext> pResContext;
2430-
if (FAILED(pResManager->GetDefaultContext(IID_ResourceContext,(void**)&pResContext)))
2473+
if (FAILED(pResManager->GetDefaultContext(IID_PPV_ARGS(&pResContext))))
24312474
return;
24322475
int iconFlags=0;
24332476
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
26042647
return;
26052648

26062649
auto ExtractIconAsBitmap = [&](int iconSize) -> HBITMAP {
2650+
2651+
if (iconIndex == -65536)
2652+
return LoadMetroBitmap0(iconPath, iconSize);
2653+
26072654
HICON hIcon;
26082655

26092656
if (!*iconPath)

Src/StartMenu/StartMenuDLL/ItemManager.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ class CItemManager
173173
const ItemInfo *GetItemInfo( CString path, int refreshFlags, TLocation location=LOCATION_UNKNOWN );
174174
const ItemInfo *GetCustomIcon( const wchar_t *location, int index, TIconSizeType iconSizeType, bool bTemp );
175175
const ItemInfo *GetCustomIcon( const wchar_t *path, TIconSizeType iconSizeType );
176+
const ItemInfo* GetLinkIcon(IShellLink* link, TIconSizeType iconSizeType);
176177
const ItemInfo *GetMetroAppInfo10( const wchar_t *appid );
177178
void UpdateItemInfo( const ItemInfo *pInfo, int refreshFlags, bool bHasWriteLock=false );
178179
void WaitForShortcuts( const POINT &balloonPos );

Src/StartMenu/StartMenuDLL/JumpLists.cpp

Lines changed: 51 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,9 @@ static void AddJumpItem( CJumpGroup &group, IUnknown *pUnknown, std::vector<CCom
322322
}
323323
}
324324
LOG_MENU(LOG_OPEN,L"Jumplist Link Name: %s",item.name);
325+
#ifdef _DEBUG
326+
LogPropertyStore(LOG_OPEN, pStore);
327+
#endif
325328
if (!item.name.IsEmpty())
326329
group.items.push_back(item);
327330
return;
@@ -519,91 +522,15 @@ bool GetJumplist( const wchar_t *appid, CJumpList &list, int maxCount, int maxHe
519522
bool ExecuteJumpItem( const CItemManager::ItemInfo *pAppInfo, const CJumpItem &item, HWND hwnd )
520523
{
521524
Assert(GetWinVersion()>=WIN_VER_WIN7);
522-
if (!item.pItem) return false;
525+
if (!item.pItem)
526+
return false;
527+
523528
if (item.type==CJumpItem::TYPE_ITEM)
524529
{
525-
/* CString appid;
526-
{
527-
CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS);
528-
appid=pAppInfo->GetAppid();
529-
}
530-
LOG_MENU(LOG_OPEN,L"Execute Item: name=%s, appid=%s",item.name,appid);*/
531530
CComQIPtr<IShellItem> pItem(item.pItem);
532531
if (!pItem)
533532
return false;
534-
/* CComString pName;
535-
if (FAILED(pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName)))
536-
return false;
537-
wchar_t ext[_MAX_EXT];
538-
Strcpy(ext,_countof(ext),PathFindExtension(pName));
539533

540-
// find the correct association handler by appid and invoke it on the item
541-
CComPtr<IEnumAssocHandlers> pEnumHandlers;
542-
if (ext[0] && SUCCEEDED(SHAssocEnumHandlers(ext,ASSOC_FILTER_RECOMMENDED,&pEnumHandlers)))
543-
{
544-
CComPtr<IAssocHandler> pHandler;
545-
ULONG count;
546-
while (SUCCEEDED(pEnumHandlers->Next(1,&pHandler,&count)) && count==1)
547-
{
548-
CComQIPtr<IObjectWithAppUserModelID> pObject=pHandler;
549-
if (pObject)
550-
{
551-
CComString pID;
552-
if (SUCCEEDED(pObject->GetAppID(&pID)))
553-
{
554-
// found explicit appid
555-
if (_wcsicmp(appid,pID)==0)
556-
{
557-
LOG_MENU(LOG_OPEN,L"Found handler appid");
558-
CComPtr<IDataObject> pDataObject;
559-
if (SUCCEEDED(pItem->BindToHandler(NULL,BHID_DataObject,IID_IDataObject,(void**)&pDataObject)) && SUCCEEDED(pHandler->Invoke(pDataObject)))
560-
return true;
561-
break;
562-
}
563-
}
564-
}
565-
pHandler=NULL;
566-
}
567-
pEnumHandlers=NULL;
568-
569-
// find the correct association handler by exe name and invoke it on the item
570-
wchar_t targetPath[_MAX_PATH];
571-
targetPath[0]=0;
572-
{
573-
CComPtr<IShellItem> pItem;
574-
SHCreateItemFromIDList(pAppInfo->GetPidl(),IID_IShellItem,(void**)&pItem);
575-
CComPtr<IShellLink> pLink;
576-
if (pItem)
577-
pItem->BindToHandler(NULL,BHID_SFUIObject,IID_IShellLink,(void**)&pLink);
578-
CAbsolutePidl target;
579-
if (pLink && SUCCEEDED(pLink->Resolve(NULL,SLR_INVOKE_MSI|SLR_NO_UI|SLR_NOUPDATE)) && SUCCEEDED(pLink->GetIDList(&target)))
580-
{
581-
if (FAILED(SHGetPathFromIDList(target,targetPath)))
582-
targetPath[0]=0;
583-
}
584-
}
585-
if (targetPath[0] && SUCCEEDED(SHAssocEnumHandlers(ext,ASSOC_FILTER_RECOMMENDED,&pEnumHandlers)))
586-
{
587-
while (SUCCEEDED(pEnumHandlers->Next(1,&pHandler,&count)) && count==1)
588-
{
589-
CComString pExe;
590-
if (SUCCEEDED(pHandler->GetName(&pExe)))
591-
{
592-
if (_wcsicmp(targetPath,pExe)==0)
593-
{
594-
LOG_MENU(LOG_OPEN,L"Found handler appexe %s",targetPath);
595-
CComPtr<IDataObject> pDataObject;
596-
if (SUCCEEDED(pItem->BindToHandler(NULL,BHID_DataObject,IID_IDataObject,(void**)&pDataObject)) && SUCCEEDED(pHandler->Invoke(pDataObject)))
597-
return true;
598-
break;
599-
}
600-
}
601-
pHandler=NULL;
602-
}
603-
}
604-
}
605-
*/
606-
// couldn't find a handler, execute the old way
607534
SHELLEXECUTEINFO execute={sizeof(execute),SEE_MASK_IDLIST|SEE_MASK_FLAG_LOG_USAGE};
608535
execute.nShow=SW_SHOWNORMAL;
609536
CAbsolutePidl pidl;
@@ -617,9 +544,50 @@ bool ExecuteJumpItem( const CItemManager::ItemInfo *pAppInfo, const CJumpItem &i
617544

618545
if (item.type==CJumpItem::TYPE_LINK)
619546
{
620-
// invoke the link through its context menu
547+
// Name: System.AppUserModel.HostEnvironment -- PKEY_AppUserModel_HostEnvironment
548+
// Type: UInt32 -- VT_UI4
549+
// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 14
550+
static const PROPERTYKEY PKEY_AppUserModel_HostEnvironment = { {0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 14 };
551+
552+
// Name: System.AppUserModel.ActivationContext -- PKEY_AppUserModel_ActivationContext
553+
// Type: String -- VT_LPWSTR
554+
// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 20
555+
static const PROPERTYKEY PKEY_AppUserModel_ActivationContext = { {0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 20 };
556+
621557
CComQIPtr<IContextMenu> pMenu(item.pItem);
622-
if (!pMenu) return false;
558+
CStringA params;
559+
560+
CComQIPtr<IShellLink> pLink(item.pItem);
561+
if (pLink)
562+
{
563+
CComQIPtr<IPropertyStore> store(pLink);
564+
if (store)
565+
{
566+
auto appId = GetPropertyStoreString(store, PKEY_AppUserModel_ID);
567+
if (!appId.IsEmpty())
568+
{
569+
CComPtr<IShellItem2> target;
570+
if (SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder, 0, appId, IID_PPV_ARGS(&target))))
571+
{
572+
ULONG modern = 0;
573+
if (SUCCEEDED(target->GetUInt32(PKEY_AppUserModel_HostEnvironment, &modern)) && modern)
574+
{
575+
CComQIPtr<IContextMenu> targetMenu;
576+
if (SUCCEEDED(target->BindToHandler(nullptr, BHID_SFUIObject, IID_PPV_ARGS(&targetMenu))))
577+
{
578+
pMenu = targetMenu;
579+
params = CT2CA(GetPropertyStoreString(store, PKEY_AppUserModel_ActivationContext));
580+
}
581+
}
582+
}
583+
}
584+
}
585+
}
586+
587+
// invoke the link through its context menu
588+
if (!pMenu)
589+
return false;
590+
623591
HRESULT hr;
624592
HMENU menu=CreatePopupMenu();
625593
hr=pMenu->QueryContextMenu(menu,0,1,1000,CMF_DEFAULTONLY);
@@ -633,6 +601,8 @@ bool ExecuteJumpItem( const CItemManager::ItemInfo *pAppInfo, const CJumpItem &i
633601
{
634602
CMINVOKECOMMANDINFO command={sizeof(command),CMIC_MASK_FLAG_LOG_USAGE};
635603
command.lpVerb=MAKEINTRESOURCEA(id-1);
604+
if (!params.IsEmpty())
605+
command.lpParameters = params;
636606
wchar_t path[_MAX_PATH];
637607
GetModuleFileName(NULL,path,_countof(path));
638608
if (_wcsicmp(PathFindFileName(path),L"explorer.exe")==0)

Src/StartMenu/StartMenuDLL/MenuContainer.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2203,10 +2203,7 @@ void CMenuContainer::AddJumpListItems( std::vector<MenuItem> &items )
22032203
if (pLink)
22042204
{
22052205
pLink->GetIDList(&item.pItem1);
2206-
wchar_t location[_MAX_PATH];
2207-
int index;
2208-
if (pLink->GetIconLocation(location,_countof(location),&index)==S_OK && location[0])
2209-
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
2206+
item.pItemInfo = g_ItemManager.GetLinkIcon(pLink, CItemManager::ICON_SIZE_TYPE_SMALL);
22102207
}
22112208
}
22122209
else if (jumpItem.type==CJumpItem::TYPE_ITEM)
@@ -6668,8 +6665,7 @@ bool CMenuContainer::GetDescription( int index, wchar_t *text, int size )
66686665
{
66696666
if (SUCCEEDED(pLink->GetDescription(text,size)) && text[0])
66706667
return true;
6671-
wchar_t args[256];
6672-
if (SUCCEEDED(pLink->GetArguments(args,_countof(args))) && args[0])
6668+
if (jumpItem.bHasArguments)
66736669
{
66746670
// don't use default tip for items with arguments
66756671
Strcpy(text,size,item.name);

0 commit comments

Comments
 (0)