2 * PROJECT: ReactOS Compatibility Layer Shell Extension
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: CLayerUIPropPage implementation
5 * COPYRIGHT: Copyright 2015-2019 Mark Jansen (mark.jansen@reactos.org)
12 #include <shellutils.h>
18 const GUID CLSID_CLayerUIPropPage
= { 0x513D916F, 0x2A8E, 0x4F51, { 0xAE, 0xAB, 0x0C, 0xBC, 0x76, 0xFB, 0x1A, 0xF8 } };
21 #define GPLK_MACHINE 2
22 #define MAX_LAYER_LENGTH 256
28 { L
"Windows 95", L
"WIN95" },
29 { L
"Windows 98/ME", L
"WIN98" },
30 { L
"Windows NT 4.0 (SP5)", L
"NT4SP5" },
31 { L
"Windows 2000", L
"WIN2000" },
32 { L
"Windows XP (SP3)", L
"WINXPSP3" },
33 { L
"Windows Server 2003 (SP1)", L
"WINSRV03SP1" },
34 { L
"Windows Server 2008 (SP1)", L
"WINSRV08SP1" },
35 { L
"Windows Vista (SP2)", L
"VISTASP2" },
36 { L
"Windows 7", L
"WIN7RTM" },
37 { L
"Windows 7 (SP1)", L
"WIN7SP1" },
38 { L
"Windows 8.1", L
"WIN81RTM" },
39 { L
"Windows 10", L
"WIN10RTM" },
40 { L
"Windows Server 2016", L
"WINSRV16RTM" },
41 { L
"Windows Server 2019", L
"WINSRV19RTM" },
49 { L
"256COLOR", IDC_CHKRUNIN256COLORS
},
50 { L
"640X480", IDC_CHKRUNIN640480RES
},
51 { L
"DISABLETHEMES", IDC_CHKDISABLEVISUALTHEMES
},
53 { L
"DISABLEDWM", IDC_
??, TRUE
},
54 { L
"HIGHDPIAWARE", IDC_
??, TRUE
},
55 { L
"RUNASADMIN", IDC_
??, TRUE
},
60 static const WCHAR
* g_AllowedExtensions
[] = {
69 BOOL
IsBuiltinLayer(PCWSTR Name
)
73 for (n
= 0; g_Layers
[n
].Name
; ++n
)
75 if (!wcsicmp(g_Layers
[n
].Name
, Name
))
81 for (n
= 0; g_CompatModes
[n
].Name
; ++n
)
83 if (!wcsicmp(g_CompatModes
[n
].Name
, Name
))
92 void ACDBG_FN(PCSTR FunctionName
, PCWSTR Format
, ...)
95 WCHAR
* Current
= Buffer
;
96 size_t Length
= _countof(Buffer
);
98 StringCchPrintfExW(Current
, Length
, &Current
, &Length
, STRSAFE_NULL_ON_FAILURE
, L
"[%-20S] ", FunctionName
);
100 va_start(ArgList
, Format
);
101 StringCchVPrintfExW(Current
, Length
, &Current
, &Length
, STRSAFE_NULL_ON_FAILURE
, Format
, ArgList
);
103 OutputDebugStringW(Buffer
);
106 #define ACDBG(fmt, ...) ACDBG_FN(__FUNCTION__, fmt, ##__VA_ARGS__ )
110 CLayerUIPropPage::CLayerUIPropPage()
111 : m_IsSfcProtected(FALSE
)
112 , m_AllowPermLayer(FALSE
)
113 , m_LayerQueryFlags(GPLK_USER
) /* TODO: When do we read from HKLM? */
114 , m_RegistryOSMode(0)
116 , m_RegistryEnabledLayers(0)
120 title
.LoadString(g_hModule
, IDS_COMPAT_TITLE
);
121 m_psp
.pszTitle
= title
.Detach();
122 m_psp
.dwFlags
|= PSP_USETITLE
;
125 CLayerUIPropPage::~CLayerUIPropPage()
128 title
.Attach((BSTR
)m_psp
.pszTitle
);
131 HRESULT
CLayerUIPropPage::InitFile(PCWSTR Filename
)
133 CString ExpandedFilename
;
134 DWORD dwRequired
= ExpandEnvironmentStringsW(Filename
, NULL
, 0);
137 LPWSTR Buffer
= ExpandedFilename
.GetBuffer(dwRequired
);
138 DWORD dwReturned
= ExpandEnvironmentStringsW(Filename
, Buffer
, dwRequired
);
139 if (dwRequired
== dwReturned
)
141 ExpandedFilename
.ReleaseBufferSetLength(dwReturned
- 1);
142 ACDBG(L
"Expanded '%s' => '%s'\r\n", Filename
, (PCWSTR
)ExpandedFilename
);
146 ExpandedFilename
.ReleaseBufferSetLength(0);
147 ExpandedFilename
= Filename
;
148 ACDBG(L
"Failed during expansion '%s'\r\n", Filename
);
153 ACDBG(L
"Failed to expand '%s'\r\n", Filename
);
154 ExpandedFilename
= Filename
;
156 PCWSTR pwszExt
= PathFindExtensionW(ExpandedFilename
);
159 ACDBG(L
"Failed to find an extension: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
162 if (!wcsicmp(pwszExt
, L
".lnk"))
164 WCHAR Buffer
[MAX_PATH
];
165 if (!GetExeFromLnk(ExpandedFilename
, Buffer
, _countof(Buffer
)))
167 ACDBG(L
"Failed to read link target from: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
170 if (!wcsicmp(Buffer
, ExpandedFilename
))
172 ACDBG(L
"Link redirects to itself: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
175 return InitFile(Buffer
);
179 if (tmp
.GetEnvironmentVariable(L
"SystemRoot"))
181 tmp
+= L
"\\System32";
182 if (ExpandedFilename
.GetLength() >= tmp
.GetLength() &&
183 ExpandedFilename
.Left(tmp
.GetLength()).MakeLower() == tmp
.MakeLower())
185 ACDBG(L
"Ignoring System32: %s\r\n", (PCWSTR
)ExpandedFilename
);
188 tmp
.GetEnvironmentVariable(L
"SystemRoot");
190 if (ExpandedFilename
.GetLength() >= tmp
.GetLength() &&
191 ExpandedFilename
.Left(tmp
.GetLength()).MakeLower() == tmp
.MakeLower())
193 ACDBG(L
"Ignoring WinSxs: %s\r\n", (PCWSTR
)ExpandedFilename
);
198 for (size_t n
= 0; g_AllowedExtensions
[n
]; ++n
)
200 if (!wcsicmp(g_AllowedExtensions
[n
], pwszExt
))
202 m_Filename
= ExpandedFilename
;
203 ACDBG(L
"Got: %s\r\n", (PCWSTR
)ExpandedFilename
);
204 m_IsSfcProtected
= SfcIsFileProtected(NULL
, m_Filename
);
205 m_AllowPermLayer
= AllowPermLayer(ExpandedFilename
);
209 ACDBG(L
"Extension not included: '%s'\r\n", pwszExt
);
213 static BOOL
GetLayerInfo(PCWSTR Filename
, DWORD QueryFlags
, PDWORD OSMode
, PDWORD Enabledlayers
, CSimpleArray
<CString
>& customLayers
)
215 WCHAR wszLayers
[MAX_LAYER_LENGTH
] = { 0 };
216 DWORD dwBytes
= sizeof(wszLayers
);
218 *OSMode
= *Enabledlayers
= 0;
219 customLayers
.RemoveAll();
220 if (!SdbGetPermLayerKeys(Filename
, wszLayers
, &dwBytes
, QueryFlags
))
223 for (PWCHAR Layer
= wcstok(wszLayers
, L
" "); Layer
; Layer
= wcstok(NULL
, L
" "))
226 for (n
= 0; g_Layers
[n
].Name
; ++n
)
228 if (!wcsicmp(g_Layers
[n
].Name
, Layer
))
230 *Enabledlayers
|= (1<<n
);
234 /* Did we find it? */
235 if (g_Layers
[n
].Name
)
238 for (n
= 0; g_CompatModes
[n
].Name
; ++n
)
240 if (!wcsicmp(g_CompatModes
[n
].Name
, Layer
))
246 /* Did we find it? */
247 if (g_CompatModes
[n
].Name
)
250 /* Must be a 'custom' layer */
251 customLayers
.Add(Layer
);
256 int CLayerUIPropPage::OnSetActive()
258 if (!GetLayerInfo(m_Filename
, m_LayerQueryFlags
, &m_RegistryOSMode
, &m_RegistryEnabledLayers
, m_RegistryCustomLayers
))
259 m_RegistryOSMode
= m_RegistryEnabledLayers
= 0;
261 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
262 CheckDlgButton(g_Layers
[n
].Id
, (m_RegistryEnabledLayers
& (1<<n
)) ? BST_CHECKED
: BST_UNCHECKED
);
264 CheckDlgButton(IDC_CHKRUNCOMPATIBILITY
, m_RegistryOSMode
? BST_CHECKED
: BST_UNCHECKED
);
266 if (m_RegistryOSMode
)
267 ComboBox_SetCurSel(GetDlgItem(IDC_COMPATIBILITYMODE
), m_RegistryOSMode
-1);
269 m_CustomLayers
= m_RegistryCustomLayers
;
277 static BOOL
ArrayEquals(const CSimpleArray
<CString
>& lhs
, const CSimpleArray
<CString
>& rhs
)
279 if (lhs
.GetSize() != rhs
.GetSize())
282 for (int n
= 0; n
< lhs
.GetSize(); ++n
)
284 if (lhs
[n
] != rhs
[n
])
290 BOOL
CLayerUIPropPage::HasChanges() const
292 if (m_RegistryEnabledLayers
!= m_EnabledLayers
)
295 if (m_RegistryOSMode
!= m_OSMode
)
298 if (!ArrayEquals(m_RegistryCustomLayers
, m_CustomLayers
))
304 int CLayerUIPropPage::OnApply()
308 BOOL bMachine
= m_LayerQueryFlags
== GPLK_MACHINE
;
310 for (size_t n
= 0; g_CompatModes
[n
].Name
; ++n
)
311 SetPermLayerState(m_Filename
, g_CompatModes
[n
].Name
, 0, bMachine
, (n
+1) == m_OSMode
);
313 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
315 SetPermLayerState(m_Filename
, g_Layers
[n
].Name
, 0, bMachine
, ((1<<n
) & m_EnabledLayers
) != 0);
318 /* Disable all old values */
319 for (int j
= 0; j
< m_RegistryCustomLayers
.GetSize(); j
++)
321 SetPermLayerState(m_Filename
, m_RegistryCustomLayers
[j
].GetString(), 0, bMachine
, FALSE
);
324 /* Enable all new values */
325 for (int j
= 0; j
< m_CustomLayers
.GetSize(); j
++)
327 SetPermLayerState(m_Filename
, m_CustomLayers
[j
].GetString(), 0, bMachine
, TRUE
);
330 SHChangeNotify(SHCNE_UPDATEITEM
, SHCNF_PATHW
, (PCWSTR
)m_Filename
, NULL
);
333 return PSNRET_NOERROR
;
336 LRESULT
CLayerUIPropPage::OnInitDialog(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
338 HWND cboMode
= GetDlgItem(IDC_COMPATIBILITYMODE
);
339 for (size_t n
= 0; g_CompatModes
[n
].Display
; ++n
)
340 ComboBox_AddString(cboMode
, g_CompatModes
[n
].Display
);
341 ComboBox_SetCurSel(cboMode
, 4);
343 CStringW explanation
;
344 if (!m_AllowPermLayer
)
346 explanation
.LoadString(g_hModule
, IDS_FAILED_NETWORK
);
348 ACDBG(L
"AllowPermLayer returned FALSE\r\n");
350 else if (m_IsSfcProtected
)
352 explanation
.LoadString(g_hModule
, IDS_FAILED_PROTECTED
);
354 ACDBG(L
"Protected OS file\r\n");
360 SetDlgItemTextW(IDC_EXPLANATION
, explanation
);
364 INT_PTR
CLayerUIPropPage::DisableControls()
366 ::EnableWindow(GetDlgItem(IDC_COMPATIBILITYMODE
), 0);
367 ::EnableWindow(GetDlgItem(IDC_CHKRUNCOMPATIBILITY
), 0);
368 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
369 ::EnableWindow(GetDlgItem(g_Layers
[n
].Id
), 0);
370 ::EnableWindow(GetDlgItem(IDC_EDITCOMPATIBILITYMODES
), 0);
374 void CLayerUIPropPage::UpdateControls()
376 m_OSMode
= 0, m_EnabledLayers
= 0;
377 BOOL ModeEnabled
= IsDlgButtonChecked(IDC_CHKRUNCOMPATIBILITY
);
379 m_OSMode
= ComboBox_GetCurSel(GetDlgItem(IDC_COMPATIBILITYMODE
))+1;
380 ::EnableWindow(GetDlgItem(IDC_COMPATIBILITYMODE
), ModeEnabled
);
382 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
384 m_EnabledLayers
|= IsDlgButtonChecked(g_Layers
[n
].Id
) ? (1<<n
) : 0;
385 ::ShowWindow(GetDlgItem(g_Layers
[n
].Id
), SW_SHOW
);
388 CStringW customLayers
;
389 for (int j
= 0; j
< m_CustomLayers
.GetSize(); ++j
)
392 customLayers
+= L
", ";
393 customLayers
+= m_CustomLayers
[j
];
395 SetDlgItemTextW(IDC_ENABLED_LAYERS
, customLayers
);
397 SetModified(HasChanges());
400 LRESULT
CLayerUIPropPage::OnCtrlCommand(WORD wNotifyCode
, WORD wID
, HWND hWndCtl
, BOOL
&bHandled
)
406 LRESULT
CLayerUIPropPage::OnEditModes(WORD wNotifyCode
, WORD wID
, HWND hWndCtl
, BOOL
&bHandled
)
408 if (ShowEditCompatModes(m_hWnd
, this))
413 LRESULT
CLayerUIPropPage::OnClickNotify(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
415 if (hdr
->idFrom
== IDC_INFOLINK
)
416 ShellExecute(NULL
, L
"open", L
"https://reactos.org/forum/viewforum.php?f=4", NULL
, NULL
, SW_SHOW
);
420 static BOOL
DisableShellext()
423 LSTATUS ret
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, L
"SOFTWARE\\Policies\\Microsoft\\Windows\\AppCompat", 0, KEY_QUERY_VALUE
, &hkey
);
424 BOOL Disable
= FALSE
;
425 if (ret
== ERROR_SUCCESS
)
428 DWORD type
, size
= sizeof(dwValue
);
429 ret
= RegQueryValueExW(hkey
, L
"DisableEngine", NULL
, &type
, (PBYTE
)&dwValue
, &size
);
430 if (ret
== ERROR_SUCCESS
&& type
== REG_DWORD
)
436 size
= sizeof(dwValue
);
437 ret
= RegQueryValueExW(hkey
, L
"DisablePropPage", NULL
, &type
, (PBYTE
)&dwValue
, &size
);
438 if (ret
== ERROR_SUCCESS
&& type
== REG_DWORD
)
449 STDMETHODIMP
CLayerUIPropPage::Initialize(PCIDLIST_ABSOLUTE pidlFolder
, LPDATAOBJECT pDataObj
, HKEY hkeyProgID
)
451 FORMATETC etc
= { CF_HDROP
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
454 if (DisableShellext())
455 return E_ACCESSDENIED
;
457 HRESULT hr
= pDataObj
->GetData(&etc
, &stg
);
460 ACDBG(L
"Failed to retrieve Data from pDataObj.\r\n");
464 HDROP hdrop
= (HDROP
)GlobalLock(stg
.hGlobal
);
467 UINT uNumFiles
= DragQueryFileW(hdrop
, 0xFFFFFFFF, NULL
, 0);
470 WCHAR szFile
[MAX_PATH
* 2];
471 if (DragQueryFileW(hdrop
, 0, szFile
, _countof(szFile
)))
474 hr
= InitFile(szFile
);
478 ACDBG(L
"Failed to query the file.\r\n");
483 ACDBG(L
"Invalid number of files: %d\r\n", uNumFiles
);
485 GlobalUnlock(stg
.hGlobal
);
489 ACDBG(L
"Could not lock stg.hGlobal\r\n");
491 ReleaseStgMedium(&stg
);