https://git.reactos.org/?p=reactos.git;a=commitdiff;h=d41dec2e07f919235406c…
commit d41dec2e07f919235406ca3a4d47e8b6ae1aa588
Author: Whindmar Saksit <whindsaks(a)proton.me>
AuthorDate: Wed Jan 17 17:07:21 2024 +0100
Commit: GitHub <noreply(a)github.com>
CommitDate: Wed Jan 17 17:07:21 2024 +0100
[SHELL32][CONTROL] Added basic IOpenControlPanel support (#6248)
Add a basic IOpenControlPanel implementation that supports Vista canonical registry
names.
Implements `control.exe /name company.name [/page id]` and `IOpenControlPanel`
handling of Vista-style canonical registry names.
The documented `Microsoft.*` names don't work because they are simply not
in our registry but "[Executable Control Panel
Items](https://learn.microsoft.com/en-us/windows/win32/shell/how-to-registe…
registered by 3rd-party ISVs
will function correctly in control.exe and the COM API.
Notes:
- `IOpenControlPanel` is implemented in CControlPanelFolder.cpp because
it is supposed to have tighter integration with that shell folder than
it does in this PR.
- `IOpenControlPanel` is also supposed to handle .cpl files with canonical
names registered under [`Extended
Properties`](https://learn.microsoft.com/en-us/windows/win32/shell/how-to-r…
but the control panel folder
does not implement `IShellFolder2::GetDetailsEx` yet, so it will have to wait.
- These "Executable Control Panel Items" are also supposed to be displayed
in the control panel itself but this PR does not address that. The
`ITEMIDLIST` format for those needs investigation...
- The Wow64 handling is perhaps not correct but it does not matter,
`ShellExecuteEx` gets to deal with whatever is in the `...\shell\open\command`
key.
`CControlPanelFolder` would have to take more care when it starts
reading those keys so it knows when to append "(32-bit)" to the display
name.
- `%s%s` because .cpl canonical names don't have the `::` prefix according
to Geoff Chappell.
- Always returns `CPVIEW_CLASSIC` because our `CControlPanelFolder` does
not support the category view.
---
base/applications/control/CMakeLists.txt | 2 +
base/applications/control/control.c | 63 +++++++++-
dll/win32/shell32/folders/CControlPanelFolder.cpp | 141 ++++++++++++++++++++++
dll/win32/shell32/folders/CControlPanelFolder.h | 21 ++++
dll/win32/shell32/res/rgs/controlpanel.rgs | 22 ++++
dll/win32/shell32/shell32.cpp | 1 +
sdk/include/psdk/shlguid.h | 1 +
sdk/include/psdk/shobjidl.idl | 31 +++++
8 files changed, 281 insertions(+), 1 deletion(-)
diff --git a/base/applications/control/CMakeLists.txt
b/base/applications/control/CMakeLists.txt
index 06e49c19229..827d6fa4be3 100644
--- a/base/applications/control/CMakeLists.txt
+++ b/base/applications/control/CMakeLists.txt
@@ -2,5 +2,7 @@
add_rc_deps(control.rc ${CMAKE_CURRENT_SOURCE_DIR}/resources/config.ico)
add_executable(control control.c control.rc)
set_module_type(control win32gui UNICODE)
+add_delay_importlibs(control ole32)
+target_link_libraries(control uuid)
add_importlibs(control advapi32 shell32 user32 msvcrt kernel32)
add_cd_file(TARGET control DESTINATION reactos/system32 FOR all)
diff --git a/base/applications/control/control.c b/base/applications/control/control.c
index a13000c1153..3a39694d1c9 100644
--- a/base/applications/control/control.c
+++ b/base/applications/control/control.c
@@ -4,12 +4,14 @@
* PURPOSE: ReactOS System Control Panel
* COPYRIGHT: Copyright 2004 Gero Kuehn (reactos.filter(a)gkware.com)
* Copyright 2008 Colin Finck (colin(a)reactos.org)
- * Copyright 2014 Herm�s B�lusca-Ma�to (hermes.belusca-maito(a)reactos.org)
+ * Copyright 2014 Hermès Bélusca-Maïto (hermes.belusca-maito(a)reactos.org)
*/
#include <stdio.h>
#define WIN32_NO_STATUS
+#define COBJMACROS
+
#include <windef.h>
#include <winbase.h>
@@ -17,6 +19,9 @@
#include <winreg.h>
#include <shellapi.h>
#include <strsafe.h>
+#include <objbase.h>
+#include <shobjidl.h>
+#include <shlguid.h>
#include "resource.h"
@@ -34,6 +39,35 @@ VOID
WINAPI
Control_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow);
+static BOOL
+IsSwitch(LPCWSTR Switch, LPCWSTR Arg)
+{
+ if (*Arg == '/' || *Arg == '-')
+ {
+ return !lstrcmpiW(Arg+1, Switch);
+ }
+ return FALSE;
+}
+
+static HRESULT
+OpenControlPanelItem(LPCWSTR Name, LPCWSTR Page)
+{
+ HRESULT hr = CoInitialize(0);
+ if (SUCCEEDED(hr))
+ {
+ IOpenControlPanel *pOCP;
+ hr = CoCreateInstance(&CLSID_OpenControlPanel, NULL, CLSCTX_INPROC_SERVER,
+ &IID_IOpenControlPanel, (void**)&pOCP);
+ if (SUCCEEDED(hr))
+ {
+ hr = IOpenControlPanel_Open(pOCP, Name, Page, NULL);
+ IOpenControlPanel_Release(pOCP);
+ }
+ CoUninitialize();
+ }
+ return hr;
+}
+
static INT
OpenShellFolder(LPWSTR lpFolderCLSID)
{
@@ -73,11 +107,16 @@ wWinMain(HINSTANCE hInstance,
INT nCmdShow)
{
HKEY hKey;
+ LPWSTR *argv;
+ int argc;
/* Show the control panel window if no argument or "panel" was passed */
if (*lpCmdLine == 0 || !_wcsicmp(lpCmdLine, L"panel"))
return OpenShellFolder(L"");
+ /* Map legacy control panels */
+ if (!_wcsicmp(lpCmdLine, L"sticpl.cpl")) lpCmdLine = (LPWSTR)
L"scannercamera";
+
/* Check one of the built-in control panel handlers */
if (!_wcsicmp(lpCmdLine, L"admintools")) return
OpenShellFolder(L"\\::{D20EA4E1-3957-11d2-A40B-0C5020524153}");
else if (!_wcsicmp(lpCmdLine, L"color")) return
RunControlPanel(L"desk.cpl,,2");
@@ -99,6 +138,28 @@ wWinMain(HINSTANCE hInstance,
else if (!_wcsicmp(lpCmdLine, L"userpasswords")) return
RunControlPanel(L"nusrmgr.cpl"); /* Graphical User Account Manager */
else if (!_wcsicmp(lpCmdLine, L"userpasswords2")) return
RUNDLL(L"netplwiz.dll,UsersRunDll"); /* Dialog based advanced User Account
Manager */
+ /*
https://learn.microsoft.com/en-us/windows/win32/shell/executing-control-pan…
*/
+ argv = CommandLineToArgvW(lpCmdLine, &argc);
+ if (argv)
+ {
+ UINT argi = 0;
+ HRESULT hr = -1;
+ if (argc >= 2 && IsSwitch(L"name", argv[argi + 0]))
+ {
+ LPCWSTR pszPage = NULL;
+ if (argc >= 4 && IsSwitch(L"page", argv[argi + 2]))
+ {
+ pszPage = argv[argi + 3];
+ }
+ hr = OpenControlPanelItem(argv[argi + 1], pszPage);
+ }
+ LocalFree(argv);
+ if (hr != -1)
+ {
+ return SUCCEEDED(hr);
+ }
+ }
+
/* It is none of them, so look for a handler in the registry */
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Control
Panel\\Cpls",
diff --git a/dll/win32/shell32/folders/CControlPanelFolder.cpp
b/dll/win32/shell32/folders/CControlPanelFolder.cpp
index be71cc8f714..085b074ee18 100644
--- a/dll/win32/shell32/folders/CControlPanelFolder.cpp
+++ b/dll/win32/shell32/folders/CControlPanelFolder.cpp
@@ -773,3 +773,144 @@ HRESULT WINAPI CCPLItemMenu::HandleMenuMsg(
return E_NOTIMPL;
}
+
+/**************************************************************************
+* COpenControlPanel
+*/
+
+static HRESULT GetParsingName(PCIDLIST_ABSOLUTE pidl, PWSTR*Name)
+{
+ PIDLIST_ABSOLUTE pidlFree = NULL;
+ if (IS_INTRESOURCE(pidl))
+ {
+ HRESULT hr = SHGetSpecialFolderLocation(NULL, (UINT)(SIZE_T)pidl,
&pidlFree);
+ if (FAILED(hr))
+ return hr;
+ pidl = pidlFree;
+ }
+ HRESULT hr = SHGetNameFromIDList(pidl, SIGDN_DESKTOPABSOLUTEPARSING, Name);
+ ILFree(pidlFree);
+ return hr;
+}
+
+static HRESULT CreateCplAbsoluteParsingPath(LPCWSTR Prefix, LPCWSTR InFolderParse, PWSTR
Buf, UINT cchBuf)
+{
+ PWSTR cpfolder;
+ HRESULT hr = GetParsingName((PCIDLIST_ABSOLUTE)CSIDL_CONTROLS, &cpfolder);
+ if (SUCCEEDED(hr))
+ {
+ hr = StringCchPrintfW(Buf, cchBuf, L"%s\\%s%s", cpfolder, Prefix,
InFolderParse);
+ SHFree(cpfolder);
+ }
+ return hr;
+}
+
+static HRESULT FindExeCplClass(LPCWSTR Canonical, HKEY hKey, BOOL Wow64, LPWSTR clsid)
+{
+ HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+ HKEY hNSKey;
+ WCHAR key[MAX_PATH], buf[MAX_PATH];
+ wsprintfW(key,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\%s\\NameSpace",
+ Wow64 ? L"ControlPanelWOW64" : L"ControlPanel");
+ LSTATUS error = RegOpenKeyExW(hKey, key, 0, KEY_READ, &hNSKey);
+ if (error)
+ return HRESULT_FROM_WIN32(error);
+ for (DWORD i = 0; RegEnumKeyW(hNSKey, i, key, _countof(key)) == ERROR_SUCCESS; ++i)
+ {
+ IID validate;
+ if (SUCCEEDED(IIDFromString(key, &validate)))
+ {
+ wsprintfW(buf, L"CLSID\\%s", key);
+ DWORD cb = sizeof(buf);
+ if (RegGetValueW(HKEY_CLASSES_ROOT, buf,
L"System.ApplicationName",
+ RRF_RT_REG_SZ, NULL, buf, &cb) == ERROR_SUCCESS)
+ {
+ if (!lstrcmpiW(buf, Canonical))
+ {
+ lstrcpyW(clsid, key);
+ hr = S_OK;
+ }
+ }
+ }
+ }
+ RegCloseKey(hNSKey);
+ return hr;
+}
+
+static HRESULT FindExeCplClass(LPCWSTR Canonical, LPWSTR clsid)
+{
+ HRESULT hr = E_FAIL;
+ if (FAILED(hr))
+ hr = FindExeCplClass(Canonical, HKEY_CURRENT_USER, FALSE, clsid);
+ if (FAILED(hr))
+ hr = FindExeCplClass(Canonical, HKEY_CURRENT_USER, TRUE, clsid);
+ if (FAILED(hr))
+ hr = FindExeCplClass(Canonical, HKEY_LOCAL_MACHINE, FALSE, clsid);
+ if (FAILED(hr))
+ hr = FindExeCplClass(Canonical, HKEY_LOCAL_MACHINE, TRUE, clsid);
+ return hr;
+}
+
+HRESULT WINAPI COpenControlPanel::Open(LPCWSTR pszName, LPCWSTR pszPage, IUnknown
*punkSite)
+{
+ WCHAR path[MAX_PATH], clspath[MAX_PATH];
+ HRESULT hr = S_OK;
+ SHELLEXECUTEINFOW sei = { sizeof(sei), SEE_MASK_FLAG_DDEWAIT };
+ sei.lpFile = path;
+ sei.nShow = SW_SHOW;
+ if (!pszName)
+ {
+ GetSystemDirectoryW(path, _countof(path));
+ PathAppendW(path, L"control.exe");
+ }
+ else
+ {
+ LPWSTR clsid = clspath + wsprintfW(clspath, L"CLSID\\");
+ if (SUCCEEDED(hr = FindExeCplClass(pszName, clsid)))
+ {
+ if (SUCCEEDED(hr = CreateCplAbsoluteParsingPath(L"::", clsid, path,
_countof(path))))
+ {
+ // NT6 will execute
"::{26EE0668-A00A-44D7-9371-BEB064C98683}\0\::{clsid}[\pszPage]"
+ // but we don't support parsing that so we force the class instead.
+ sei.fMask |= SEE_MASK_CLASSNAME;
+ sei.lpClass = clspath;
+ }
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ DWORD error = ShellExecuteExW(&sei) ? ERROR_SUCCESS : GetLastError();
+ hr = HRESULT_FROM_WIN32(error);
+ }
+ return hr;
+}
+
+HRESULT WINAPI COpenControlPanel::GetPath(LPCWSTR pszName, LPWSTR pszPath, UINT
cchPath)
+{
+ HRESULT hr;
+ if (!pszName)
+ {
+ PWSTR cpfolder;
+ if (SUCCEEDED(hr = GetParsingName((PCIDLIST_ABSOLUTE)CSIDL_CONTROLS,
&cpfolder)))
+ {
+ hr = StringCchCopyW(pszPath, cchPath, cpfolder);
+ SHFree(cpfolder);
+ }
+ }
+ else
+ {
+ WCHAR clsid[38 + 1];
+ if (SUCCEEDED(hr = FindExeCplClass(pszName, clsid)))
+ {
+ hr = CreateCplAbsoluteParsingPath(L"::", clsid, pszPath,
cchPath);
+ }
+ }
+ return hr;
+}
+
+HRESULT WINAPI COpenControlPanel::GetCurrentView(CPVIEW *pView)
+{
+ *pView = CPVIEW_CLASSIC;
+ return S_OK;
+}
diff --git a/dll/win32/shell32/folders/CControlPanelFolder.h
b/dll/win32/shell32/folders/CControlPanelFolder.h
index 1fa7969aff4..deb29a5f2e1 100644
--- a/dll/win32/shell32/folders/CControlPanelFolder.h
+++ b/dll/win32/shell32/folders/CControlPanelFolder.h
@@ -111,4 +111,25 @@ public:
END_COM_MAP()
};
+class COpenControlPanel :
+ public CComCoClass<COpenControlPanel, &CLSID_OpenControlPanel>,
+ public CComObjectRootEx<CComMultiThreadModelNoCS>,
+ public IOpenControlPanel
+{
+ public:
+ // IOpenControlPanel
+ virtual HRESULT WINAPI Open(LPCWSTR pszName, LPCWSTR pszPage, IUnknown
*punkSite);
+ virtual HRESULT WINAPI GetPath(LPCWSTR pszName, LPWSTR pszPath, UINT cchPath);
+ virtual HRESULT WINAPI GetCurrentView(CPVIEW *pView);
+
+ static HRESULT WINAPI UpdateRegistry(BOOL bRegister) { return S_OK; } //
CControlPanelFolder does it for us
+ DECLARE_NOT_AGGREGATABLE(COpenControlPanel)
+
+ DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+ BEGIN_COM_MAP(COpenControlPanel)
+ COM_INTERFACE_ENTRY_IID(IID_IOpenControlPanel, IOpenControlPanel)
+ END_COM_MAP()
+};
+
#endif /* _SHFLDR_CPANEL_H_ */
diff --git a/dll/win32/shell32/res/rgs/controlpanel.rgs
b/dll/win32/shell32/res/rgs/controlpanel.rgs
index 79a1794d573..d53211d1799 100644
--- a/dll/win32/shell32/res/rgs/controlpanel.rgs
+++ b/dll/win32/shell32/res/rgs/controlpanel.rgs
@@ -51,3 +51,25 @@ HKLM
}
}
}
+
+HKCR
+{
+ NoRemove CLSID
+ {
+ ForceRemove {06622D85-6856-4460-8DE1-A81921B41C4B} = s 'COpenControlPanel'
+ {
+ val AppID = s '{06622D85-6856-4460-8DE1-A81921B41C4B}'
+ InprocServer32 = s '%MODULE%'
+ {
+ val ThreadingModel = s 'Apartment'
+ }
+ }
+ }
+ NoRemove AppID
+ {
+ ForceRemove {06622D85-6856-4460-8DE1-A81921B41C4B} = s 'COpenControlPanel'
+ {
+ val DllSurrogate = s ''
+ }
+ }
+}
diff --git a/dll/win32/shell32/shell32.cpp b/dll/win32/shell32/shell32.cpp
index 011d5b2980b..91e5ed22c84 100644
--- a/dll/win32/shell32/shell32.cpp
+++ b/dll/win32/shell32/shell32.cpp
@@ -280,6 +280,7 @@ BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_Shell, CShellDispatch)
OBJECT_ENTRY(CLSID_DragDropHelper, CDropTargetHelper)
OBJECT_ENTRY(CLSID_ControlPanel, CControlPanelFolder)
+ OBJECT_ENTRY(CLSID_OpenControlPanel, COpenControlPanel)
OBJECT_ENTRY(CLSID_MyDocuments, CMyDocsFolder)
OBJECT_ENTRY(CLSID_NetworkPlaces, CNetFolder)
OBJECT_ENTRY(CLSID_FontsFolderShortcut, CFontsFolder)
diff --git a/sdk/include/psdk/shlguid.h b/sdk/include/psdk/shlguid.h
index 132f2963fdc..adada9427cd 100644
--- a/sdk/include/psdk/shlguid.h
+++ b/sdk/include/psdk/shlguid.h
@@ -231,6 +231,7 @@ DEFINE_GUID(CLSID_NewMenu, 0xd969A300, 0xe7FF, 0x11D0,
0xA9, 0x3B,
DEFINE_GUID(IID_IShellFolderViewCB, 0x2047E320, 0xF2A9, 0x11CE, 0xAE, 0x65, 0x8,
0x00, 0x2B, 0x2E, 0x12, 0x62);
DEFINE_GUID(CLSID_InternetButtons, 0x1E796980, 0x9CC5, 0x11D1, 0xA8, 0x3F, 0x0,
0xC0, 0x4F, 0xC9, 0x9D, 0x61);
DEFINE_GUID(CLSID_MenuDeskBar, 0xECD4FC4F, 0x521C, 0x11D0, 0xB7, 0x92, 0x00,
0xA0, 0xC9, 0x03, 0x12, 0xE1);
+DEFINE_GUID(CLSID_OpenControlPanel, 0x06622D85, 0x6856, 0x4460, 0x8D, 0xE1, 0xA8,
0x19, 0x21, 0xB4, 0x1C, 0x4B);
DEFINE_GUID(SID_SMenuBandChild, 0xed9cc020, 0x08b9, 0x11d1, 0x98, 0x23, 0x0,
0xc0, 0x4f, 0xd9, 0x19, 0x72);
DEFINE_GUID(SID_SMenuBandParent, 0x8c278eec, 0x3eab, 0x11d1, 0x8c, 0xb0, 0x0,
0xc0, 0x4f, 0xd9, 0x18, 0xd0);
diff --git a/sdk/include/psdk/shobjidl.idl b/sdk/include/psdk/shobjidl.idl
index ef9e51d16e1..7a75dff0f09 100644
--- a/sdk/include/psdk/shobjidl.idl
+++ b/sdk/include/psdk/shobjidl.idl
@@ -2360,6 +2360,37 @@ interface IExplorerCommandProvider : IUnknown
[out, iid_is(riid)] void **ppv);
};
+
+/*****************************************************************************
+ * IOpenControlPanel interface (Vista+)
+ */
+[
+ object,
+ uuid(D11AD862-66DE-4DF4-BF6C-1F5621996AF1),
+ pointer_default(unique)
+]
+interface IOpenControlPanel : IUnknown
+{
+ typedef [v1_enum] enum CPVIEW
+ {
+ CPVIEW_CLASSIC = 0,
+ CPVIEW_CATEGORY = 1,
+ CPVIEW_ALLITEMS = CPVIEW_CLASSIC,
+ CPVIEW_HOME = CPVIEW_CATEGORY
+ } CPVIEW;
+
+ HRESULT Open(
+ [in, optional] LPCWSTR pszName,
+ [in, optional] LPCWSTR pszPage,
+ [in, optional] IUnknown *punkSite);
+ HRESULT GetPath(
+ [in, optional] LPCWSTR pszName,
+ [out, string, size_is(cchPath)] LPWSTR pszPath,
+ [in] UINT cchPath);
+ HRESULT GetCurrentView(
+ [out] CPVIEW *pView);
+}
+
#endif // __REACTOS__
/*****************************************************************************