2 * PROJECT: ReactOS Compatibility Layer Shell Extension
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: CLayerUIPropPage implementation
5 * COPYRIGHT: Copyright 2015-2018 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 } };
19 #define ACP_WNDPROP L"{513D916F-2A8E-4F51-AEAB-0CBC76FB1AF8}.Prop"
22 #define GPLK_MACHINE 2
23 #define MAX_LAYER_LENGTH 256
29 { L
"Windows 95", L
"WIN95" },
30 { L
"Windows 98/ME", L
"WIN98" },
31 { L
"Windows NT 4.0 (SP5)", L
"NT4SP5" },
32 { L
"Windows 2000", L
"WIN2000" },
33 { L
"Windows XP (SP2)", L
"WINXPSP2" },
34 { L
"Windows XP (SP3)", L
"WINXPSP3" },
35 { L
"Windows Server 2003 (SP1)", L
"WINSRV03SP1" },
36 { L
"Windows Server 2008 (SP1)", L
"WINSRV08SP1" },
37 { L
"Windows Vista", L
"VISTARTM" },
38 { L
"Windows Vista (SP1)", L
"VISTASP1" },
39 { L
"Windows Vista (SP2)", L
"VISTASP2" },
40 { L
"Windows 7", L
"WIN7RTM" },
48 { L
"256COLOR", IDC_CHKRUNIN256COLORS
},
49 { L
"640X480", IDC_CHKRUNIN640480RES
},
50 { L
"DISABLETHEMES", IDC_CHKDISABLEVISUALTHEMES
},
52 { L
"DISABLEDWM", IDC_
??, TRUE
},
53 { L
"HIGHDPIAWARE", IDC_
??, TRUE
},
54 { L
"RUNASADMIN", IDC_
??, TRUE
},
59 static const WCHAR
* g_AllowedExtensions
[] = {
68 BOOL
IsBuiltinLayer(PCWSTR Name
)
72 for (n
= 0; g_Layers
[n
].Name
; ++n
)
74 if (!wcsicmp(g_Layers
[n
].Name
, Name
))
80 for (n
= 0; g_CompatModes
[n
].Name
; ++n
)
82 if (!wcsicmp(g_CompatModes
[n
].Name
, Name
))
91 void ACDBG_FN(PCSTR FunctionName
, PCWSTR Format
, ...)
94 WCHAR
* Current
= Buffer
;
95 size_t Length
= _countof(Buffer
);
97 StringCchPrintfExW(Current
, Length
, &Current
, &Length
, STRSAFE_NULL_ON_FAILURE
, L
"[%-20S] ", FunctionName
);
99 va_start(ArgList
, Format
);
100 StringCchVPrintfExW(Current
, Length
, &Current
, &Length
, STRSAFE_NULL_ON_FAILURE
, Format
, ArgList
);
102 OutputDebugStringW(Buffer
);
105 #define ACDBG(fmt, ...) ACDBG_FN(__FUNCTION__, fmt, ##__VA_ARGS__ )
109 CLayerUIPropPage::CLayerUIPropPage()
110 : m_IsSfcProtected(FALSE
)
111 , m_AllowPermLayer(FALSE
)
112 , m_LayerQueryFlags(GPLK_USER
) /* TODO: When do we read from HKLM? */
113 , m_RegistryOSMode(0)
115 , m_RegistryEnabledLayers(0)
119 title
.LoadString(g_hModule
, IDS_COMPAT_TITLE
);
120 m_psp
.pszTitle
= title
.Detach();
121 m_psp
.dwFlags
|= PSP_USETITLE
;
124 CLayerUIPropPage::~CLayerUIPropPage()
127 title
.Attach((BSTR
)m_psp
.pszTitle
);
130 HRESULT
CLayerUIPropPage::InitFile(PCWSTR Filename
)
132 CString ExpandedFilename
;
133 DWORD dwRequired
= ExpandEnvironmentStringsW(Filename
, NULL
, 0);
136 LPWSTR Buffer
= ExpandedFilename
.GetBuffer(dwRequired
);
137 DWORD dwReturned
= ExpandEnvironmentStringsW(Filename
, Buffer
, dwRequired
);
138 if (dwRequired
== dwReturned
)
140 ExpandedFilename
.ReleaseBufferSetLength(dwReturned
- 1);
141 ACDBG(L
"Expanded '%s' => '%s'\r\n", Filename
, (PCWSTR
)ExpandedFilename
);
145 ExpandedFilename
.ReleaseBufferSetLength(0);
146 ExpandedFilename
= Filename
;
147 ACDBG(L
"Failed during expansion '%s'\r\n", Filename
);
152 ACDBG(L
"Failed to expand '%s'\r\n", Filename
);
153 ExpandedFilename
= Filename
;
155 PCWSTR pwszExt
= PathFindExtensionW(ExpandedFilename
);
158 ACDBG(L
"Failed to find an extension: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
161 if (!wcsicmp(pwszExt
, L
".lnk"))
163 WCHAR Buffer
[MAX_PATH
];
164 if (!GetExeFromLnk(ExpandedFilename
, Buffer
, _countof(Buffer
)))
166 ACDBG(L
"Failed to read link target from: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
169 if (!wcsicmp(Buffer
, ExpandedFilename
))
171 ACDBG(L
"Link redirects to itself: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
174 return InitFile(Buffer
);
178 if (tmp
.GetEnvironmentVariable(L
"SystemRoot"))
180 tmp
+= L
"\\System32";
181 if (ExpandedFilename
.GetLength() >= tmp
.GetLength() &&
182 ExpandedFilename
.Left(tmp
.GetLength()).MakeLower() == tmp
.MakeLower())
184 ACDBG(L
"Ignoring System32: %s\r\n", (PCWSTR
)ExpandedFilename
);
187 tmp
.GetEnvironmentVariable(L
"SystemRoot");
189 if (ExpandedFilename
.GetLength() >= tmp
.GetLength() &&
190 ExpandedFilename
.Left(tmp
.GetLength()).MakeLower() == tmp
.MakeLower())
192 ACDBG(L
"Ignoring WinSxs: %s\r\n", (PCWSTR
)ExpandedFilename
);
197 for (size_t n
= 0; g_AllowedExtensions
[n
]; ++n
)
199 if (!wcsicmp(g_AllowedExtensions
[n
], pwszExt
))
201 m_Filename
= ExpandedFilename
;
202 ACDBG(L
"Got: %s\r\n", (PCWSTR
)ExpandedFilename
);
203 m_IsSfcProtected
= SfcIsFileProtected(NULL
, m_Filename
);
204 m_AllowPermLayer
= AllowPermLayer(ExpandedFilename
);
208 ACDBG(L
"Extension not included: '%s'\r\n", pwszExt
);
212 static BOOL
GetLayerInfo(PCWSTR Filename
, DWORD QueryFlags
, PDWORD OSMode
, PDWORD Enabledlayers
, CSimpleArray
<CString
>& customLayers
)
214 WCHAR wszLayers
[MAX_LAYER_LENGTH
] = { 0 };
215 DWORD dwBytes
= sizeof(wszLayers
);
217 *OSMode
= *Enabledlayers
= 0;
218 customLayers
.RemoveAll();
219 if (!SdbGetPermLayerKeys(Filename
, wszLayers
, &dwBytes
, QueryFlags
))
222 for (PWCHAR Layer
= wcstok(wszLayers
, L
" "); Layer
; Layer
= wcstok(NULL
, L
" "))
225 for (n
= 0; g_Layers
[n
].Name
; ++n
)
227 if (!wcsicmp(g_Layers
[n
].Name
, Layer
))
229 *Enabledlayers
|= (1<<n
);
233 /* Did we find it? */
234 if (g_Layers
[n
].Name
)
237 for (n
= 0; g_CompatModes
[n
].Name
; ++n
)
239 if (!wcsicmp(g_CompatModes
[n
].Name
, Layer
))
245 /* Did we find it? */
246 if (g_CompatModes
[n
].Name
)
249 /* Must be a 'custom' layer */
250 customLayers
.Add(Layer
);
255 int CLayerUIPropPage::OnSetActive()
257 if (!GetLayerInfo(m_Filename
, m_LayerQueryFlags
, &m_RegistryOSMode
, &m_RegistryEnabledLayers
, m_RegistryCustomLayers
))
258 m_RegistryOSMode
= m_RegistryEnabledLayers
= 0;
260 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
261 CheckDlgButton(g_Layers
[n
].Id
, (m_RegistryEnabledLayers
& (1<<n
)) ? BST_CHECKED
: BST_UNCHECKED
);
263 CheckDlgButton(IDC_CHKRUNCOMPATIBILITY
, m_RegistryOSMode
? BST_CHECKED
: BST_UNCHECKED
);
265 if (m_RegistryOSMode
)
266 ComboBox_SetCurSel(GetDlgItem(IDC_COMPATIBILITYMODE
), m_RegistryOSMode
-1);
268 m_CustomLayers
= m_RegistryCustomLayers
;
276 static BOOL
ArrayEquals(const CSimpleArray
<CString
>& lhs
, const CSimpleArray
<CString
>& rhs
)
278 if (lhs
.GetSize() != rhs
.GetSize())
281 for (int n
= 0; n
< lhs
.GetSize(); ++n
)
283 if (lhs
[n
] != rhs
[n
])
289 BOOL
CLayerUIPropPage::HasChanges() const
291 if (m_RegistryEnabledLayers
!= m_EnabledLayers
)
294 if (m_RegistryOSMode
!= m_OSMode
)
297 if (!ArrayEquals(m_RegistryCustomLayers
, m_CustomLayers
))
303 int CLayerUIPropPage::OnApply()
307 BOOL bMachine
= m_LayerQueryFlags
== GPLK_MACHINE
;
309 for (size_t n
= 0; g_CompatModes
[n
].Name
; ++n
)
310 SetPermLayerState(m_Filename
, g_CompatModes
[n
].Name
, 0, bMachine
, (n
+1) == m_OSMode
);
312 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
314 SetPermLayerState(m_Filename
, g_Layers
[n
].Name
, 0, bMachine
, ((1<<n
) & m_EnabledLayers
) != 0);
317 /* Disable all old values */
318 for (int j
= 0; j
< m_RegistryCustomLayers
.GetSize(); j
++)
320 SetPermLayerState(m_Filename
, m_RegistryCustomLayers
[j
].GetString(), 0, bMachine
, FALSE
);
323 /* Enable all new values */
324 for (int j
= 0; j
< m_CustomLayers
.GetSize(); j
++)
326 SetPermLayerState(m_Filename
, m_CustomLayers
[j
].GetString(), 0, bMachine
, TRUE
);
329 SHChangeNotify(SHCNE_UPDATEITEM
, SHCNF_PATHW
, (PCWSTR
)m_Filename
, NULL
);
332 return PSNRET_NOERROR
;
335 LRESULT
CLayerUIPropPage::OnInitDialog(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
337 HWND cboMode
= GetDlgItem(IDC_COMPATIBILITYMODE
);
338 for (size_t n
= 0; g_CompatModes
[n
].Display
; ++n
)
339 ComboBox_AddString(cboMode
, g_CompatModes
[n
].Display
);
340 ComboBox_SetCurSel(cboMode
, 5);
342 CComBSTR explanation
;
343 if (!m_AllowPermLayer
)
345 explanation
.LoadString(g_hModule
, IDS_FAILED_NETWORK
);
347 ACDBG(L
"AllowPermLayer returned FALSE\r\n");
349 else if (m_IsSfcProtected
)
351 explanation
.LoadString(g_hModule
, IDS_FAILED_PROTECTED
);
353 ACDBG(L
"Protected OS file\r\n");
359 SetDlgItemTextW(IDC_EXPLANATION
, explanation
);
363 INT_PTR
CLayerUIPropPage::DisableControls()
365 ::EnableWindow(GetDlgItem(IDC_COMPATIBILITYMODE
), 0);
366 ::EnableWindow(GetDlgItem(IDC_CHKRUNCOMPATIBILITY
), 0);
367 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
368 ::EnableWindow(GetDlgItem(g_Layers
[n
].Id
), 0);
369 ::EnableWindow(GetDlgItem(IDC_EDITCOMPATIBILITYMODES
), 0);
373 void CLayerUIPropPage::UpdateControls()
375 m_OSMode
= 0, m_EnabledLayers
= 0;
376 BOOL ModeEnabled
= IsDlgButtonChecked(IDC_CHKRUNCOMPATIBILITY
);
378 m_OSMode
= ComboBox_GetCurSel(GetDlgItem(IDC_COMPATIBILITYMODE
))+1;
379 ::EnableWindow(GetDlgItem(IDC_COMPATIBILITYMODE
), ModeEnabled
);
381 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
383 m_EnabledLayers
|= IsDlgButtonChecked(g_Layers
[n
].Id
) ? (1<<n
) : 0;
384 ::ShowWindow(GetDlgItem(g_Layers
[n
].Id
), SW_SHOW
);
387 CStringW customLayers
;
388 for (int j
= 0; j
< m_CustomLayers
.GetSize(); ++j
)
391 customLayers
+= L
", ";
392 customLayers
+= m_CustomLayers
[j
];
394 SetDlgItemTextW(IDC_ENABLED_LAYERS
, customLayers
);
396 SetModified(HasChanges());
399 LRESULT
CLayerUIPropPage::OnCtrlCommand(WORD wNotifyCode
, WORD wID
, HWND hWndCtl
, BOOL
&bHandled
)
405 LRESULT
CLayerUIPropPage::OnEditModes(WORD wNotifyCode
, WORD wID
, HWND hWndCtl
, BOOL
&bHandled
)
407 if (DialogBoxParamW(g_hModule
, MAKEINTRESOURCEW(IDD_EDITCOMPATIBILITYMODES
), m_hWnd
, EditModesProc
, (LPARAM
)this) == IDOK
)
412 LRESULT
CLayerUIPropPage::OnClickNotify(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
414 if (hdr
->idFrom
== IDC_INFOLINK
)
415 ShellExecute(NULL
, L
"open", L
"https://www.reactos.org/forum/viewforum.php?f=4", NULL
, NULL
, SW_SHOW
);
419 static void ListboxChanged(HWND hWnd
)
421 int Sel
= ListBox_GetCurSel(GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
));
422 EnableWindow(GetDlgItem(hWnd
, IDC_EDIT
), Sel
>= 0);
423 EnableWindow(GetDlgItem(hWnd
, IDC_DELETE
), Sel
>= 0);
426 static void OnAdd(HWND hWnd
)
428 HWND Combo
= GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
430 int Length
= ComboBox_GetTextLength(Combo
);
431 CComBSTR
Str(Length
);
432 ComboBox_GetText(Combo
, Str
, Length
+1);
433 HWND List
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
434 int Index
= ListBox_FindStringExact(List
, -1, Str
);
436 Index
= ListBox_AddString(List
, Str
);
437 ListBox_SetCurSel(List
, Index
);
438 ListboxChanged(hWnd
);
439 ComboBox_SetCurSel(Combo
, -1);
443 static BOOL
ComboHasData(HWND hWnd
)
445 HWND Combo
= GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
446 if (ComboBox_GetCurSel(Combo
) >= 0)
448 ULONG Len
= ComboBox_GetTextLength(Combo
);
452 INT_PTR CALLBACK
CLayerUIPropPage::EditModesProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
454 CLayerUIPropPage
* page
= NULL
;
459 page
= (CLayerUIPropPage
*)lParam
;
461 ::SetProp(hWnd
, ACP_WNDPROP
, page
);
463 HWND Combo
= ::GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
464 CComObject
<CLayerStringList
> pList
;
468 CComHeapPtr
<OLECHAR
> str
;
469 HRESULT hr
= pList
.Next(1, &str
, NULL
);
472 ComboBox_AddString(Combo
, str
);
475 HWND List
= ::GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
476 for (int n
= 0; n
< page
->m_CustomLayers
.GetSize(); ++n
)
478 const WCHAR
* Str
= page
->m_CustomLayers
[n
].GetString();
479 int Index
= ListBox_FindStringExact(List
, -1, Str
);
481 Index
= ListBox_AddString(List
, Str
);
487 page
= (CLayerUIPropPage
*)::GetProp(hWnd
, ACP_WNDPROP
);
488 ::RemoveProp(hWnd
, ACP_WNDPROP
);
493 switch(LOWORD(wParam
))
500 HWND List
= ::GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
501 int Cur
= ListBox_GetCurSel(List
);
502 int Length
= ListBox_GetTextLen(List
, Cur
);
503 CComBSTR
Str(Length
);
504 ListBox_GetText(List
, Cur
, Str
);
505 ListBox_DeleteString(List
, Cur
);
506 HWND Combo
= ::GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
507 ComboBox_SetCurSel(Combo
, -1);
508 ::SetWindowText(Combo
,Str
);
509 ListboxChanged(hWnd
);
510 ComboBox_SetEditSel(Combo
, 30000, 30000);
516 HWND List
= ::GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
517 ListBox_DeleteString(List
, ListBox_GetCurSel(List
));
518 ListboxChanged(hWnd
);
521 case IDC_COMPATIBILITYMODE
:
522 ListboxChanged(hWnd
);
524 case IDC_NEWCOMPATIBILITYMODE
:
526 ::EnableWindow(::GetDlgItem(hWnd
, IDC_ADD
), ComboHasData(hWnd
));
530 /* Copy from list! */
532 if (ComboHasData(hWnd
))
534 CComBSTR question
, title
;
535 title
.LoadString(g_hModule
, IDS_COMPAT_TITLE
);
536 question
.LoadString(g_hModule
, IDS_YOU_DID_NOT_ADD
);
537 int result
= ::MessageBoxW(hWnd
, question
, title
, MB_YESNOCANCEL
| MB_ICONQUESTION
);
550 page
= (CLayerUIPropPage
*)::GetProp(hWnd
, ACP_WNDPROP
);
552 HWND List
= ::GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
553 int Count
= ListBox_GetCount(List
);
554 page
->m_CustomLayers
.RemoveAll();
555 for (int Cur
= 0; Cur
< Count
; ++Cur
)
557 int Length
= ListBox_GetTextLen(List
, Cur
);
559 LPWSTR Buffer
= Str
.GetBuffer(Length
+ 1);
560 ListBox_GetText(List
, Cur
, Buffer
);
561 Str
.ReleaseBuffer(Length
);
562 page
->m_CustomLayers
.Add(Str
);
567 ::EndDialog(hWnd
, LOWORD(wParam
));
572 ::EndDialog(hWnd
, IDCANCEL
);
578 static BOOL
DisableShellext()
581 LSTATUS ret
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, L
"SOFTWARE\\Policies\\Microsoft\\Windows\\AppCompat", 0, KEY_QUERY_VALUE
, &hkey
);
582 BOOL Disable
= FALSE
;
583 if (ret
== ERROR_SUCCESS
)
586 DWORD type
, size
= sizeof(dwValue
);
587 ret
= RegQueryValueExW(hkey
, L
"DisableEngine", NULL
, &type
, (PBYTE
)&dwValue
, &size
);
588 if (ret
== ERROR_SUCCESS
&& type
== REG_DWORD
)
594 size
= sizeof(dwValue
);
595 ret
= RegQueryValueExW(hkey
, L
"DisablePropPage", NULL
, &type
, (PBYTE
)&dwValue
, &size
);
596 if (ret
== ERROR_SUCCESS
&& type
== REG_DWORD
)
607 STDMETHODIMP
CLayerUIPropPage::Initialize(LPCITEMIDLIST pidlFolder
, LPDATAOBJECT pDataObj
, HKEY hkeyProgID
)
609 FORMATETC etc
= { CF_HDROP
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
612 if (DisableShellext())
613 return E_ACCESSDENIED
;
615 HRESULT hr
= pDataObj
->GetData(&etc
, &stg
);
618 ACDBG(L
"Failed to retrieve Data from pDataObj.\r\n");
622 HDROP hdrop
= (HDROP
)GlobalLock(stg
.hGlobal
);
625 UINT uNumFiles
= DragQueryFileW(hdrop
, 0xFFFFFFFF, NULL
, 0);
628 WCHAR szFile
[MAX_PATH
* 2];
629 if (DragQueryFileW(hdrop
, 0, szFile
, _countof(szFile
)))
632 hr
= InitFile(szFile
);
636 ACDBG(L
"Failed to query the file.\r\n");
641 ACDBG(L
"Invalid number of files: %d\r\n", uNumFiles
);
643 GlobalUnlock(stg
.hGlobal
);
647 ACDBG(L
"Could not lock stg.hGlobal\r\n");
649 ReleaseStgMedium(&stg
);