Skip to content

Commit 1f57c78

Browse files
committed
Modern settings shell folder
Adds virtual shell folder that contains items representing modern settings parsed from `%windir%\ImmersiveControlPanel\Settings\AllSystemSettings_{253E530E-387D-4BC2-959D-E6F86122E5F2}.xml`. It can be accessed via `shell:::{82E749ED-B971-4550-BAF7-06AA2BF7E836}` (in explorer). Item in folder will open given setting page in `Settings` application.
1 parent 1f6e06f commit 1f57c78

13 files changed

+1589
-1
lines changed

Src/StartMenu/StartMenuHelper/ModernSettings.cpp

Lines changed: 434 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Modern settings helper
2+
3+
#pragma once
4+
5+
#include <functional>
6+
#include <map>
7+
#include <string>
8+
#include <vector>
9+
10+
struct Blob
11+
{
12+
const void* data = nullptr;
13+
size_t size = 0;
14+
};
15+
16+
class File
17+
{
18+
public:
19+
File(const WCHAR* fileName, DWORD desiredAccess, DWORD shareMode, DWORD creationDisposition = OPEN_EXISTING, DWORD flagsAndAttributes = FILE_ATTRIBUTE_NORMAL)
20+
{
21+
m_handle = ::CreateFile(fileName, desiredAccess, shareMode, nullptr, creationDisposition, flagsAndAttributes, nullptr);
22+
}
23+
24+
~File()
25+
{
26+
if (m_handle != INVALID_HANDLE_VALUE)
27+
::CloseHandle(m_handle);
28+
}
29+
30+
File(const File&) = delete;
31+
File& operator=(const File&) = delete;
32+
33+
explicit operator bool() const
34+
{
35+
return (m_handle != INVALID_HANDLE_VALUE);
36+
}
37+
38+
operator HANDLE() const
39+
{
40+
return m_handle;
41+
}
42+
43+
uint64_t size() const
44+
{
45+
LARGE_INTEGER li = {};
46+
return ::GetFileSizeEx(m_handle, &li) ? li.QuadPart : (uint64_t)-1;
47+
}
48+
49+
private:
50+
HANDLE m_handle;
51+
};
52+
53+
class MappedFile
54+
{
55+
public:
56+
MappedFile(const WCHAR* fileName) : m_file(fileName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_DELETE)
57+
{
58+
if (m_file)
59+
{
60+
auto mapping = ::CreateFileMapping(m_file, nullptr, PAGE_READONLY, 0, 0, nullptr);
61+
if (mapping)
62+
{
63+
m_view.data = ::MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
64+
if (m_view.data)
65+
m_view.size = (size_t)m_file.size();
66+
67+
::CloseHandle(mapping);
68+
}
69+
}
70+
}
71+
72+
~MappedFile()
73+
{
74+
if (m_view.data)
75+
::UnmapViewOfFile(m_view.data);
76+
}
77+
78+
MappedFile(const MappedFile&) = delete;
79+
MappedFile& operator=(const MappedFile&) = delete;
80+
81+
explicit operator bool() const
82+
{
83+
return (m_view.data != nullptr);
84+
}
85+
86+
Blob get() const
87+
{
88+
return m_view;
89+
}
90+
91+
private:
92+
File m_file;
93+
Blob m_view;
94+
};
95+
96+
class ModernSettings
97+
{
98+
public:
99+
ModernSettings(const wchar_t* fname);
100+
101+
size_t size() const
102+
{
103+
return m_settings.size();
104+
}
105+
106+
struct Setting
107+
{
108+
std::wstring_view fileName;
109+
110+
std::wstring_view deepLink;
111+
std::wstring_view icon;
112+
std::wstring_view glyph;
113+
114+
std::wstring_view pageId;
115+
std::wstring_view hostId;
116+
std::wstring_view groupId;
117+
std::wstring_view settingId;
118+
std::wstring_view description;
119+
std::wstring_view keywords;
120+
121+
Setting() = default;
122+
Setting(const Blob& blob);
123+
124+
explicit operator bool() const
125+
{
126+
return !fileName.empty();
127+
}
128+
};
129+
130+
std::vector<Setting> enumerate() const;
131+
Setting get(const std::wstring_view& name) const;
132+
133+
private:
134+
MappedFile m_storage;
135+
std::map<std::wstring_view, Blob> m_settings;
136+
};
137+
138+
// retrieve actual instance of ModernSettings
139+
std::shared_ptr<ModernSettings> GetModernSettings();
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// Context menu handler for Open-Shell Modern Settings shell folder
2+
3+
// Based on Explorer Data Provider Sample (https://docs.microsoft.com/en-us/windows/win32/shell/samples-explorerdataprovider)
4+
5+
#include "stdafx.h"
6+
#include "ModernSettings.h"
7+
#include "ModernSettingsContextMenu.h"
8+
9+
#define MENUVERB_OPEN 0
10+
11+
struct ICIVERBTOIDMAP
12+
{
13+
LPCWSTR pszCmd; // verbW
14+
LPCSTR pszCmdA; // verbA
15+
UINT idCmd; // hmenu id
16+
};
17+
18+
static const ICIVERBTOIDMAP g_ContextMenuIDMap[] =
19+
{
20+
{ L"open", "open", MENUVERB_OPEN },
21+
{ NULL, NULL, (UINT)-1 }
22+
};
23+
24+
HRESULT _MapICIVerbToCmdID(LPCMINVOKECOMMANDINFO pici, UINT* pid)
25+
{
26+
if (IS_INTRESOURCE(pici->lpVerb))
27+
{
28+
*pid = LOWORD((UINT_PTR)pici->lpVerb);
29+
return S_OK;
30+
}
31+
32+
if (pici->fMask & CMIC_MASK_UNICODE)
33+
{
34+
for (const auto& i : g_ContextMenuIDMap)
35+
{
36+
if (StrCmpIC(((LPCMINVOKECOMMANDINFOEX)pici)->lpVerbW, i.pszCmd) == 0)
37+
{
38+
*pid = i.idCmd;
39+
return S_OK;
40+
}
41+
}
42+
}
43+
else
44+
{
45+
for (const auto& i : g_ContextMenuIDMap)
46+
{
47+
if (StrCmpICA(pici->lpVerb, i.pszCmdA) == 0)
48+
{
49+
*pid = i.idCmd;
50+
return S_OK;
51+
}
52+
}
53+
}
54+
55+
return E_FAIL;
56+
}
57+
58+
static bool ActivateModernSettingPage(const WCHAR* page)
59+
{
60+
CComPtr<IApplicationActivationManager> mgr;
61+
mgr.CoCreateInstance(CLSID_ApplicationActivationManager);
62+
if (mgr)
63+
{
64+
DWORD pid = 0;
65+
return SUCCEEDED(mgr->ActivateApplication(L"windows.immersivecontrolpanel_cw5n1h2txyewy!microsoft.windows.immersivecontrolpanel", page, AO_NONE, &pid));
66+
}
67+
68+
return false;
69+
}
70+
71+
extern ModernSettings::Setting GetModernSetting(LPCITEMIDLIST pidl);
72+
73+
static HRESULT OpenItemByPidl(LPCITEMIDLIST pidl)
74+
{
75+
auto child = ILFindLastID(pidl);
76+
auto setting = GetModernSetting(child);
77+
78+
if (!setting)
79+
return E_INVALIDARG;
80+
81+
if (setting.hostId == L"{6E6DDBCB-9C89-434B-A994-D5F22239523B}")
82+
{
83+
std::wstring cmd(L"windowsdefender://");
84+
cmd += setting.deepLink;
85+
86+
return (intptr_t)::ShellExecute(nullptr, L"open", cmd.c_str(), nullptr, nullptr, SW_SHOWNORMAL) > 32 ? S_OK : E_FAIL;
87+
}
88+
89+
if (setting.pageId.empty())
90+
return E_INVALIDARG;
91+
92+
std::wstring page;
93+
94+
page += L"page=";
95+
page += setting.pageId;
96+
97+
if (!setting.settingId.empty())
98+
{
99+
page += L"&target=";
100+
page += setting.settingId;
101+
}
102+
else if (!setting.groupId.empty())
103+
{
104+
page += L"&group=";
105+
page += setting.groupId;
106+
}
107+
108+
page += L"&ActivationType=Search";
109+
110+
ActivateModernSettingPage(page.c_str());
111+
112+
return S_OK;
113+
}
114+
115+
116+
// CModernSettingsContextMenu
117+
118+
HRESULT CModernSettingsContextMenu::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT /* idCmdLast */, UINT /* uFlags */)
119+
{
120+
InsertMenu(hmenu, indexMenu++, MF_BYPOSITION, idCmdFirst + MENUVERB_OPEN, L"Open");
121+
// other verbs could go here...
122+
123+
// indicate that we added one verb.
124+
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (USHORT)(1));
125+
}
126+
127+
HRESULT CModernSettingsContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
128+
{
129+
HRESULT hr = E_INVALIDARG;
130+
UINT uID;
131+
// Is this command for us?
132+
if (SUCCEEDED(_MapICIVerbToCmdID(pici, &uID)))
133+
{
134+
if (uID == MENUVERB_OPEN && m_pdtobj)
135+
{
136+
LPITEMIDLIST pidl;
137+
hr = SHGetIDListFromObject(m_pdtobj, &pidl);
138+
if (SUCCEEDED(hr))
139+
{
140+
hr = OpenItemByPidl(pidl);
141+
ILFree(pidl);
142+
}
143+
}
144+
}
145+
146+
return hr;
147+
}
148+
149+
HRESULT CModernSettingsContextMenu::GetCommandString(UINT_PTR /* idCmd */, UINT /* uType */, UINT* /* pRes */, LPSTR /* pszName */, UINT /* cchMax */)
150+
{
151+
return E_NOTIMPL;
152+
}
153+
154+
HRESULT CModernSettingsContextMenu::Initialize(PCIDLIST_ABSOLUTE /* pidlFolder */, IDataObject* pdtobj, HKEY /* hkeyProgID */)
155+
{
156+
m_pdtobj = pdtobj;
157+
return S_OK;
158+
}
159+
160+
HRESULT CModernSettingsContextMenu::SetSite(IUnknown* punkSite)
161+
{
162+
m_punkSite = punkSite;
163+
return S_OK;
164+
}
165+
166+
HRESULT CModernSettingsContextMenu::GetSite(REFIID riid, void** ppvSite)
167+
{
168+
return m_punkSite ? m_punkSite->QueryInterface(riid, ppvSite) : E_FAIL;
169+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Context menu handler for Open-Shell Modern Settings shell folder
2+
3+
#pragma once
4+
#include "resource.h"
5+
#include "StartMenuHelper_i.h"
6+
#include <shlobj.h>
7+
8+
// CModernSettingsContextMenu
9+
10+
class ATL_NO_VTABLE CModernSettingsContextMenu :
11+
public CComObjectRootEx<CComSingleThreadModel>,
12+
public CComCoClass<CModernSettingsContextMenu, &CLSID_ModernSettingsContextMenu>,
13+
public IContextMenu,
14+
public IShellExtInit,
15+
public IObjectWithSite
16+
{
17+
public:
18+
CModernSettingsContextMenu()
19+
{
20+
}
21+
22+
DECLARE_REGISTRY_RESOURCEID(IDR_MODERNSETTINGSCONTEXTMENU)
23+
24+
DECLARE_NOT_AGGREGATABLE(CModernSettingsContextMenu)
25+
26+
BEGIN_COM_MAP(CModernSettingsContextMenu)
27+
COM_INTERFACE_ENTRY(IContextMenu)
28+
COM_INTERFACE_ENTRY(IShellExtInit)
29+
COM_INTERFACE_ENTRY(IObjectWithSite)
30+
END_COM_MAP()
31+
32+
DECLARE_PROTECT_FINAL_CONSTRUCT()
33+
34+
HRESULT FinalConstruct()
35+
{
36+
return S_OK;
37+
}
38+
39+
void FinalRelease()
40+
{
41+
}
42+
43+
// IContextMenu
44+
IFACEMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
45+
IFACEMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO lpici);
46+
IFACEMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uType, UINT* pRes, LPSTR pszName, UINT cchMax);
47+
48+
// IShellExtInit
49+
IFACEMETHODIMP Initialize(PCIDLIST_ABSOLUTE pidlFolder, IDataObject* pdtobj, HKEY hkeyProgID);
50+
51+
// IObjectWithSite
52+
IFACEMETHODIMP SetSite(IUnknown* punkSite);
53+
IFACEMETHODIMP GetSite(REFIID riid, void** ppvSite);
54+
55+
private:
56+
CComPtr<IDataObject> m_pdtobj;
57+
CComPtr<IUnknown> m_punkSite;
58+
};
59+
60+
OBJECT_ENTRY_AUTO(__uuidof(ModernSettingsContextMenu), CModernSettingsContextMenu)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
HKCR
2+
{
3+
NoRemove CLSID
4+
{
5+
ForceRemove {5ab14324-c087-42c1-b905-a0bfdb4e9532} = s 'Open-Shell Modern Settings Context Menu'
6+
{
7+
InprocServer32 = s '%MODULE%'
8+
{
9+
val ThreadingModel = s 'Apartment'
10+
}
11+
ShellEx
12+
{
13+
MayChangeDefaultMenu = s ''
14+
{
15+
}
16+
}
17+
}
18+
}
19+
}

0 commit comments

Comments
 (0)