[EXPLORER][RUNDLL32] Restore minimized non-task windows (#5228)

The minimized non-task windows were not usable due to the bugs. In some situations, the system will restore the minimized non-task windows.
- Add IsTaskWnd helper function.
- Add SendPulseToTray function to send a pulse to the tray window.
- At some shell hook handlings, send a pulse to the tray window.
- Add IgnorePulse flag to control the timing of restoring.
- Add a timer to reset IgnorePulse flag.
- If the pulse has come and IgnorePulse flag is false, then restore the minimized non-task windows.
- Modify the rundll32 window procedure.
- Use WINDOWPLACEMENT to restore the minimized windows.
CORE-13895, CORE-18350
This commit is contained in:
Katayama Hirofumi MZ 2023-05-03 07:39:05 +09:00 committed by GitHub
parent 31fdaca8c0
commit e6bced7a35
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 175 additions and 42 deletions

View file

@ -130,6 +130,7 @@ HRESULT WINAPI _CBandSite_CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void
#define TWM_GETTASKSWITCH (WM_USER + 236) #define TWM_GETTASKSWITCH (WM_USER + 236)
#define TWM_OPENSTARTMENU (WM_USER + 260) #define TWM_OPENSTARTMENU (WM_USER + 260)
#define TWM_SETTINGSCHANGED (WM_USER + 300) #define TWM_SETTINGSCHANGED (WM_USER + 300)
#define TWM_PULSE (WM_USER + 400)
extern const GUID IID_IShellDesktopTray; extern const GUID IID_IShellDesktopTray;
@ -149,6 +150,7 @@ DECLARE_INTERFACE_(ITrayWindow, IUnknown)
STDMETHOD_(HWND, DisplayProperties) (THIS) PURE; STDMETHOD_(HWND, DisplayProperties) (THIS) PURE;
STDMETHOD_(BOOL, ExecContextMenuCmd) (THIS_ UINT uiCmd) PURE; STDMETHOD_(BOOL, ExecContextMenuCmd) (THIS_ UINT uiCmd) PURE;
STDMETHOD_(BOOL, Lock) (THIS_ BOOL bLock) PURE; STDMETHOD_(BOOL, Lock) (THIS_ BOOL bLock) PURE;
STDMETHOD_(BOOL, IsTaskWnd) (THIS_ HWND hWnd) PURE;
}; };
#undef INTERFACE #undef INTERFACE
@ -166,6 +168,7 @@ DECLARE_INTERFACE_(ITrayWindow, IUnknown)
#define ITrayWindow_DisplayProperties(p) (p)->lpVtbl->DisplayProperties(p) #define ITrayWindow_DisplayProperties(p) (p)->lpVtbl->DisplayProperties(p)
#define ITrayWindow_ExecContextMenuCmd(p,a) (p)->lpVtbl->ExecContextMenuCmd(p,a) #define ITrayWindow_ExecContextMenuCmd(p,a) (p)->lpVtbl->ExecContextMenuCmd(p,a)
#define ITrayWindow_Lock(p,a) (p)->lpVtbl->Lock(p,a) #define ITrayWindow_Lock(p,a) (p)->lpVtbl->Lock(p,a)
#define ITrayWindow_IsTaskWnd(p,a) (p)->lpVtbl->IsTaskWnd(p,a)
#endif #endif
HRESULT CreateTrayWindow(ITrayWindow ** ppTray); HRESULT CreateTrayWindow(ITrayWindow ** ppTray);

View file

@ -82,6 +82,7 @@ typedef struct _TASK_ITEM
PTASK_GROUP Group; PTASK_GROUP Group;
INT Index; INT Index;
INT IconIndex; INT IconIndex;
WINDOWPLACEMENT wndpl;
union union
{ {
@ -1112,6 +1113,8 @@ public:
TaskItem->hWnd = hWnd; TaskItem->hWnd = hWnd;
TaskItem->Index = -1; TaskItem->Index = -1;
TaskItem->Group = AddToTaskGroup(hWnd); TaskItem->Group = AddToTaskGroup(hWnd);
TaskItem->wndpl.length = sizeof(TaskItem->wndpl);
::GetWindowPlacement(hWnd, &TaskItem->wndpl);
if (!m_IsDestroying) if (!m_IsDestroying)
{ {
@ -1131,7 +1134,6 @@ public:
} }
CheckActivateTaskItem(TaskItem); CheckActivateTaskItem(TaskItem);
return FALSE; return FALSE;
} }
@ -1384,22 +1386,11 @@ public:
BOOL CALLBACK EnumWindowsProc(IN HWND hWnd) BOOL CALLBACK EnumWindowsProc(IN HWND hWnd)
{ {
/* Only show windows that still exist and are visible and none of explorer's if (m_Tray->IsTaskWnd(hWnd))
special windows (such as the desktop or the tray window) */
if (::IsWindow(hWnd) && ::IsWindowVisible(hWnd) &&
!m_Tray->IsSpecialHWND(hWnd))
{
DWORD exStyle = ::GetWindowLong(hWnd, GWL_EXSTYLE);
/* Don't list popup windows and also no tool windows */
if ((::GetWindow(hWnd, GW_OWNER) == NULL || exStyle & WS_EX_APPWINDOW) &&
!(exStyle & WS_EX_TOOLWINDOW))
{ {
TRACE("Adding task for %p...\n", hWnd); TRACE("Adding task for %p...\n", hWnd);
AddTask(hWnd); AddTask(hWnd);
} }
}
return TRUE; return TRUE;
} }
@ -1475,6 +1466,12 @@ public:
return TRUE; return TRUE;
} }
VOID SendPulseToTray(BOOL bDelete, HWND hwndActive)
{
HWND hwndTray = m_Tray->GetHWND();
::SendMessage(hwndTray, TWM_PULSE, bDelete, (LPARAM)hwndActive);
}
BOOL HandleAppCommand(IN WPARAM wParam, IN LPARAM lParam) BOOL HandleAppCommand(IN WPARAM wParam, IN LPARAM lParam)
{ {
BOOL Ret = FALSE; BOOL Ret = FALSE;
@ -1516,17 +1513,20 @@ public:
break; break;
case HSHELL_WINDOWCREATED: case HSHELL_WINDOWCREATED:
SendPulseToTray(FALSE, (HWND)lParam);
AddTask((HWND) lParam); AddTask((HWND) lParam);
break; break;
case HSHELL_WINDOWDESTROYED: case HSHELL_WINDOWDESTROYED:
/* The window still exists! Delay destroying it a bit */ /* The window still exists! Delay destroying it a bit */
DeleteTask((HWND) lParam); SendPulseToTray(TRUE, (HWND)lParam);
DeleteTask((HWND)lParam);
break; break;
case HSHELL_RUDEAPPACTIVATED: case HSHELL_RUDEAPPACTIVATED:
case HSHELL_WINDOWACTIVATED: case HSHELL_WINDOWACTIVATED:
ActivateTask((HWND) lParam); SendPulseToTray(FALSE, (HWND)lParam);
ActivateTask((HWND)lParam);
break; break;
case HSHELL_FLASH: case HSHELL_FLASH:
@ -1597,12 +1597,17 @@ public:
if (!bIsMinimized && bIsActive) if (!bIsMinimized && bIsActive)
{ {
TaskItem->wndpl.length = sizeof(TaskItem->wndpl);
::GetWindowPlacement(TaskItem->hWnd, &TaskItem->wndpl);
::ShowWindowAsync(TaskItem->hWnd, SW_MINIMIZE); ::ShowWindowAsync(TaskItem->hWnd, SW_MINIMIZE);
TRACE("Valid button clicked. App window Minimized.\n"); TRACE("Valid button clicked. App window Minimized.\n");
} }
else else
{ {
::SwitchToThisWindow(TaskItem->hWnd, TRUE); ::SwitchToThisWindow(TaskItem->hWnd, TRUE);
::SetWindowPlacement(TaskItem->hWnd, &TaskItem->wndpl);
TRACE("Valid button clicked. App window Restored.\n"); TRACE("Valid button clicked. App window Restored.\n");
} }
} }
@ -1631,6 +1636,7 @@ public:
TaskItem = FindTaskItemByIndex((INT) wIndex); TaskItem = FindTaskItemByIndex((INT) wIndex);
if (TaskItem != NULL) if (TaskItem != NULL)
{ {
SendPulseToTray(FALSE, TaskItem->hWnd);
HandleTaskItemClick(TaskItem); HandleTaskItemClick(TaskItem);
return TRUE; return TRUE;
} }

View file

@ -169,7 +169,13 @@ IsThereAnyEffectiveWindow(BOOL bMustBeInMonitor)
return ei.hwndFound != NULL; return ei.hwndFound != NULL;
} }
CSimpleArray<HWND> g_MinimizedAll; /* Minimized window position info */
struct MINWNDPOS
{
HWND hwnd;
WINDOWPLACEMENT wndpl;
};
CSimpleArray<MINWNDPOS> g_MinimizedAll;
/* /*
* ITrayWindow * ITrayWindow
@ -559,6 +565,7 @@ public:
DWORD InSizeMove : 1; DWORD InSizeMove : 1;
DWORD IsDragging : 1; DWORD IsDragging : 1;
DWORD NewPosSize : 1; DWORD NewPosSize : 1;
DWORD IgnorePulse : 1;
}; };
}; };
@ -587,6 +594,7 @@ public:
ZeroMemory(&m_TraySize, sizeof(m_TraySize)); ZeroMemory(&m_TraySize, sizeof(m_TraySize));
ZeroMemory(&m_AutoHideOffset, sizeof(m_AutoHideOffset)); ZeroMemory(&m_AutoHideOffset, sizeof(m_AutoHideOffset));
ZeroMemory(&m_MouseTrackingInfo, sizeof(m_MouseTrackingInfo)); ZeroMemory(&m_MouseTrackingInfo, sizeof(m_MouseTrackingInfo));
IgnorePulse = TRUE;
} }
virtual ~CTrayWindow() virtual ~CTrayWindow()
@ -2460,6 +2468,22 @@ ChangePos:
return bPrevLock; return bPrevLock;
} }
/* The task window is visible and non-WS_EX_TOOLWINDOW and
{ has WS_EX_APPWINDOW style or has no owner } and is none of explorer's
special windows (such as the desktop or the tray window) */
BOOL STDMETHODCALLTYPE IsTaskWnd(HWND hWnd)
{
if (::IsWindow(hWnd) && ::IsWindowVisible(hWnd) && !IsSpecialHWND(hWnd))
{
DWORD exStyle = (DWORD)::GetWindowLongPtr(hWnd, GWL_EXSTYLE);
if (((exStyle & WS_EX_APPWINDOW) || ::GetWindow(hWnd, GW_OWNER) == NULL) &&
!(exStyle & WS_EX_TOOLWINDOW))
{
return TRUE;
}
}
return FALSE;
}
/* /*
* IContextMenu * IContextMenu
@ -2596,6 +2620,15 @@ ChangePos:
return TRUE; return TRUE;
} }
#define TIMER_ID_IGNOREPULSERESET 888
#define TIMER_IGNOREPULSERESET_TIMEOUT 200
LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
KillTimer(TIMER_ID_IGNOREPULSERESET);
return 0;
}
LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) LRESULT OnThemeChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{ {
if (m_Theme) if (m_Theme)
@ -3160,6 +3193,39 @@ HandleTrayContextMenu:
return (LRESULT)m_TaskSwitch; return (LRESULT)m_TaskSwitch;
} }
void RestoreMinimizedNonTaskWnds(BOOL bDestroyed, HWND hwndActive)
{
for (INT i = g_MinimizedAll.GetSize() - 1; i >= 0; --i)
{
HWND hwnd = g_MinimizedAll[i].hwnd;
if (!hwnd || hwndActive == hwnd)
continue;
if (::IsWindowVisible(hwnd) && ::IsIconic(hwnd) &&
(!IsTaskWnd(hwnd) || !::IsWindowEnabled(hwnd)))
{
::SetWindowPlacement(hwnd, &g_MinimizedAll[i].wndpl); // Restore
}
}
g_MinimizedAll.RemoveAll();
if (!bDestroyed)
::SetForegroundWindow(hwndActive);
}
LRESULT OnPulse(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
if (IgnorePulse)
return 0;
KillTimer(TIMER_ID_IGNOREPULSERESET);
IgnorePulse = TRUE;
RestoreMinimizedNonTaskWnds((BOOL)wParam, (HWND)lParam);
SetTimer(TIMER_ID_IGNOREPULSERESET, TIMER_IGNOREPULSERESET_TIMEOUT, NULL);
return 0;
}
LRESULT OnHotkey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) LRESULT OnHotkey(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{ {
return HandleHotKey(wParam); return HandleHotKey(wParam);
@ -3170,8 +3236,7 @@ HandleTrayContextMenu:
HWND hwndDesktop; HWND hwndDesktop;
HWND hTrayWnd; HWND hTrayWnd;
HWND hwndProgman; HWND hwndProgman;
BOOL bRet; CSimpleArray<MINWNDPOS> *pMinimizedAll;
CSimpleArray<HWND> *pMinimizedAll;
BOOL bShowDesktop; BOOL bShowDesktop;
}; };
@ -3185,11 +3250,9 @@ HandleTrayContextMenu:
static BOOL CALLBACK MinimizeWindowsProc(HWND hwnd, LPARAM lParam) static BOOL CALLBACK MinimizeWindowsProc(HWND hwnd, LPARAM lParam)
{ {
MINIMIZE_INFO *info = (MINIMIZE_INFO *)lParam; MINIMIZE_INFO *info = (MINIMIZE_INFO *)lParam;
if (hwnd == info->hwndDesktop || hwnd == info->hTrayWnd || if (hwnd == info->hwndDesktop || hwnd == info->hTrayWnd || hwnd == info->hwndProgman)
hwnd == info->hwndProgman) return TRUE; // Ignore special windows
{
return TRUE;
}
if (!info->bShowDesktop) if (!info->bShowDesktop)
{ {
if (!::IsWindowEnabled(hwnd) || IsDialog(hwnd)) if (!::IsWindowEnabled(hwnd) || IsDialog(hwnd))
@ -3198,35 +3261,38 @@ HandleTrayContextMenu:
if (hwndOwner && !::IsWindowEnabled(hwndOwner)) if (hwndOwner && !::IsWindowEnabled(hwndOwner))
return TRUE; return TRUE;
} }
if (::IsWindowVisible(hwnd) && !::IsIconic(hwnd)) if (::IsWindowVisible(hwnd) && !::IsIconic(hwnd))
{ {
MINWNDPOS mwp;
mwp.hwnd = hwnd;
mwp.wndpl.length = sizeof(mwp.wndpl);
::GetWindowPlacement(hwnd, &mwp.wndpl); // Save the position and status
info->pMinimizedAll->Add(mwp);
::ShowWindowAsync(hwnd, SW_MINIMIZE); ::ShowWindowAsync(hwnd, SW_MINIMIZE);
info->bRet = TRUE;
info->pMinimizedAll->Add(hwnd);
} }
return TRUE; return TRUE;
} }
VOID MinimizeAll(BOOL bShowDesktop = FALSE) VOID MinimizeAll(BOOL bShowDesktop = FALSE)
{ {
IgnorePulse = TRUE;
KillTimer(TIMER_ID_IGNOREPULSERESET);
MINIMIZE_INFO info; MINIMIZE_INFO info;
info.hwndDesktop = GetDesktopWindow();; info.hwndDesktop = GetDesktopWindow();;
info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL); info.hTrayWnd = FindWindowW(L"Shell_TrayWnd", NULL);
info.hwndProgman = FindWindowW(L"Progman", NULL); info.hwndProgman = FindWindowW(L"Progman", NULL);
info.bRet = FALSE;
info.pMinimizedAll = &g_MinimizedAll; info.pMinimizedAll = &g_MinimizedAll;
info.bShowDesktop = bShowDesktop; info.bShowDesktop = bShowDesktop;
EnumWindows(MinimizeWindowsProc, (LPARAM)&info); EnumWindows(MinimizeWindowsProc, (LPARAM)&info);
// invalid handles should be cleared to avoid mismatch of handles
for (INT i = 0; i < g_MinimizedAll.GetSize(); ++i)
{
if (!::IsWindow(g_MinimizedAll[i]))
g_MinimizedAll[i] = NULL;
}
::SetForegroundWindow(m_DesktopWnd); ::SetForegroundWindow(m_DesktopWnd);
::SetFocus(m_DesktopWnd); ::SetFocus(m_DesktopWnd);
SetTimer(TIMER_ID_IGNOREPULSERESET, TIMER_IGNOREPULSERESET_TIMEOUT, NULL);
} }
VOID ShowDesktop() VOID ShowDesktop()
@ -3236,15 +3302,20 @@ HandleTrayContextMenu:
VOID RestoreAll() VOID RestoreAll()
{ {
IgnorePulse = TRUE;
KillTimer(TIMER_ID_IGNOREPULSERESET);
for (INT i = g_MinimizedAll.GetSize() - 1; i >= 0; --i) for (INT i = g_MinimizedAll.GetSize() - 1; i >= 0; --i)
{ {
HWND hwnd = g_MinimizedAll[i]; HWND hwnd = g_MinimizedAll[i].hwnd;
if (::IsWindowVisible(hwnd) && ::IsIconic(hwnd)) if (::IsWindowVisible(hwnd) && ::IsIconic(hwnd))
{ {
::ShowWindowAsync(hwnd, SW_RESTORE); ::SetWindowPlacement(hwnd, &g_MinimizedAll[i].wndpl);
} }
} }
g_MinimizedAll.RemoveAll(); g_MinimizedAll.RemoveAll();
SetTimer(TIMER_ID_IGNOREPULSERESET, TIMER_IGNOREPULSERESET_TIMEOUT, NULL);
} }
LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) LRESULT OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
@ -3288,9 +3359,12 @@ HandleTrayContextMenu:
{ {
ProcessAutoHide(); ProcessAutoHide();
} }
else if (wParam == TIMER_ID_IGNOREPULSERESET)
bHandled = FALSE; {
return TRUE; KillTimer(TIMER_ID_IGNOREPULSERESET);
IgnorePulse = FALSE;
}
return 0;
} }
LRESULT OnNcActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) LRESULT OnNcActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
@ -3479,7 +3553,7 @@ HandleTrayContextMenu:
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground) MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
MESSAGE_HANDLER(WM_SIZE, OnSize) MESSAGE_HANDLER(WM_SIZE, OnSize)
MESSAGE_HANDLER(WM_CREATE, OnCreate) MESSAGE_HANDLER(WM_CREATE, OnCreate)
/*MESSAGE_HANDLER(WM_DESTROY, OnDestroy)*/ MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest) MESSAGE_HANDLER(WM_NCHITTEST, OnNcHitTest)
MESSAGE_HANDLER(WM_COMMAND, OnCommand) MESSAGE_HANDLER(WM_COMMAND, OnCommand)
MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand) MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)
@ -3512,6 +3586,7 @@ HandleTrayContextMenu:
MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu) MESSAGE_HANDLER(TWM_OPENSTARTMENU, OnOpenStartMenu)
MESSAGE_HANDLER(TWM_DOEXITWINDOWS, OnDoExitWindows) MESSAGE_HANDLER(TWM_DOEXITWINDOWS, OnDoExitWindows)
MESSAGE_HANDLER(TWM_GETTASKSWITCH, OnGetTaskSwitch) MESSAGE_HANDLER(TWM_GETTASKSWITCH, OnGetTaskSwitch)
MESSAGE_HANDLER(TWM_PULSE, OnPulse)
ALT_MSG_MAP(1) ALT_MSG_MAP(1)
END_MSG_MAP() END_MSG_MAP()

View file

@ -29,6 +29,7 @@
#include <winnls.h> #include <winnls.h>
#include <winuser.h> #include <winuser.h>
#include <tchar.h> #include <tchar.h>
#include <undocuser.h> // For WM_POPUPSYSTEMMENU
#include "resource.h" #include "resource.h"
@ -299,9 +300,57 @@ LPSTR DuplicateToMultiByte(LPCTSTR lptString, size_t nBufferSize)
return lpString; return lpString;
} }
typedef struct
{
HWND hwndOwner;
HWND hwndTarget;
} FIND_OWNED, *PFIND_OWNED;
static BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
PFIND_OWNED pFindOwned = (PFIND_OWNED)lParam;
if (pFindOwned->hwndOwner == GetWindow(hwnd, GW_OWNER))
{
pFindOwned->hwndTarget = hwnd;
return FALSE;
}
return TRUE;
}
LRESULT CALLBACK EmptyWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) LRESULT CALLBACK EmptyWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{ {
switch (uMsg)
{
case WM_POPUPSYSTEMMENU:
case WM_SYSCOMMAND:
{
/* Find the owned window */
FIND_OWNED FindOwned = { hWnd, NULL };
EnumWindows(EnumWindowsProc, (LPARAM)&FindOwned);
/* Forward message */
if (FindOwned.hwndTarget)
PostMessageW(FindOwned.hwndTarget, uMsg, wParam, lParam);
break;
}
case WM_ACTIVATE:
{
/* Find the owned window */
FIND_OWNED FindOwned = { hWnd, NULL };
EnumWindows(EnumWindowsProc, (LPARAM)&FindOwned);
if (FindOwned.hwndTarget)
{
if (LOWORD(wParam) != WA_INACTIVE) /* To be activated */
{
SetActiveWindow(FindOwned.hwndTarget);
return 0;
}
}
/* Fall through */
}
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam); return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
} }
// Registers a minimal window class for passing to the dll function // Registers a minimal window class for passing to the dll function