mirror of
https://github.com/reactos/reactos.git
synced 2024-09-27 21:16:34 +00:00
[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.
This commit is contained in:
parent
fb43301bad
commit
d41dec2e07
|
@ -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)
|
||||
|
|
|
@ -4,12 +4,14 @@
|
|||
* PURPOSE: ReactOS System Control Panel
|
||||
* COPYRIGHT: Copyright 2004 Gero Kuehn (reactos.filter@gkware.com)
|
||||
* Copyright 2008 Colin Finck (colin@reactos.org)
|
||||
* Copyright 2014 Hermès Bélusca-Maïto (hermes.belusca-maito@reactos.org)
|
||||
* Copyright 2014 Hermès Bélusca-Maïto (hermes.belusca-maito@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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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 ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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__
|
||||
|
||||
/*****************************************************************************
|
||||
|
|
Loading…
Reference in a new issue