https://git.reactos.org/?p=reactos.git;a=commitdiff;h=d41dec2e07f919235406ca3a4d47e8b6ae1aa588

commit d41dec2e07f919235406ca3a4d47e8b6ae1aa588
Author:     Whindmar Saksit <whinds...@proton.me>
AuthorDate: Wed Jan 17 17:07:21 2024 +0100
Commit:     GitHub <nore...@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-register-an-executable-control-panel-item-registration-)"
 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-register-dll-control-panel-item-registration-#step-3)
 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.fil...@gkware.com)
  *              Copyright 2008 Colin Finck (co...@reactos.org)
- *              Copyright 2014 Herm�s B�lusca-Ma�to 
(hermes.belusca-ma...@reactos.org)
+ *              Copyright 2014 Hermès Bélusca-Maïto 
(hermes.belusca-ma...@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-panel-items#windows-vista-canonical-names
 */
+    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__
 
 /*****************************************************************************

Reply via email to