[SHELL32] Make Control Panel applets start in single instance (#4405) (#4405)

In MS Windows all control panel applets are started in single instance.
This prevents conflicts of concurrent accesses to the configuration data.

Before starting applets, look up the dialog window of the existing instance
by checking several atoms, such as the applet path, CPLName, and CPLFlags.
If the dialog is found, it's brought to the foreground, and a new applet
instance not started.

CORE-7921 CORE-17025

Signed-off-by: Raymond Czerny <chip@raymisoft.de>
Reviewed-by: Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Reviewed-by: Joachim Henze <joachim.henze@reactos.org>
Reviewed-by: Mark Jansen <mark.jansen@reactos.org>
This commit is contained in:
Raymond Czerny 2022-04-07 14:58:13 +02:00 committed by GitHub
parent 184b8a30cb
commit 1b5abe8837
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -2,6 +2,7 @@
*
* Copyright 2001 Eric Pouech
* Copyright 2008 Owen Rudge
* Copyright 2022 Raymond Czerny
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -736,6 +737,75 @@ static void Control_DoWindow(CPanel* panel, HWND hWnd, HINSTANCE hInst)
}
#endif
#ifdef __REACTOS__
/** Structure for in and out data when
* search for the cpl dialog of first instance
*/
typedef struct tagAppDlgFindData
{
PCWSTR szAppFile; /**< Full path to applet library as search parameter */
UINT_PTR sAppletNo; /**< Number of applet in a system control library as search parameter */
ATOM aCPLName; /**< to read window property 'CPLName' */
ATOM aCPLFlags; /**< to read window property 'CPLFlags'*/
HWND hRunDLL; /**< to skip self instance */
HWND hDlgResult; /**< Returned dialog handle or NULL if not found */
} AppDlgFindData;
/**
* Callback function to search applet dialog
* @param hwnd A handle to a top-level window.
* @param lParam Pointer of AppDlgFindData
* @return TRUE: continue enumeration, FALSE: stop enumeration
*/
static BOOL CALLBACK
Control_EnumWinProc(
_In_ HWND hwnd,
_In_ LPARAM lParam)
{
AppDlgFindData* pData = (AppDlgFindData*)lParam;
WCHAR szClassName[256] = L"";
if (pData->hRunDLL == hwnd)
{
// Skip self instance
return TRUE;
}
if (GetClassNameW(hwnd, szClassName, _countof(szClassName)))
{
// Note: A comparison on identical is not possible, the class names are different.
// ReactOS: 'rundll32_window'
// WinXP: 'RunDLL'
// other OS: not checked
if (StrStrIW(szClassName, L"rundll32") != NULL)
{
UINT_PTR sAppletNo;
sAppletNo = (UINT_PTR)GetPropW(hwnd, (LPTSTR)MAKEINTATOM(pData->aCPLFlags));
if (sAppletNo == pData->sAppletNo)
{
HANDLE hRes;
WCHAR szAppFile[MAX_PATH];
hRes = GetPropW(hwnd, (LPTSTR)MAKEINTATOM(pData->aCPLName));
GlobalGetAtomNameW((ATOM)HandleToUlong(hRes), szAppFile, _countof(szAppFile));
if (wcscmp(szAppFile, pData->szAppFile) == 0)
{
HWND hDialog = GetLastActivePopup(hwnd);
if (IsWindow(hDialog))
{
pData->hDlgResult = hDialog;
return FALSE; // stop enumeration
}
}
}
}
}
return TRUE; // continue enumeration
}
#endif /* __REACTOS__ */
static void Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd)
/* forms to parse:
* foo.cpl,@sp,str
@ -828,6 +898,10 @@ static void Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd)
#ifdef __REACTOS__
ULONG_PTR cookie;
BOOL bActivated;
ATOM aCPLName;
ATOM aCPLFlags;
ATOM aCPLPath;
AppDlgFindData findData;
#endif
/* we've been given a textual parameter (or none at all) */
if (sp == -1) {
@ -846,10 +920,51 @@ static void Control_DoLaunch(CPanel* panel, HWND hWnd, LPCWSTR wszCmd)
#ifdef __REACTOS__
bActivated = (applet->hActCtx != INVALID_HANDLE_VALUE ? ActivateActCtx(applet->hActCtx, &cookie) : FALSE);
aCPLPath = GlobalFindAtomW(applet->cmd);
if (!aCPLPath)
{
aCPLPath = GlobalAddAtomW(applet->cmd);
}
aCPLName = GlobalFindAtomW(L"CPLName");
if (!aCPLName)
{
aCPLName = GlobalAddAtomW(L"CPLName");
}
aCPLFlags = GlobalFindAtomW(L"CPLFlags");
if (!aCPLFlags)
{
aCPLFlags = GlobalAddAtomW(L"CPLFlags");
}
findData.szAppFile = applet->cmd;
findData.sAppletNo = (UINT_PTR)(sp + 1);
findData.aCPLName = aCPLName;
findData.aCPLFlags = aCPLFlags;
findData.hRunDLL = applet->hWnd;
findData.hDlgResult = NULL;
// Find the dialog of this applet in the first instance.
// Note: The simpler functions "FindWindow" or "FindWindowEx" does not find this type of dialogs.
EnumWindows(Control_EnumWinProc, (LPARAM)&findData);
if (findData.hDlgResult)
{
BringWindowToTop(findData.hDlgResult);
}
else
{
SetPropW(applet->hWnd, (LPTSTR)MAKEINTATOM(aCPLName), (HANDLE)MAKEINTATOM(aCPLPath));
SetPropW(applet->hWnd, (LPTSTR)MAKEINTATOM(aCPLFlags), UlongToHandle(sp + 1));
#endif
if (!applet->proc(applet->hWnd, CPL_STARTWPARMSW, sp, (LPARAM)extraPmts))
applet->proc(applet->hWnd, CPL_DBLCLK, sp, applet->info[sp].data);
#ifdef __REACTOS__
RemovePropW(applet->hWnd, applet->cmd);
GlobalDeleteAtom(aCPLPath);
}
#endif
Control_UnloadApplet(applet);