Files
Open-Shell-Menu/Src/StartMenu/StartMenuDLL/MetroLinkManager.cpp
2020-09-23 11:50:32 +02:00

388 lines
13 KiB
C++

// 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
#include "stdafx.h"
#include "MetroLinkManager.h"
#include "ItemManager.h"
#include "LogManager.h"
#include "FNVHash.h"
#include "ResourceHelper.h"
#include "Translations.h"
#include <propkey.h>
#include <map>
PROPERTYKEY PKEY_MetroAppLink={{0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 10}; // pidl
PROPERTYKEY PKEY_MetroAppLauncher={{0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 14}; // =1 for metro apps
PROPERTYKEY PKEY_MetroIconColor={{0x86D40B4D, 0x9069, 0x443C, {0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC}}, 4};
PROPERTYKEY PKEY_MetroPackageName={{0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 21};
PROPERTYKEY PKEY_MetroPackagePath={{0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 15};
PROPERTYKEY PKEY_AppUserModel_ParentID={{0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 19}; // non-empty for content tiles
PROPERTYKEY PKEY_AppUserModel_InstalledBy={{0x9F4C2855, 0x9F79, 0x4B39, {0xA8, 0xD0, 0xE1, 0xD4, 0x2D, 0xE1, 0xD5, 0xF3}}, 18};
PROPERTYKEY PKEY_Launcher_AppState={{0x0ded77b3, 0xc614, 0x456c, {0xae, 0x5b, 0x28, 0x5b, 0x38, 0xd7, 0xb0, 0x1b}}, 7};
// FOLDERID_AppsFolder is defined in the 8.0 SDK, but we don't want to require it
KNOWNFOLDERID FOLDERID_AppsFolder2={0x1E87508D,0x89C2,0x42F0,{0x8A,0x7E,0x64,0x5A,0x0F,0x50,0xCA,0x58}}; // similar to shell:::{4234d49b-0245-4df3-b780-3893943456e1}
GUID CLSID_PinExt={0x90AA3A4E,0x1CBA,0x4233,{0xB8,0xBB,0x53,0x57,0x73,0xD4,0x84,0x49}};
const wchar_t *MetroAppClassId=L"Launcher.ImmersiveApplication";
// Returns a list of links for all metro apps
void GetMetroLinks( std::vector<MetroLink> &links, bool bLog, std::vector<CString> *pNonApps10 )
{
LOG_MENU(LOG_APPS,L"Collect Metro Links (start)");
if (bLog && !(g_LogCategories&LOG_APPS))
bLog=false;
CComPtr<IShellItem> pApps;
if (FAILED(ShGetKnownFolderItem(FOLDERID_AppsFolder2,&pApps)))
return;
CComPtr<IEnumShellItems> pEnum;
pApps->BindToHandler(NULL,BHID_EnumItems,IID_IEnumShellItems,(void**)&pEnum);
CComPtr<IShellItem> pChild;
while (pEnum && (pChild=NULL,pEnum->Next(1,&pChild,NULL)==S_OK))
{
if (bLog)
{
CComString pName;
pChild->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName);
LOG_MENU(LOG_APPS,L"App: %s",(const wchar_t*)pName);
}
CComPtr<IPropertyStore> pStore;
if (FAILED(pChild->BindToHandler(NULL,BHID_PropertyStore,IID_IPropertyStore,(void**)&pStore)))
{
LOG_MENU(LOG_APPS,L" No Store");
continue;
}
PROPVARIANT val;
PropVariantInit(&val);
bool bNonApp=false;
if (FAILED(pStore->GetValue(PKEY_MetroAppLauncher,&val)))
{
LOG_MENU(LOG_APPS,L" No launcher");
bNonApp=true;
}
else
{
if ((val.vt!=VT_I4 && val.vt!=VT_UI4) || !val.intVal)
{
LOG_MENU(LOG_APPS,L" No launcher: %d",val.vt);
bNonApp=true;
}
PropVariantClear(&val);
}
if (GetWinVersion()>=WIN_VER_WIN10)
{
PIDLIST_ABSOLUTE pidl;
if (!bNonApp && SUCCEEDED(SHGetIDListFromObject(pChild,&pidl)))
{
links.resize(links.size()+1);
MetroLink &link=*links.rbegin();
link.pidl.Attach(pidl);
link.appid=GetPropertyStoreString(pStore,PKEY_AppUserModel_ID);
link.pItem=pChild;
}
else
bNonApp=true;
if (bNonApp && pNonApps10)
pNonApps10->push_back(GetPropertyStoreString(pStore,PKEY_AppUserModel_ID));
continue;
}
if (bNonApp)
continue;
if (FAILED(pStore->GetValue(PKEY_MetroAppLink,&val)))
{
LOG_MENU(LOG_APPS,L" No link");
continue;
}
if (val.vt!=(VT_VECTOR|VT_UI1) || !val.caub.pElems)
{
LOG_MENU(LOG_APPS,L" No link: %d",val.vt);
PropVariantClear(&val);
continue;
}
PIDLIST_ABSOLUTE pidl=ILCloneFull((PIDLIST_ABSOLUTE)val.caub.pElems);
PropVariantClear(&val);
links.resize(links.size()+1);
MetroLink &link=*links.rbegin();
link.pidl.Attach(pidl);
SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&link.pItem);
if (bLog && link.pItem)
{
CComString pName;
link.pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName);
LOG_MENU(LOG_APPS,L" Link: %s",(const wchar_t*)pName);
}
}
LOG_MENU(LOG_APPS,L"Collect Metro Links (end)");
}
void ExecuteMetroLink( const CItemManager::ItemInfo *pInfo )
{
SHELLEXECUTEINFO execute={sizeof(execute),SEE_MASK_CLASSNAME|SEE_MASK_INVOKEIDLIST|SEE_MASK_FLAG_LOG_USAGE|SEE_MASK_FLAG_NO_UI};
wchar_t path[_MAX_PATH];
{
CItemManager::RWLock lock(&g_ItemManager,false,CItemManager::RWLOCK_ITEMS);
Strcpy(path,_countof(path),pInfo->GetPath());
}
execute.lpFile=path;
execute.nShow=SW_SHOWNORMAL;
execute.lpClass=MetroAppClassId;
BOOL res=ShellExecuteEx(&execute);
LOG_MENU(LOG_EXECUTE,L"ExecuteMetroLink: 0x%08X 0x%p",res?0:GetLastError(),execute.hInstApp);
// create UserAssist entry
{
CRegKey regKey;
if (regKey.Open(HKEY_CURRENT_USER,L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced")!=ERROR_SUCCESS)
return;
DWORD val;
if (regKey.QueryDWORDValue(L"Start_TrackProgs",val)==ERROR_SUCCESS && !val)
return;
}
CRegKey regKeyLink;
if (regKeyLink.Open(HKEY_CURRENT_USER,USERASSIST_LINKS_KEY,KEY_READ|KEY_WRITE)!=ERROR_SUCCESS)
return;
EncodeUserAssistPath(path);
EncodeRot13(path);
UserAssistData data;
DWORD size=sizeof(data);
if (regKeyLink.QueryBinaryValue(path,&data,&size)!=ERROR_SUCCESS)
memset(&data,0,sizeof(data));
GetSystemTimeAsFileTime(&data.timestamp);
data.count++;
regKeyLink.SetBinaryValue(path,&data,sizeof(data));
}
/*
void ExecutePCSettings( void )
{
const wchar_t *appid=L"windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel";
CComPtr<IShellItem> pItem;
if (SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder2,0,appid,IID_IShellItem,(void**)&pItem)))
{
CAbsolutePidl pidl;
if (SUCCEEDED(SHGetIDListFromObject(pItem,&pidl)))
{
SHELLEXECUTEINFO execute={sizeof(execute),SEE_MASK_IDLIST|SEE_MASK_FLAG_LOG_USAGE};
execute.lpIDList=pidl;
execute.nShow=SW_SHOWNORMAL;
ShellExecuteEx(&execute);
}
}
}
}
*/
CComPtr<IContextMenu> GetMetroPinMenu( const wchar_t *appid )
{
if (GetWinVersion()<WIN_VER_WIN10 && !IsWin81Update1()) return NULL;
CComPtr<IShellFolder> pAppFolder;
{
CAbsolutePidl pidl;
if (SUCCEEDED(SHGetKnownFolderIDList(FOLDERID_AppsFolder2,KF_FLAG_DEFAULT,NULL,&pidl)))
SHBindToObject(NULL,pidl,NULL,IID_IShellFolder,(void **)&pAppFolder);
}
CComPtr<IContextMenu> pPinMenu;
if (pAppFolder)
{
PIDLIST_RELATIVE child;
if (SUCCEEDED(pAppFolder->ParseDisplayName(NULL,NULL,(LPWSTR)appid,NULL,&child,NULL)))
{
CComPtr<IDataObject> pDataObject;
if (SUCCEEDED(pAppFolder->GetUIObjectOf(NULL,1,(PCUITEMID_CHILD*)&child,IID_IDataObject,NULL,(void**)&pDataObject)))
{
CRegKey regKey;
if (regKey.Open(HKEY_CLASSES_ROOT,MetroAppClassId,KEY_READ)==ERROR_SUCCESS)
{
CComPtr<IShellExtInit> pInit;
pInit.CoCreateInstance(CLSID_PinExt);
if (pInit && SUCCEEDED(pInit->Initialize(NULL,pDataObject,regKey)))
pPinMenu=CComQIPtr<IContextMenu>(pInit);
}
}
ILFree(child);
}
}
return pPinMenu;
}
static const wchar_t *g_UninstallableApps[]={
L"windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel",
L"Microsoft.WindowsStore_8wekyb3d8bbwe!App",
L"Microsoft.Windows.Cortana_cw5n1h2txyewy!CortanaUI",
};
// Returns true if the app should not be uninstalled
bool IsProtectedApp( const wchar_t *appid )
{
for (int i=0;i<_countof(g_UninstallableApps);i++)
{
if (_wcsicmp(appid,g_UninstallableApps[i])==0)
return true;
}
return false;
}
// Returns true if uninstalling is allowed by the policy
bool GetUninstallPolicy( void )
{
if (GetWinVersion()<WIN_VER_WIN8)
return false;
CRegKey regKey;
if (regKey.Open(HKEY_CURRENT_USER,L"Software\\Policies\\Microsoft\\Windows\\Explorer",KEY_READ)==ERROR_SUCCESS)
{
DWORD val;
if (regKey.QueryDWORDValue(L"NoUninstallFromStart",val)==ERROR_SUCCESS && val)
return false;
}
return true;
}
// Checks if the app can be uninstalled
bool CanUninstallMetroApp( const wchar_t *appid )
{
if (IsProtectedApp(appid) || !GetUninstallPolicy())
return false;
CComPtr<IShellItem2> pAppItem;
if (FAILED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder2,0,appid,IID_IShellItem2,(void**)&pAppItem)))
return false;
CComPtr<IPropertyStore> pStore;
PROPERTYKEY keys[]={
PKEY_MetroPackagePath,
PKEY_AppUserModel_ParentID,
PKEY_AppUserModel_InstalledBy,
PKEY_AppUserModel_IsDualMode,
PKEY_Launcher_AppState,
};
if (FAILED(pAppItem->GetPropertyStoreForKeys(keys,_countof(keys),GPS_FASTPROPERTIESONLY,IID_IPropertyStore,(void**)&pStore)))
return false;
CString parentid=GetPropertyStoreString(pStore,PKEY_AppUserModel_ParentID);
if (!parentid.IsEmpty())
return false; // this is a child tile
CString packagePath=GetPropertyStoreString(pStore,PKEY_MetroPackagePath);
if (packagePath.IsEmpty() || GetFileAttributes(packagePath)==INVALID_FILE_ATTRIBUTES)
return false;
PROPVARIANT val;
PropVariantInit(&val);
bool res=true;
if (SUCCEEDED(pStore->GetValue(PKEY_AppUserModel_InstalledBy,&val)))
{
if (val.vt==VT_UI4 && val.intVal==1)
res=false; // from CAppTileHelper::IsInboxAppTile
}
PropVariantClear(&val);
if (res && SUCCEEDED(pStore->GetValue(PKEY_AppUserModel_IsDualMode,&val)))
{
if (val.vt==VT_BOOL && val.boolVal)
res=false;
}
PropVariantClear(&val);
if (!res) return false;
if (SUCCEEDED(pStore->GetValue(PKEY_Launcher_AppState,&val)))
{
if (val.vt!=VT_UI4 || val.intVal==4 || val.intVal==6)
res=false;
}
else
res=false;
PropVariantClear(&val);
return res;
}
// Uninstalls the app with the given id
void UninstallMetroApp( const wchar_t *appid )
{
auto packageName = GetPackageFullName(appid);
if (!packageName.IsEmpty())
{
wchar_t command[1024];
Sprintf(command, _countof(command), L"Remove-AppxPackage %s", packageName);
ShellExecute(NULL, L"open", L"powershell.exe", command, NULL, SW_HIDE);
}
}
// Asks for confirmation to uninstall the specified app
void UninstallMetroApp( HWND parent, const wchar_t *name, const wchar_t *appid )
{
wchar_t text[1024];
Sprintf(text,_countof(text),FindTranslation(L"Menu.UninstallPrompt",L"Are you sure you want to uninstall %s?"),name);
CString title=FindTranslation(L"Menu.UninstallTitle",L"Uninstall");
HICON hIcon=NULL;
CComPtr<IShellItem> pAppItem;
if (SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder2,0,appid,IID_IShellItem,(void**)&pAppItem)))
{
CAbsolutePidl pidl;
SHGetIDListFromObject(pAppItem,&pidl);
const CItemManager::ItemInfo *pItemInfo=g_ItemManager.GetItemInfo(pAppItem,pidl,CItemManager::INFO_LINK|CItemManager::INFO_METRO);
g_ItemManager.UpdateItemInfo(pItemInfo,CItemManager::INFO_LARGE_ICON|CItemManager::INFO_REFRESH_NOW);
HBITMAP hMonoBitmap=CreateBitmap(CItemManager::LARGE_ICON_SIZE,CItemManager::LARGE_ICON_SIZE,1,1,NULL);
ICONINFO info={TRUE,0,0,hMonoBitmap,pItemInfo->largeIcon->bitmap};
hIcon=CreateIconIndirect(&info);
DeleteObject(hMonoBitmap);
}
TASKDIALOGCONFIG task={sizeof(task),parent,NULL,TDF_ALLOW_DIALOG_CANCELLATION|TDF_USE_HICON_MAIN,TDCBF_YES_BUTTON|TDCBF_NO_BUTTON};
task.pszWindowTitle=title;
task.pszContent=text;
task.hMainIcon=hIcon?hIcon:LoadIcon(NULL,IDI_QUESTION);
int res;
if (FAILED(TaskDialogIndirect(&task,&res,NULL,NULL)))
res=0;
if (hIcon) DestroyIcon(hIcon);
if (res==IDYES)
UninstallMetroApp(appid);
}
// Determines if Edge is the default browser
bool IsEdgeDefaultBrowser( void )
{
if (GetWinVersion()<WIN_VER_WIN10)
return false;
CRegKey userKey;
if (userKey.Open(HKEY_CURRENT_USER,L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",KEY_READ)==ERROR_SUCCESS)
{
wchar_t text[_MAX_PATH]=L"";
ULONG size=_countof(text);
if (userKey.QueryStringValue(L"ProgId",text,&size)==ERROR_SUCCESS)
{
wchar_t path[_MAX_PATH];
Sprintf(path,_countof(path),L"%s\\Application",text);
CRegKey appKey;
if (userKey.Open(HKEY_CLASSES_ROOT,path,KEY_READ)==ERROR_SUCCESS)
{
size=_countof(text);
if (userKey.QueryStringValue(L"AppUserModelID",text,&size)==ERROR_SUCCESS)
{
if (_wcsicmp(text,L"Microsoft.MicrosoftEdge_8wekyb3d8bbwe!MicrosoftEdge")==0)
return true;
}
}
}
}
return false;
}
CString GetPackageFullName(const wchar_t* appId)
{
CComPtr<IShellItem> item;
if (SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder, 0, appId, IID_PPV_ARGS(&item))))
{
CComPtr<IPropertyStore> store;
if (SUCCEEDED(item->BindToHandler(nullptr, BHID_PropertyStore, IID_PPV_ARGS(&store))))
return GetPropertyStoreString(store, PKEY_MetroPackageName);
}
return {};
}