mirror of
https://github.com/Open-Shell/Open-Shell-Menu.git
synced 2026-10-09 21:21:14 +11:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88e8cd8653 | ||
|
|
ef61ce1dc0 | ||
|
|
2e2f681c5b | ||
|
|
523ff4b9f4 | ||
|
|
1e7a52a5ab |
@@ -47,6 +47,69 @@ struct Chunk
|
|||||||
int start1, start2, len;
|
int start1, start2, len;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool IsExplorerShellRunning( void )
|
||||||
|
{
|
||||||
|
HWND shellWindow=GetShellWindow();
|
||||||
|
if (!shellWindow)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DWORD shellProcessId=0;
|
||||||
|
GetWindowThreadProcessId(shellWindow,&shellProcessId);
|
||||||
|
if (!shellProcessId)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DWORD currentSession=0;
|
||||||
|
DWORD shellSession=0;
|
||||||
|
if (!ProcessIdToSessionId(GetCurrentProcessId(),¤tSession) ||
|
||||||
|
!ProcessIdToSessionId(shellProcessId,&shellSession) ||
|
||||||
|
currentSession!=shellSession)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE process=OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,FALSE,shellProcessId);
|
||||||
|
if (!process)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wchar_t path[_MAX_PATH];
|
||||||
|
DWORD pathSize=_countof(path);
|
||||||
|
bool isExplorer=false;
|
||||||
|
if (QueryFullProcessImageName(process,0,path,&pathSize))
|
||||||
|
isExplorer=_wcsicmp(PathFindFileName(path),L"explorer.exe")==0;
|
||||||
|
|
||||||
|
CloseHandle(process);
|
||||||
|
return isExplorer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RestoreExplorerShellIfNeeded( bool wasRunning )
|
||||||
|
{
|
||||||
|
if (!wasRunning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Restart Manager normally brings Explorer back before msiexec exits.
|
||||||
|
// Give it a short grace period before applying our fallback so we don't
|
||||||
|
// race a normal restart or create an extra Explorer process.
|
||||||
|
for (int i=0;i<50;i++)
|
||||||
|
{
|
||||||
|
if (IsExplorerShellRunning())
|
||||||
|
return;
|
||||||
|
Sleep(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t explorerPath[_MAX_PATH];
|
||||||
|
UINT len=GetWindowsDirectory(explorerPath,_countof(explorerPath));
|
||||||
|
if (!len || len>=_countof(explorerPath) || !PathAppend(explorerPath,L"explorer.exe"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
STARTUPINFO startupInfo={sizeof(startupInfo)};
|
||||||
|
PROCESS_INFORMATION processInfo={};
|
||||||
|
if (CreateProcess(explorerPath,NULL,NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo))
|
||||||
|
{
|
||||||
|
CloseHandle(processInfo.hThread);
|
||||||
|
CloseHandle(processInfo.hProcess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void WriteFileXOR( HANDLE hFile, const unsigned char *buf, int size )
|
static void WriteFileXOR( HANDLE hFile, const unsigned char *buf, int size )
|
||||||
{
|
{
|
||||||
if (size>0)
|
if (size>0)
|
||||||
@@ -409,6 +472,11 @@ int APIENTRY wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCm
|
|||||||
if (extractType == ARM64)
|
if (extractType == ARM64)
|
||||||
Wow64DisableWow64FsRedirection(&wow64FsRedirVal);
|
Wow64DisableWow64FsRedirection(&wow64FsRedirVal);
|
||||||
|
|
||||||
|
// Remember whether this interactive session had the Explorer desktop shell.
|
||||||
|
// If Restart Manager closes it during MSI processing but fails to bring it
|
||||||
|
// back, restore only the shell that was present before setup started.
|
||||||
|
const bool explorerWasRunning=IsExplorerShellRunning();
|
||||||
|
|
||||||
// start the installer
|
// start the installer
|
||||||
STARTUPINFO startupInfo={sizeof(startupInfo)};
|
STARTUPINFO startupInfo={sizeof(startupInfo)};
|
||||||
PROCESS_INFORMATION processInfo;
|
PROCESS_INFORMATION processInfo;
|
||||||
@@ -438,6 +506,12 @@ int APIENTRY wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCm
|
|||||||
GetExitCodeProcess(processInfo.hProcess,&code);
|
GetExitCodeProcess(processInfo.hProcess,&code);
|
||||||
CloseHandle(processInfo.hProcess);
|
CloseHandle(processInfo.hProcess);
|
||||||
DeleteFile(msiName);
|
DeleteFile(msiName);
|
||||||
|
|
||||||
|
// Only repair the shell after a successful transaction. Reboot-required
|
||||||
|
// outcomes deliberately keep Windows Installer in control of recovery.
|
||||||
|
if (code==ERROR_SUCCESS)
|
||||||
|
RestoreExplorerShellIfNeeded(explorerWasRunning);
|
||||||
|
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3337,19 +3337,19 @@ void CItemManager::LoadCacheFile( void )
|
|||||||
info.iconColor=data.iconColor;
|
info.iconColor=data.iconColor;
|
||||||
info.iconIndex=data.iconIndex;
|
info.iconIndex=data.iconIndex;
|
||||||
|
|
||||||
info.smallIcon=data.smallIcon<(int)remapIcons.size()?remapIcons[data.smallIcon]:NULL;
|
info.smallIcon=data.smallIcon>=0 && (size_t)data.smallIcon<remapIcons.size()?remapIcons[data.smallIcon]:NULL;
|
||||||
if (!info.smallIcon)
|
if (!info.smallIcon)
|
||||||
{
|
{
|
||||||
info.validFlags&=~INFO_SMALL_ICON;
|
info.validFlags&=~INFO_SMALL_ICON;
|
||||||
info.smallIcon=m_DefaultSmallIcon;
|
info.smallIcon=m_DefaultSmallIcon;
|
||||||
}
|
}
|
||||||
info.largeIcon=data.largeIcon<(int)remapIcons.size()?remapIcons[data.largeIcon]:NULL;
|
info.largeIcon=data.largeIcon>=0 && (size_t)data.largeIcon<remapIcons.size()?remapIcons[data.largeIcon]:NULL;
|
||||||
if (!info.largeIcon)
|
if (!info.largeIcon)
|
||||||
{
|
{
|
||||||
info.validFlags&=~INFO_LARGE_ICON;
|
info.validFlags&=~INFO_LARGE_ICON;
|
||||||
info.largeIcon=m_DefaultLargeIcon;
|
info.largeIcon=m_DefaultLargeIcon;
|
||||||
}
|
}
|
||||||
info.extraLargeIcon=data.extraLargeIcon<(int)remapIcons.size()?remapIcons[data.extraLargeIcon]:NULL;
|
info.extraLargeIcon=data.extraLargeIcon>=0 && (size_t)data.extraLargeIcon<remapIcons.size()?remapIcons[data.extraLargeIcon]:NULL;
|
||||||
if (!info.extraLargeIcon)
|
if (!info.extraLargeIcon)
|
||||||
{
|
{
|
||||||
info.validFlags&=~INFO_EXTRA_LARGE_ICON;
|
info.validFlags&=~INFO_EXTRA_LARGE_ICON;
|
||||||
@@ -3422,7 +3422,7 @@ DWORD CALLBACK CItemManager::SaveCacheFileThread( void *param )
|
|||||||
RWLock lock(pThis,false,RWLOCK_ICONS);
|
RWLock lock(pThis,false,RWLOCK_ICONS);
|
||||||
for (std::multimap<unsigned int,IconInfo>::const_iterator it=pThis->m_IconInfos.begin();it!=pThis->m_IconInfos.end();++it)
|
for (std::multimap<unsigned int,IconInfo>::const_iterator it=pThis->m_IconInfos.begin();it!=pThis->m_IconInfos.end();++it)
|
||||||
{
|
{
|
||||||
if (!it->second.PATH.IsEmpty() && it->second.PATH[1]!='#' && it->first!=0)
|
if (!it->second.bTemp && !it->second.bMetro && !it->second.PATH.IsEmpty() && it->second.PATH[1]!='#' && it->first!=0)
|
||||||
iconInfos.push_back(&*it);
|
iconInfos.push_back(&*it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3433,7 +3433,7 @@ DWORD CALLBACK CItemManager::SaveCacheFileThread( void *param )
|
|||||||
RWLock lock(pThis,false,RWLOCK_ITEMS);
|
RWLock lock(pThis,false,RWLOCK_ITEMS);
|
||||||
for (std::multimap<unsigned int,ItemInfo>::const_iterator it=pThis->m_ItemInfos.begin();it!=pThis->m_ItemInfos.end();++it)
|
for (std::multimap<unsigned int,ItemInfo>::const_iterator it=pThis->m_ItemInfos.begin();it!=pThis->m_ItemInfos.end();++it)
|
||||||
{
|
{
|
||||||
if (it->first!=0)
|
if (!it->second.bTemp && it->first!=0)
|
||||||
itemInfos.push_back(&*it);
|
itemInfos.push_back(&*it);
|
||||||
}
|
}
|
||||||
for (std::set<unsigned int>::const_iterator it=pThis->m_BlackListInfos10.begin();it!=pThis->m_BlackListInfos10.end();++it)
|
for (std::set<unsigned int>::const_iterator it=pThis->m_BlackListInfos10.begin();it!=pThis->m_BlackListInfos10.end();++it)
|
||||||
@@ -3566,6 +3566,15 @@ void CItemManager::SaveCacheFile( void )
|
|||||||
|
|
||||||
void CItemManager::ClearCache( void )
|
void CItemManager::ClearCache( void )
|
||||||
{
|
{
|
||||||
|
// The save thread keeps pointers to persistent cache entries while serializing them.
|
||||||
|
// Let it finish before clearing the containers those pointers refer to.
|
||||||
|
if (m_SaveCacheThread)
|
||||||
|
{
|
||||||
|
WaitForSingleObject(m_SaveCacheThread,INFINITE);
|
||||||
|
CloseHandle(m_SaveCacheThread);
|
||||||
|
m_SaveCacheThread=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
Lock cleanupLock(this,LOCK_CLEANUP);
|
Lock cleanupLock(this,LOCK_CLEANUP);
|
||||||
RWLock itemLock(this,true,RWLOCK_ITEMS);
|
RWLock itemLock(this,true,RWLOCK_ITEMS);
|
||||||
RWLock iconLock(this,true,RWLOCK_ICONS);
|
RWLock iconLock(this,true,RWLOCK_ICONS);
|
||||||
|
|||||||
@@ -63,14 +63,6 @@ static auto ExecuteOnSTAThread(TFunc&& func) -> decltype(func())
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper for IContextMenu::InvokeCommand that runs on separate STA thread and pumps messages
|
|
||||||
static HRESULT InvokeCommandSafe(IContextMenu* pMenu, LPCMINVOKECOMMANDINFO pInfo)
|
|
||||||
{
|
|
||||||
return ExecuteOnSTAThread([&]() {
|
|
||||||
return pMenu->InvokeCommand(pInfo);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper for SHOpenFolderAndSelectItems that runs on separate STA thread and pumps messages
|
// Wrapper for SHOpenFolderAndSelectItems that runs on separate STA thread and pumps messages
|
||||||
static HRESULT SHOpenFolderAndSelectItemsSafe(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD dwFlags)
|
static HRESULT SHOpenFolderAndSelectItemsSafe(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD dwFlags)
|
||||||
{
|
{
|
||||||
@@ -3182,12 +3174,13 @@ void CMenuContainer::ActivateItem( int index, TActivateType type, const POINT *p
|
|||||||
{
|
{
|
||||||
HRESULT hr{};
|
HRESULT hr{};
|
||||||
auto verb = GetContextMenuItemVerb(pInvokeMenu, (UINT_PTR)info.lpVerbW);
|
auto verb = GetContextMenuItemVerb(pInvokeMenu, (UINT_PTR)info.lpVerbW);
|
||||||
// if the verb is "properties", we need to invoke it directly in this UI thread (as it needs to be called on thread that crated context menu object),
|
// "opencontaining" may block the UI thread on recent Windows 11 versions.
|
||||||
// otherwise we will invoke the command on separate STA thread to make sure it won't block UI thread
|
// Use the shell API on a worker STA for that operation, without passing
|
||||||
if (verb.CompareNoCase(L"properties") == 0)
|
// the apartment-bound IContextMenu interface to another thread.
|
||||||
hr = pInvokeMenu->InvokeCommand((LPCMINVOKECOMMANDINFO)&info);
|
if (verb.CompareNoCase(L"opencontaining") == 0)
|
||||||
|
hr = SHOpenFolderAndSelectItemsSafe(pItemPidl1, 0, NULL, 0);
|
||||||
else
|
else
|
||||||
hr = InvokeCommandSafe(pInvokeMenu, (LPCMINVOKECOMMANDINFO)&info);
|
hr = pInvokeMenu->InvokeCommand((LPCMINVOKECOMMANDINFO)&info);
|
||||||
LOG_MENU(LOG_EXECUTE,L"Invoke command, ptr=%p, res=%d",this,hr);
|
LOG_MENU(LOG_EXECUTE,L"Invoke command, ptr=%p, res=%d",this,hr);
|
||||||
executeSuccess=SUCCEEDED(hr);
|
executeSuccess=SUCCEEDED(hr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2537,10 +2537,11 @@ void CMenuContainer::InitItems( void )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Items.empty() && m_Path1[0] && m_pDropFolder[0])
|
if (!m_bSubMenu && m_Items.empty() && m_Path1[0] && m_pDropFolder[0])
|
||||||
{
|
{
|
||||||
// add (Empty) item to the empty submenus
|
// Keep the top-level drop target placeholder. Submenus defer their
|
||||||
MenuItem item(m_bSubMenu?MENU_EMPTY:MENU_EMPTY_TOP);
|
// empty-state decision until after custom/standard items are appended.
|
||||||
|
MenuItem item(MENU_EMPTY_TOP);
|
||||||
item.name=FindTranslation(L"Menu.Empty",L"(Empty)");
|
item.name=FindTranslation(L"Menu.Empty",L"(Empty)");
|
||||||
m_Items.push_back(item);
|
m_Items.push_back(item);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user