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 (SP2)", L
"WINXPSP2" },
33 { L
"Windows XP (SP3)", L
"WINXPSP3" },
34 { L
"Windows Server 2003 (SP1)", L
"WINSRV03SP1" },
35 { L
"Windows Server 2008 (SP1)", L
"WINSRV08SP1" },
36 { L
"Windows Vista", L
"VISTARTM" },
37 { L
"Windows Vista (SP1)", L
"VISTASP1" },
38 { L
"Windows Vista (SP2)", L
"VISTASP2" },
39 { L
"Windows 7", L
"WIN7RTM" },
40 { L
"Windows 7 (SP1)", L
"WIN7SP1" },
41 { L
"Windows 8", L
"WIN8RTM" },
42 { L
"Windows 8.1", L
"WIN81RTM" },
50 { L
"256COLOR", IDC_CHKRUNIN256COLORS
},
51 { L
"640X480", IDC_CHKRUNIN640480RES
},
52 { L
"DISABLETHEMES", IDC_CHKDISABLEVISUALTHEMES
},
54 { L
"DISABLEDWM", IDC_
??, TRUE
},
55 { L
"HIGHDPIAWARE", IDC_
??, TRUE
},
56 { L
"RUNASADMIN", IDC_
??, TRUE
},
61 static const WCHAR
* g_AllowedExtensions
[] = {
70 BOOL
IsBuiltinLayer(PCWSTR Name
)
74 for (n
= 0; g_Layers
[n
].Name
; ++n
)
76 if (!wcsicmp(g_Layers
[n
].Name
, Name
))
82 for (n
= 0; g_CompatModes
[n
].Name
; ++n
)
84 if (!wcsicmp(g_CompatModes
[n
].Name
, Name
))
93 void ACDBG_FN(PCSTR FunctionName
, PCWSTR Format
, ...)
96 WCHAR
* Current
= Buffer
;
97 size_t Length
= _countof(Buffer
);
99 StringCchPrintfExW(Current
, Length
, &Current
, &Length
, STRSAFE_NULL_ON_FAILURE
, L
"[%-20S] ", FunctionName
);
101 va_start(ArgList
, Format
);
102 StringCchVPrintfExW(Current
, Length
, &Current
, &Length
, STRSAFE_NULL_ON_FAILURE
, Format
, ArgList
);
104 OutputDebugStringW(Buffer
);
107 #define ACDBG(fmt, ...) ACDBG_FN(__FUNCTION__, fmt, ##__VA_ARGS__ )
111 CLayerUIPropPage::CLayerUIPropPage()
112 : m_IsSfcProtected(FALSE
)
113 , m_AllowPermLayer(FALSE
)
114 , m_LayerQueryFlags(GPLK_USER
) /* TODO: When do we read from HKLM? */
115 , m_RegistryOSMode(0)
117 , m_RegistryEnabledLayers(0)
121 title
.LoadString(g_hModule
, IDS_COMPAT_TITLE
);
122 m_psp
.pszTitle
= title
.Detach();
123 m_psp
.dwFlags
|= PSP_USETITLE
;
126 CLayerUIPropPage::~CLayerUIPropPage()
129 title
.Attach((BSTR
)m_psp
.pszTitle
);
132 HRESULT
CLayerUIPropPage::InitFile(PCWSTR Filename
)
134 CString ExpandedFilename
;
135 DWORD dwRequired
= ExpandEnvironmentStringsW(Filename
, NULL
, 0);
138 LPWSTR Buffer
= ExpandedFilename
.GetBuffer(dwRequired
);
139 DWORD dwReturned
= ExpandEnvironmentStringsW(Filename
, Buffer
, dwRequired
);
140 if (dwRequired
== dwReturned
)
142 ExpandedFilename
.ReleaseBufferSetLength(dwReturned
- 1);
143 ACDBG(L
"Expanded '%s' => '%s'\r\n", Filename
, (PCWSTR
)ExpandedFilename
);
147 ExpandedFilename
.ReleaseBufferSetLength(0);
148 ExpandedFilename
= Filename
;
149 ACDBG(L
"Failed during expansion '%s'\r\n", Filename
);
154 ACDBG(L
"Failed to expand '%s'\r\n", Filename
);
155 ExpandedFilename
= Filename
;
157 PCWSTR pwszExt
= PathFindExtensionW(ExpandedFilename
);
160 ACDBG(L
"Failed to find an extension: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
163 if (!wcsicmp(pwszExt
, L
".lnk"))
165 WCHAR Buffer
[MAX_PATH
];
166 if (!GetExeFromLnk(ExpandedFilename
, Buffer
, _countof(Buffer
)))
168 ACDBG(L
"Failed to read link target from: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
171 if (!wcsicmp(Buffer
, ExpandedFilename
))
173 ACDBG(L
"Link redirects to itself: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
176 return InitFile(Buffer
);
180 if (tmp
.GetEnvironmentVariable(L
"SystemRoot"))
182 tmp
+= L
"\\System32";
183 if (ExpandedFilename
.GetLength() >= tmp
.GetLength() &&
184 ExpandedFilename
.Left(tmp
.GetLength()).MakeLower() == tmp
.MakeLower())
186 ACDBG(L
"Ignoring System32: %s\r\n", (PCWSTR
)ExpandedFilename
);
189 tmp
.GetEnvironmentVariable(L
"SystemRoot");
191 if (ExpandedFilename
.GetLength() >= tmp
.GetLength() &&
192 ExpandedFilename
.Left(tmp
.GetLength()).MakeLower() == tmp
.MakeLower())
194 ACDBG(L
"Ignoring WinSxs: %s\r\n", (PCWSTR
)ExpandedFilename
);
199 for (size_t n
= 0; g_AllowedExtensions
[n
]; ++n
)
201 if (!wcsicmp(g_AllowedExtensions
[n
], pwszExt
))
203 m_Filename
= ExpandedFilename
;
204 ACDBG(L
"Got: %s\r\n", (PCWSTR
)ExpandedFilename
);
205 m_IsSfcProtected
= SfcIsFileProtected(NULL
, m_Filename
);
206 m_AllowPermLayer
= AllowPermLayer(ExpandedFilename
);
210 ACDBG(L
"Extension not included: '%s'\r\n", pwszExt
);
214 static BOOL
GetLayerInfo(PCWSTR Filename
, DWORD QueryFlags
, PDWORD OSMode
, PDWORD Enabledlayers
, CSimpleArray
<CString
>& customLayers
)
216 WCHAR wszLayers
[MAX_LAYER_LENGTH
] = { 0 };
217 DWORD dwBytes
= sizeof(wszLayers
);
219 *OSMode
= *Enabledlayers
= 0;
220 customLayers
.RemoveAll();
221 if (!SdbGetPermLayerKeys(Filename
, wszLayers
, &dwBytes
, QueryFlags
))
224 for (PWCHAR Layer
= wcstok(wszLayers
, L
" "); Layer
; Layer
= wcstok(NULL
, L
" "))
227 for (n
= 0; g_Layers
[n
].Name
; ++n
)
229 if (!wcsicmp(g_Layers
[n
].Name
, Layer
))
231 *Enabledlayers
|= (1<<n
);
235 /* Did we find it? */
236 if (g_Layers
[n
].Name
)
239 for (n
= 0; g_CompatModes
[n
].Name
; ++n
)
241 if (!wcsicmp(g_CompatModes
[n
].Name
, Layer
))
247 /* Did we find it? */
248 if (g_CompatModes
[n
].Name
)
251 /* Must be a 'custom' layer */
252 customLayers
.Add(Layer
);
257 int CLayerUIPropPage::OnSetActive()
259 if (!GetLayerInfo(m_Filename
, m_LayerQueryFlags
, &m_RegistryOSMode
, &m_RegistryEnabledLayers
, m_RegistryCustomLayers
))
260 m_RegistryOSMode
= m_RegistryEnabledLayers
= 0;
262 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
263 CheckDlgButton(g_Layers
[n
].Id
, (m_RegistryEnabledLayers
& (1<<n
)) ? BST_CHECKED
: BST_UNCHECKED
);
265 CheckDlgButton(IDC_CHKRUNCOMPATIBILITY
, m_RegistryOSMode
? BST_CHECKED
: BST_UNCHECKED
);
267 if (m_RegistryOSMode
)
268 ComboBox_SetCurSel(GetDlgItem(IDC_COMPATIBILITYMODE
), m_RegistryOSMode
-1);
270 m_CustomLayers
= m_RegistryCustomLayers
;
278 static BOOL
ArrayEquals(const CSimpleArray
<CString
>& lhs
, const CSimpleArray
<CString
>& rhs
)
280 if (lhs
.GetSize() != rhs
.GetSize())
283 for (int n
= 0; n
< lhs
.GetSize(); ++n
)
285 if (lhs
[n
] != rhs
[n
])
291 BOOL
CLayerUIPropPage::HasChanges() const
293 if (m_RegistryEnabledLayers
!= m_EnabledLayers
)
296 if (m_RegistryOSMode
!= m_OSMode
)
299 if (!ArrayEquals(m_RegistryCustomLayers
, m_CustomLayers
))
305 int CLayerUIPropPage::OnApply()
309 BOOL bMachine
= m_LayerQueryFlags
== GPLK_MACHINE
;
311 for (size_t n
= 0; g_CompatModes
[n
].Name
; ++n
)
312 SetPermLayerState(m_Filename
, g_CompatModes
[n
].Name
, 0, bMachine
, (n
+1) == m_OSMode
);
314 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
316 SetPermLayerState(m_Filename
, g_Layers
[n
].Name
, 0, bMachine
, ((1<<n
) & m_EnabledLayers
) != 0);
319 /* Disable all old values */
320 for (int j
= 0; j
< m_RegistryCustomLayers
.GetSize(); j
++)
322 SetPermLayerState(m_Filename
, m_RegistryCustomLayers
[j
].GetString(), 0, bMachine
, FALSE
);
325 /* Enable all new values */
326 for (int j
= 0; j
< m_CustomLayers
.GetSize(); j
++)
328 SetPermLayerState(m_Filename
, m_CustomLayers
[j
].GetString(), 0, bMachine
, TRUE
);
331 SHChangeNotify(SHCNE_UPDATEITEM
, SHCNF_PATHW
, (PCWSTR
)m_Filename
, NULL
);
334 return PSNRET_NOERROR
;
337 LRESULT
CLayerUIPropPage::OnInitDialog(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
339 HWND cboMode
= GetDlgItem(IDC_COMPATIBILITYMODE
);
340 for (size_t n
= 0; g_CompatModes
[n
].Display
; ++n
)
341 ComboBox_AddString(cboMode
, g_CompatModes
[n
].Display
);
342 ComboBox_SetCurSel(cboMode
, 5);
344 CStringW explanation
;
345 if (!m_AllowPermLayer
)
347 explanation
.LoadString(g_hModule
, IDS_FAILED_NETWORK
);
349 ACDBG(L
"AllowPermLayer returned FALSE\r\n");
351 else if (m_IsSfcProtected
)
353 explanation
.LoadString(g_hModule
, IDS_FAILED_PROTECTED
);
355 ACDBG(L
"Protected OS file\r\n");
361 SetDlgItemTextW(IDC_EXPLANATION
, explanation
);
365 INT_PTR
CLayerUIPropPage::DisableControls()
367 ::EnableWindow(GetDlgItem(IDC_COMPATIBILITYMODE
), 0);
368 ::EnableWindow(GetDlgItem(IDC_CHKRUNCOMPATIBILITY
), 0);
369 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
370 ::EnableWindow(GetDlgItem(g_Layers
[n
].Id
), 0);
371 ::EnableWindow(GetDlgItem(IDC_EDITCOMPATIBILITYMODES
), 0);
375 void CLayerUIPropPage::UpdateControls()
377 m_OSMode
= 0, m_EnabledLayers
= 0;
378 BOOL ModeEnabled
= IsDlgButtonChecked(IDC_CHKRUNCOMPATIBILITY
);
380 m_OSMode
= ComboBox_GetCurSel(GetDlgItem(IDC_COMPATIBILITYMODE
))+1;
381 ::EnableWindow(GetDlgItem(IDC_COMPATIBILITYMODE
), ModeEnabled
);
383 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
385 m_EnabledLayers
|= IsDlgButtonChecked(g_Layers
[n
].Id
) ? (1<<n
) : 0;
386 ::ShowWindow(GetDlgItem(g_Layers
[n
].Id
), SW_SHOW
);
389 CStringW customLayers
;
390 for (int j
= 0; j
< m_CustomLayers
.GetSize(); ++j
)
393 customLayers
+= L
", ";
394 customLayers
+= m_CustomLayers
[j
];
396 SetDlgItemTextW(IDC_ENABLED_LAYERS
, customLayers
);
398 SetModified(HasChanges());
401 LRESULT
CLayerUIPropPage::OnCtrlCommand(WORD wNotifyCode
, WORD wID
, HWND hWndCtl
, BOOL
&bHandled
)
407 LRESULT
CLayerUIPropPage::OnEditModes(WORD wNotifyCode
, WORD wID
, HWND hWndCtl
, BOOL
&bHandled
)
409 if (ShowEditCompatModes(m_hWnd
, this))
414 LRESULT
CLayerUIPropPage::OnClickNotify(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
416 if (hdr
->idFrom
== IDC_INFOLINK
)
417 ShellExecute(NULL
, L
"open", L
"https://www.reactos.org/forum/viewforum.php?f=4", NULL
, NULL
, SW_SHOW
);
421 static BOOL
DisableShellext()
424 LSTATUS ret
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, L
"SOFTWARE\\Policies\\Microsoft\\Windows\\AppCompat", 0, KEY_QUERY_VALUE
, &hkey
);
425 BOOL Disable
= FALSE
;
426 if (ret
== ERROR_SUCCESS
)
429 DWORD type
, size
= sizeof(dwValue
);
430 ret
= RegQueryValueExW(hkey
, L
"DisableEngine", NULL
, &type
, (PBYTE
)&dwValue
, &size
);
431 if (ret
== ERROR_SUCCESS
&& type
== REG_DWORD
)
437 size
= sizeof(dwValue
);
438 ret
= RegQueryValueExW(hkey
, L
"DisablePropPage", NULL
, &type
, (PBYTE
)&dwValue
, &size
);
439 if (ret
== ERROR_SUCCESS
&& type
== REG_DWORD
)
450 STDMETHODIMP
CLayerUIPropPage::Initialize(PCIDLIST_ABSOLUTE pidlFolder
, LPDATAOBJECT pDataObj
, HKEY hkeyProgID
)
452 FORMATETC etc
= { CF_HDROP
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
455 if (DisableShellext())
456 return E_ACCESSDENIED
;
458 HRESULT hr
= pDataObj
->GetData(&etc
, &stg
);
461 ACDBG(L
"Failed to retrieve Data from pDataObj.\r\n");
465 HDROP hdrop
= (HDROP
)GlobalLock(stg
.hGlobal
);
468 UINT uNumFiles
= DragQueryFileW(hdrop
, 0xFFFFFFFF, NULL
, 0);
471 WCHAR szFile
[MAX_PATH
* 2];
472 if (DragQueryFileW(hdrop
, 0, szFile
, _countof(szFile
)))
475 hr
= InitFile(szFile
);
479 ACDBG(L
"Failed to query the file.\r\n");
484 ACDBG(L
"Invalid number of files: %d\r\n", uNumFiles
);
486 GlobalUnlock(stg
.hGlobal
);
490 ACDBG(L
"Could not lock stg.hGlobal\r\n");
492 ReleaseStgMedium(&stg
);