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-2017 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" },
37 { L
"Windows Server 2008 (SP1)", L
"WINSRV08SP1" },
38 { L
"Windows Vista", L
"VISTARTM" },
39 { L
"Windows Vista (SP1)", L
"VISTASP1" },
40 { L
"Windows Vista (SP2)", L
"VISTASP2" },
41 { L
"Windows 7", L
"WIN7RTM" },
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
[] = {
71 void ACDBG_FN(PCSTR FunctionName
, PCWSTR Format
, ...)
74 WCHAR
* Current
= Buffer
;
75 size_t Length
= _countof(Buffer
);
77 StringCchPrintfExW(Current
, Length
, &Current
, &Length
, STRSAFE_NULL_ON_FAILURE
, L
"[%-20S] ", FunctionName
);
79 va_start(ArgList
, Format
);
80 StringCchVPrintfExW(Current
, Length
, &Current
, &Length
, STRSAFE_NULL_ON_FAILURE
, Format
, ArgList
);
82 OutputDebugStringW(Buffer
);
85 #define ACDBG(fmt, ...) ACDBG_FN(__FUNCTION__, fmt, ##__VA_ARGS__ )
89 CLayerUIPropPage::CLayerUIPropPage()
90 : m_IsSfcProtected(FALSE
)
91 , m_AllowPermLayer(FALSE
)
92 , m_LayerQueryFlags(GPLK_USER
) /* TODO: When do we read from HKLM? */
95 , m_RegistryEnabledLayers(0)
100 CLayerUIPropPage::~CLayerUIPropPage()
104 HRESULT
CLayerUIPropPage::InitFile(PCWSTR Filename
)
106 CString ExpandedFilename
;
107 DWORD dwRequired
= ExpandEnvironmentStringsW(Filename
, NULL
, 0);
110 LPWSTR Buffer
= ExpandedFilename
.GetBuffer(dwRequired
);
111 DWORD dwReturned
= ExpandEnvironmentStringsW(Filename
, Buffer
, dwRequired
);
112 if (dwRequired
== dwReturned
)
114 ExpandedFilename
.ReleaseBufferSetLength(dwReturned
- 1);
115 ACDBG(L
"Expanded '%s' => '%s'\r\n", Filename
, (PCWSTR
)ExpandedFilename
);
119 ExpandedFilename
.ReleaseBufferSetLength(0);
120 ExpandedFilename
= Filename
;
121 ACDBG(L
"Failed during expansion '%s'\r\n", Filename
);
126 ACDBG(L
"Failed to expand '%s'\r\n", Filename
);
127 ExpandedFilename
= Filename
;
129 PCWSTR pwszExt
= PathFindExtensionW(ExpandedFilename
);
132 ACDBG(L
"Failed to find an extension: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
135 if (!wcsicmp(pwszExt
, L
".lnk"))
137 WCHAR Buffer
[MAX_PATH
];
138 if (!GetExeFromLnk(ExpandedFilename
, Buffer
, _countof(Buffer
)))
140 ACDBG(L
"Failed to read link target from: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
143 if (!wcsicmp(Buffer
, ExpandedFilename
))
145 ACDBG(L
"Link redirects to itself: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
148 return InitFile(Buffer
);
152 if (tmp
.GetEnvironmentVariable(L
"SystemRoot"))
154 tmp
+= L
"\\System32";
155 if (ExpandedFilename
.GetLength() >= tmp
.GetLength() &&
156 ExpandedFilename
.Left(tmp
.GetLength()).MakeLower() == tmp
.MakeLower())
158 ACDBG(L
"Ignoring System32: %s\r\n", (PCWSTR
)ExpandedFilename
);
161 tmp
.GetEnvironmentVariable(L
"SystemRoot");
163 if (ExpandedFilename
.GetLength() >= tmp
.GetLength() &&
164 ExpandedFilename
.Left(tmp
.GetLength()).MakeLower() == tmp
.MakeLower())
166 ACDBG(L
"Ignoring WinSxs: %s\r\n", (PCWSTR
)ExpandedFilename
);
171 for (size_t n
= 0; g_AllowedExtensions
[n
]; ++n
)
173 if (!wcsicmp(g_AllowedExtensions
[n
], pwszExt
))
175 m_Filename
= ExpandedFilename
;
176 ACDBG(L
"Got: %s\r\n", (PCWSTR
)ExpandedFilename
);
177 m_IsSfcProtected
= SfcIsFileProtected(NULL
, m_Filename
);
178 m_AllowPermLayer
= AllowPermLayer(ExpandedFilename
);
182 ACDBG(L
"Extension not included: '%s'\r\n", pwszExt
);
186 static BOOL
GetLayerInfo(PCWSTR Filename
, DWORD QueryFlags
, PDWORD OSMode
, PDWORD Enabledlayers
, CSimpleArray
<CString
>& customLayers
)
188 WCHAR wszLayers
[MAX_LAYER_LENGTH
] = { 0 };
189 DWORD dwBytes
= sizeof(wszLayers
);
191 *OSMode
= *Enabledlayers
= 0;
192 customLayers
.RemoveAll();
193 if (!SdbGetPermLayerKeys(Filename
, wszLayers
, &dwBytes
, QueryFlags
))
196 for (PWCHAR Layer
= wcstok(wszLayers
, L
" "); Layer
; Layer
= wcstok(NULL
, L
" "))
199 for (n
= 0; g_Layers
[n
].Name
; ++n
)
201 if (!wcsicmp(g_Layers
[n
].Name
, Layer
))
203 *Enabledlayers
|= (1<<n
);
207 /* Did we find it? */
208 if (g_Layers
[n
].Name
)
211 for (n
= 0; g_CompatModes
[n
].Name
; ++n
)
213 if (!wcsicmp(g_CompatModes
[n
].Name
, Layer
))
219 /* Did we find it? */
220 if (g_CompatModes
[n
].Name
)
223 /* Must be a 'custom' layer */
224 customLayers
.Add(Layer
);
229 int CLayerUIPropPage::OnSetActive()
231 if (!GetLayerInfo(m_Filename
, m_LayerQueryFlags
, &m_RegistryOSMode
, &m_RegistryEnabledLayers
, m_RegistryCustomLayers
))
232 m_RegistryOSMode
= m_RegistryEnabledLayers
= 0;
234 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
235 CheckDlgButton(g_Layers
[n
].Id
, (m_RegistryEnabledLayers
& (1<<n
)) ? BST_CHECKED
: BST_UNCHECKED
);
237 CheckDlgButton(IDC_CHKRUNCOMPATIBILITY
, m_RegistryOSMode
? BST_CHECKED
: BST_UNCHECKED
);
239 if (m_RegistryOSMode
)
240 ComboBox_SetCurSel(GetDlgItem(IDC_COMPATIBILITYMODE
), m_RegistryOSMode
-1);
242 m_CustomLayers
= m_RegistryCustomLayers
;
244 /* TODO: visualize 'custom' layers! */
252 static BOOL
ArrayEquals(const CSimpleArray
<CString
>& lhs
, const CSimpleArray
<CString
>& rhs
)
254 if (lhs
.GetSize() != rhs
.GetSize())
257 for (int n
= 0; n
< lhs
.GetSize(); ++n
)
259 if (lhs
[n
] != rhs
[n
])
265 BOOL
CLayerUIPropPage::HasChanges() const
267 if (m_RegistryEnabledLayers
!= m_EnabledLayers
)
270 if (m_RegistryOSMode
!= m_OSMode
)
273 if (!ArrayEquals(m_RegistryCustomLayers
, m_CustomLayers
))
279 int CLayerUIPropPage::OnApply()
283 BOOL bMachine
= m_LayerQueryFlags
== GPLK_MACHINE
;
285 for (size_t n
= 0; g_CompatModes
[n
].Name
; ++n
)
286 SetPermLayerState(m_Filename
, g_CompatModes
[n
].Name
, 0, bMachine
, (n
+1) == m_OSMode
);
288 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
290 SetPermLayerState(m_Filename
, g_Layers
[n
].Name
, 0, bMachine
, ((1<<n
) & m_EnabledLayers
) != 0);
293 /* Disable all old values */
294 for (int j
= 0; j
< m_RegistryCustomLayers
.GetSize(); j
++)
296 SetPermLayerState(m_Filename
, m_RegistryCustomLayers
[j
].GetString(), 0, bMachine
, FALSE
);
299 /* Enable all new values */
300 for (int j
= 0; j
< m_CustomLayers
.GetSize(); j
++)
302 SetPermLayerState(m_Filename
, m_CustomLayers
[j
].GetString(), 0, bMachine
, TRUE
);
305 SHChangeNotify(SHCNE_UPDATEITEM
, SHCNF_PATHW
, (PCWSTR
)m_Filename
, NULL
);
308 return PSNRET_NOERROR
;
311 LRESULT
CLayerUIPropPage::OnInitDialog(UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL
&bHandled
)
313 HWND cboMode
= GetDlgItem(IDC_COMPATIBILITYMODE
);
314 for (size_t n
= 0; g_CompatModes
[n
].Display
; ++n
)
315 ComboBox_AddString(cboMode
, g_CompatModes
[n
].Display
);
316 ComboBox_SetCurSel(cboMode
, 5);
318 CComBSTR explanation
;
319 if (!m_AllowPermLayer
)
321 explanation
.LoadString(g_hModule
, IDS_FAILED_NETWORK
);
323 ACDBG(L
"AllowPermLayer returned FALSE\r\n");
325 else if (m_IsSfcProtected
)
327 explanation
.LoadString(g_hModule
, IDS_FAILED_PROTECTED
);
329 ACDBG(L
"Protected OS file\r\n");
335 SetDlgItemTextW(IDC_EXPLANATION
, explanation
);
339 INT_PTR
CLayerUIPropPage::DisableControls()
341 ::EnableWindow(GetDlgItem(IDC_COMPATIBILITYMODE
), 0);
342 ::EnableWindow(GetDlgItem(IDC_CHKRUNCOMPATIBILITY
), 0);
343 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
344 ::EnableWindow(GetDlgItem(g_Layers
[n
].Id
), 0);
345 ::EnableWindow(GetDlgItem(IDC_EDITCOMPATIBILITYMODES
), 0);
349 void CLayerUIPropPage::UpdateControls()
351 m_OSMode
= 0, m_EnabledLayers
= 0;
352 BOOL ModeEnabled
= IsDlgButtonChecked(IDC_CHKRUNCOMPATIBILITY
);
354 m_OSMode
= ComboBox_GetCurSel(GetDlgItem(IDC_COMPATIBILITYMODE
))+1;
355 ::EnableWindow(GetDlgItem(IDC_COMPATIBILITYMODE
), ModeEnabled
);
357 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
359 m_EnabledLayers
|= IsDlgButtonChecked(g_Layers
[n
].Id
) ? (1<<n
) : 0;
360 ::ShowWindow(GetDlgItem(g_Layers
[n
].Id
), SW_SHOW
);
363 SetModified(HasChanges());
366 LRESULT
CLayerUIPropPage::OnCtrlCommand(WORD wNotifyCode
, WORD wID
, HWND hWndCtl
, BOOL
&bHandled
)
372 LRESULT
CLayerUIPropPage::OnEditModes(WORD wNotifyCode
, WORD wID
, HWND hWndCtl
, BOOL
&bHandled
)
374 if (DialogBoxParam(g_hModule
, MAKEINTRESOURCE(IDD_EDITCOMPATIBILITYMODES
), m_hWnd
, EditModesProc
, (LPARAM
)this) == IDOK
)
379 LRESULT
CLayerUIPropPage::OnClickNotify(INT uCode
, LPNMHDR hdr
, BOOL
& bHandled
)
381 if (hdr
->idFrom
== IDC_INFOLINK
)
382 ShellExecute(NULL
, L
"open", L
"https://www.reactos.org/forum/viewforum.php?f=4", NULL
, NULL
, SW_SHOW
);
386 static void ListboxChanged(HWND hWnd
)
388 int Sel
= ListBox_GetCurSel(GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
));
389 EnableWindow(GetDlgItem(hWnd
, IDC_EDIT
), Sel
>= 0);
390 EnableWindow(GetDlgItem(hWnd
, IDC_DELETE
), Sel
>= 0);
393 static void OnAdd(HWND hWnd
)
395 HWND Combo
= GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
397 int Length
= ComboBox_GetTextLength(Combo
);
398 CComBSTR
Str(Length
);
399 ComboBox_GetText(Combo
, Str
, Length
+1);
400 HWND List
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
401 int Index
= ListBox_FindStringExact(List
, -1, Str
);
403 Index
= ListBox_AddString(List
, Str
);
404 ListBox_SetCurSel(List
, Index
);
405 ListboxChanged(hWnd
);
406 ComboBox_SetCurSel(Combo
, -1);
410 static BOOL
ComboHasData(HWND hWnd
)
412 HWND Combo
= GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
413 if (ComboBox_GetCurSel(Combo
) >= 0)
415 ULONG Len
= ComboBox_GetTextLength(Combo
);
419 INT_PTR CALLBACK
CLayerUIPropPage::EditModesProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
421 CLayerUIPropPage
* page
= NULL
;
426 page
= (CLayerUIPropPage
*)lParam
;
428 ::SetProp(hWnd
, ACP_WNDPROP
, page
);
430 HWND Combo
= ::GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
431 CComObject
<CLayerStringList
> pList
;
435 CComHeapPtr
<OLECHAR
> str
;
436 HRESULT hr
= pList
.Next(1, &str
, NULL
);
439 ComboBox_AddString(Combo
, str
);
442 HWND List
= ::GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
443 for (int n
= 0; n
< page
->m_CustomLayers
.GetSize(); ++n
)
445 const WCHAR
* Str
= page
->m_CustomLayers
[n
].GetString();
446 int Index
= ListBox_FindStringExact(List
, -1, Str
);
448 Index
= ListBox_AddString(List
, Str
);
454 page
= (CLayerUIPropPage
*)::GetProp(hWnd
, ACP_WNDPROP
);
455 ::RemoveProp(hWnd
, ACP_WNDPROP
);
460 switch(LOWORD(wParam
))
467 HWND List
= ::GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
468 int Cur
= ListBox_GetCurSel(List
);
469 int Length
= ListBox_GetTextLen(List
, Cur
);
470 CComBSTR
Str(Length
);
471 ListBox_GetText(List
, Cur
, Str
);
472 ListBox_DeleteString(List
, Cur
);
473 HWND Combo
= ::GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
474 ComboBox_SetCurSel(Combo
, -1);
475 ::SetWindowText(Combo
,Str
);
476 ListboxChanged(hWnd
);
477 ComboBox_SetEditSel(Combo
, 30000, 30000);
483 HWND List
= ::GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
484 ListBox_DeleteString(List
, ListBox_GetCurSel(List
));
485 ListboxChanged(hWnd
);
488 case IDC_COMPATIBILITYMODE
:
489 ListboxChanged(hWnd
);
491 case IDC_NEWCOMPATIBILITYMODE
:
493 ::EnableWindow(::GetDlgItem(hWnd
, IDC_ADD
), ComboHasData(hWnd
));
497 /* Copy from list! */
499 if (ComboHasData(hWnd
))
501 CComBSTR question
, title
;
502 title
.LoadString(g_hModule
, IDS_TABTITLE
);
503 question
.LoadString(g_hModule
, IDS_YOU_DID_NOT_ADD
);
504 int result
= ::MessageBoxW(hWnd
, question
, title
, MB_YESNOCANCEL
| MB_ICONQUESTION
);
517 page
= (CLayerUIPropPage
*)::GetProp(hWnd
, ACP_WNDPROP
);
519 HWND List
= ::GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
520 int Count
= ListBox_GetCount(List
);
521 page
->m_CustomLayers
.RemoveAll();
522 for (int Cur
= 0; Cur
< Count
; ++Cur
)
524 int Length
= ListBox_GetTextLen(List
, Cur
);
526 LPWSTR Buffer
= Str
.GetBuffer(Length
+ 1);
527 ListBox_GetText(List
, Cur
, Buffer
);
528 Str
.ReleaseBuffer(Length
);
529 page
->m_CustomLayers
.Add(Str
);
534 ::EndDialog(hWnd
, LOWORD(wParam
));
539 ::EndDialog(hWnd
, IDCANCEL
);
545 static BOOL
DisableShellext()
548 LSTATUS ret
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, L
"SOFTWARE\\Policies\\Microsoft\\Windows\\AppCompat", 0, KEY_QUERY_VALUE
, &hkey
);
549 BOOL Disable
= FALSE
;
550 if (ret
== ERROR_SUCCESS
)
553 DWORD type
, size
= sizeof(dwValue
);
554 ret
= RegQueryValueExW(hkey
, L
"DisableEngine", NULL
, &type
, (PBYTE
)&dwValue
, &size
);
555 if (ret
== ERROR_SUCCESS
&& type
== REG_DWORD
)
561 size
= sizeof(dwValue
);
562 ret
= RegQueryValueExW(hkey
, L
"DisablePropPage", NULL
, &type
, (PBYTE
)&dwValue
, &size
);
563 if (ret
== ERROR_SUCCESS
&& type
== REG_DWORD
)
574 STDMETHODIMP
CLayerUIPropPage::Initialize(LPCITEMIDLIST pidlFolder
, LPDATAOBJECT pDataObj
, HKEY hkeyProgID
)
576 FORMATETC etc
= { CF_HDROP
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
579 if (DisableShellext())
580 return E_ACCESSDENIED
;
582 HRESULT hr
= pDataObj
->GetData(&etc
, &stg
);
585 ACDBG(L
"Failed to retrieve Data from pDataObj.\r\n");
589 HDROP hdrop
= (HDROP
)GlobalLock(stg
.hGlobal
);
592 UINT uNumFiles
= DragQueryFileW(hdrop
, 0xFFFFFFFF, NULL
, 0);
595 WCHAR szFile
[MAX_PATH
* 2];
596 if (DragQueryFileW(hdrop
, 0, szFile
, _countof(szFile
)))
599 hr
= InitFile(szFile
);
603 ACDBG(L
"Failed to query the file.\r\n");
608 ACDBG(L
"Invalid number of files: %d\r\n", uNumFiles
);
610 GlobalUnlock(stg
.hGlobal
);
614 ACDBG(L
"Could not lock stg.hGlobal\r\n");
616 ReleaseStgMedium(&stg
);