mirror of
https://github.com/Open-Shell/Open-Shell-Menu.git
synced 2026-04-11 17:37:22 +10:00
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.
This commit is contained in:
@@ -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<IPropertyStore> 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<IResourceManager> resManager;
|
||||
if (SUCCEEDED(resManager.CoCreateInstance(CLSID_ResourceManager)))
|
||||
{
|
||||
if (SUCCEEDED(resManager->InitializeForPackage(GetPackageFullName(appId))))
|
||||
{
|
||||
CComPtr<IResourceMap> 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<IResourceMap> pResMap;
|
||||
if (FAILED(pResManager->GetMainResourceMap(IID_IResourceMap,(void**)&pResMap)))
|
||||
if (FAILED(pResManager->GetMainResourceMap(IID_PPV_ARGS(&pResMap))))
|
||||
return;
|
||||
CComPtr<IResourceContext> 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)
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -322,6 +322,9 @@ static void AddJumpItem( CJumpGroup &group, IUnknown *pUnknown, std::vector<CCom
|
||||
}
|
||||
}
|
||||
LOG_MENU(LOG_OPEN,L"Jumplist Link Name: %s",item.name);
|
||||
#ifdef _DEBUG
|
||||
LogPropertyStore(LOG_OPEN, pStore);
|
||||
#endif
|
||||
if (!item.name.IsEmpty())
|
||||
group.items.push_back(item);
|
||||
return;
|
||||
@@ -519,91 +522,15 @@ bool GetJumplist( const wchar_t *appid, CJumpList &list, int maxCount, int maxHe
|
||||
bool ExecuteJumpItem( const CItemManager::ItemInfo *pAppInfo, const CJumpItem &item, HWND hwnd )
|
||||
{
|
||||
Assert(GetWinVersion()>=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<IShellItem> 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<IEnumAssocHandlers> pEnumHandlers;
|
||||
if (ext[0] && SUCCEEDED(SHAssocEnumHandlers(ext,ASSOC_FILTER_RECOMMENDED,&pEnumHandlers)))
|
||||
{
|
||||
CComPtr<IAssocHandler> pHandler;
|
||||
ULONG count;
|
||||
while (SUCCEEDED(pEnumHandlers->Next(1,&pHandler,&count)) && count==1)
|
||||
{
|
||||
CComQIPtr<IObjectWithAppUserModelID> 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<IDataObject> 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<IShellItem> pItem;
|
||||
SHCreateItemFromIDList(pAppInfo->GetPidl(),IID_IShellItem,(void**)&pItem);
|
||||
CComPtr<IShellLink> 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<IDataObject> 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<IContextMenu> pMenu(item.pItem);
|
||||
if (!pMenu) return false;
|
||||
CStringA params;
|
||||
|
||||
CComQIPtr<IShellLink> pLink(item.pItem);
|
||||
if (pLink)
|
||||
{
|
||||
CComQIPtr<IPropertyStore> store(pLink);
|
||||
if (store)
|
||||
{
|
||||
auto appId = GetPropertyStoreString(store, PKEY_AppUserModel_ID);
|
||||
if (!appId.IsEmpty())
|
||||
{
|
||||
CComPtr<IShellItem2> 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<IContextMenu> 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)
|
||||
|
||||
@@ -2203,10 +2203,7 @@ void CMenuContainer::AddJumpListItems( std::vector<MenuItem> &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);
|
||||
|
||||
Reference in New Issue
Block a user