https://git.reactos.org/?p=reactos.git;a=commitdiff;h=1b5abe8837e443f893552…
commit 1b5abe8837e443f893552ecf2c9165f9c6bdd19b
Author: Raymond Czerny <102076097+RaymondCzerny(a)users.noreply.github.com>
AuthorDate: Thu Apr 7 14:58:13 2022 +0200
Commit: GitHub <noreply(a)github.com>
CommitDate: Thu Apr 7 15:58:13 2022 +0300
[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(a)raymisoft.de>
Reviewed-by: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
Reviewed-by: Joachim Henze <joachim.henze(a)reactos.org>
Reviewed-by: Mark Jansen <mark.jansen(a)reactos.org>
---
dll/win32/shell32/wine/control.c | 115 +++++++++++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)
diff --git a/dll/win32/shell32/wine/control.c b/dll/win32/shell32/wine/control.c
index 272747dc3f0..385f985b0f3 100644
--- a/dll/win32/shell32/wine/control.c
+++ b/dll/win32/shell32/wine/control.c
@@ -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);