mirror of
https://github.com/Open-Shell/Open-Shell-Menu.git
synced 2026-04-12 01:47:24 +10:00
383 lines
12 KiB
C++
383 lines
12 KiB
C++
// Classic Shell (c) 2009-2016, Ivo Beltchev
|
|
// 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 )
|
|
{
|
|
CComPtr<IShellItem> pAppItem;
|
|
if (SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder2,0,appid,IID_IShellItem,(void**)&pAppItem)))
|
|
{
|
|
CComPtr<IPropertyStore> pStore;
|
|
pAppItem->BindToHandler(NULL,BHID_PropertyStore,IID_IPropertyStore,(void**)&pStore);
|
|
if (pStore)
|
|
{
|
|
CString packageName=GetPropertyStoreString(pStore,PKEY_MetroPackageName);
|
|
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;
|
|
}
|