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 void CLayerUIPropPage::OnRefresh(HWND hWnd
)
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(hWnd
, g_Layers
[n
].Id
, (m_RegistryEnabledLayers
& (1<<n
)) ? BST_CHECKED
: BST_UNCHECKED
);
237 CheckDlgButton(hWnd
, IDC_CHKRUNCOMPATIBILITY
, m_RegistryOSMode
? BST_CHECKED
: BST_UNCHECKED
);
239 if (m_RegistryOSMode
)
240 ComboBox_SetCurSel(GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
), m_RegistryOSMode
-1);
242 m_CustomLayers
= m_RegistryCustomLayers
;
244 /* TODO: visualize 'custom' layers! */
246 UpdateControls(hWnd
);
250 static BOOL
ArrayEquals(const CSimpleArray
<CString
>& lhs
, const CSimpleArray
<CString
>& rhs
)
252 if (lhs
.GetSize() != rhs
.GetSize())
255 for (int n
= 0; n
< lhs
.GetSize(); ++n
)
257 if (lhs
[n
] != rhs
[n
])
263 BOOL
CLayerUIPropPage::HasChanges() const
265 if (m_RegistryEnabledLayers
!= m_EnabledLayers
)
268 if (m_RegistryOSMode
!= m_OSMode
)
271 if (!ArrayEquals(m_RegistryCustomLayers
, m_CustomLayers
))
277 void CLayerUIPropPage::OnApply(HWND hWnd
)
281 BOOL bMachine
= m_LayerQueryFlags
== GPLK_MACHINE
;
283 for (size_t n
= 0; g_CompatModes
[n
].Name
; ++n
)
284 SetPermLayerState(m_Filename
, g_CompatModes
[n
].Name
, 0, bMachine
, (n
+1) == m_OSMode
);
286 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
288 SetPermLayerState(m_Filename
, g_Layers
[n
].Name
, 0, bMachine
, ((1<<n
) & m_EnabledLayers
) != 0);
291 /* Disable all old values */
292 for (int j
= 0; j
< m_RegistryCustomLayers
.GetSize(); j
++)
294 SetPermLayerState(m_Filename
, m_RegistryCustomLayers
[j
].GetString(), 0, bMachine
, FALSE
);
297 /* Enable all new values */
298 for (int j
= 0; j
< m_CustomLayers
.GetSize(); j
++)
300 SetPermLayerState(m_Filename
, m_CustomLayers
[j
].GetString(), 0, bMachine
, TRUE
);
303 SHChangeNotify(SHCNE_UPDATEITEM
, SHCNF_PATHW
, (PCWSTR
)m_Filename
, NULL
);
307 INT_PTR
CLayerUIPropPage::InitDialog(HWND hWnd
)
309 HWND cboMode
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
310 for (size_t n
= 0; g_CompatModes
[n
].Display
; ++n
)
311 ComboBox_AddString(cboMode
, g_CompatModes
[n
].Display
);
312 ComboBox_SetCurSel(cboMode
, 5);
314 CComBSTR explanation
;
315 if (!m_AllowPermLayer
)
317 explanation
.LoadString(g_hModule
, IDS_FAILED_NETWORK
);
318 DisableControls(hWnd
);
319 ACDBG(L
"AllowPermLayer returned FALSE\r\n");
321 else if (m_IsSfcProtected
)
323 explanation
.LoadString(g_hModule
, IDS_FAILED_PROTECTED
);
324 DisableControls(hWnd
);
325 ACDBG(L
"Protected OS file\r\n");
331 SetDlgItemTextW(hWnd
, IDC_EXPLANATION
, explanation
);
335 INT_PTR
CLayerUIPropPage::DisableControls(HWND hWnd
)
337 EnableWindow(GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
), 0);
338 EnableWindow(GetDlgItem(hWnd
, IDC_CHKRUNCOMPATIBILITY
), 0);
339 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
340 EnableWindow(GetDlgItem(hWnd
, g_Layers
[n
].Id
), 0);
341 EnableWindow(GetDlgItem(hWnd
, IDC_EDITCOMPATIBILITYMODES
), 0);
345 void CLayerUIPropPage::UpdateControls(HWND hWnd
)
347 m_OSMode
= 0, m_EnabledLayers
= 0;
348 BOOL ModeEnabled
= IsDlgButtonChecked(hWnd
, IDC_CHKRUNCOMPATIBILITY
);
350 m_OSMode
= ComboBox_GetCurSel(GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
))+1;
351 EnableWindow(GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
), ModeEnabled
);
353 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
355 m_EnabledLayers
|= IsDlgButtonChecked(hWnd
, g_Layers
[n
].Id
) ? (1<<n
) : 0;
356 ShowWindow(GetDlgItem(hWnd
, g_Layers
[n
].Id
), SW_SHOW
);
361 PropSheet_Changed(GetParent(hWnd
), hWnd
);
365 PropSheet_UnChanged(GetParent(hWnd
), hWnd
);
369 INT_PTR
CLayerUIPropPage::OnCommand(HWND hWnd
, WORD id
)
373 case IDC_CHKRUNCOMPATIBILITY
:
374 UpdateControls(hWnd
);
376 case IDC_COMPATIBILITYMODE
:
377 UpdateControls(hWnd
);
379 case IDC_CHKRUNIN256COLORS
:
380 case IDC_CHKRUNIN640480RES
:
381 case IDC_CHKDISABLEVISUALTHEMES
:
382 UpdateControls(hWnd
);
384 case IDC_EDITCOMPATIBILITYMODES
:
385 if (DialogBoxParam(g_hModule
, MAKEINTRESOURCE(IDD_EDITCOMPATIBILITYMODES
), hWnd
, EditModesProc
, (LPARAM
)this) == IDOK
)
387 UpdateControls(hWnd
);
394 INT_PTR CALLBACK
CLayerUIPropPage::PropDlgProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
396 CLayerUIPropPage
* page
= NULL
;
401 page
= (CLayerUIPropPage
*)((LPPROPSHEETPAGE
)lParam
)->lParam
;
402 SetProp(hWnd
, ACP_WNDPROP
, page
);
403 return page
->InitDialog(hWnd
);
407 page
= (CLayerUIPropPage
*)GetProp(hWnd
, ACP_WNDPROP
);
408 RemoveProp(hWnd
, ACP_WNDPROP
);
413 page
= (CLayerUIPropPage
*)GetProp(hWnd
, ACP_WNDPROP
);
414 return page
->OnCommand(hWnd
, LOWORD(wParam
));
416 switch (((LPNMHDR
)lParam
)->code
)
419 if (((LPNMHDR
)lParam
)->hwndFrom
== GetParent(hWnd
))
421 page
= (CLayerUIPropPage
*)GetProp(hWnd
, ACP_WNDPROP
);
422 page
->OnRefresh(hWnd
);
426 if (((LPNMHDR
)lParam
)->hwndFrom
== GetParent(hWnd
))
428 page
= (CLayerUIPropPage
*)GetProp(hWnd
, ACP_WNDPROP
);
434 if (((LPNMHDR
)lParam
)->idFrom
== IDC_INFOLINK
)
436 ShellExecute(NULL
, L
"open", L
"https://www.reactos.org/forum/viewforum.php?f=4", NULL
, NULL
, SW_SHOW
);
448 static void ListboxChanged(HWND hWnd
)
450 int Sel
= ListBox_GetCurSel(GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
));
451 EnableWindow(GetDlgItem(hWnd
, IDC_EDIT
), Sel
>= 0);
452 EnableWindow(GetDlgItem(hWnd
, IDC_DELETE
), Sel
>= 0);
455 static void OnAdd(HWND hWnd
)
457 HWND Combo
= GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
459 int Length
= ComboBox_GetTextLength(Combo
);
460 CComBSTR
Str(Length
);
461 ComboBox_GetText(Combo
, Str
, Length
+1);
462 HWND List
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
463 int Index
= ListBox_FindStringExact(List
, -1, Str
);
465 Index
= ListBox_AddString(List
, Str
);
466 ListBox_SetCurSel(List
, Index
);
467 ListboxChanged(hWnd
);
468 ComboBox_SetCurSel(Combo
, -1);
472 static BOOL
ComboHasData(HWND hWnd
)
474 HWND Combo
= GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
475 if (ComboBox_GetCurSel(Combo
) >= 0)
477 ULONG Len
= ComboBox_GetTextLength(Combo
);
481 INT_PTR CALLBACK
CLayerUIPropPage::EditModesProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
483 CLayerUIPropPage
* page
= NULL
;
488 page
= (CLayerUIPropPage
*)lParam
;
490 SetProp(hWnd
, ACP_WNDPROP
, page
);
492 HWND Combo
= GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
493 CComObject
<CLayerStringList
> pList
;
497 CComHeapPtr
<OLECHAR
> str
;
498 HRESULT hr
= pList
.Next(1, &str
, NULL
);
501 ComboBox_AddString(Combo
, str
);
504 HWND List
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
505 for (int n
= 0; n
< page
->m_CustomLayers
.GetSize(); ++n
)
507 const WCHAR
* Str
= page
->m_CustomLayers
[n
].GetString();
508 int Index
= ListBox_FindStringExact(List
, -1, Str
);
510 Index
= ListBox_AddString(List
, Str
);
516 page
= (CLayerUIPropPage
*)GetProp(hWnd
, ACP_WNDPROP
);
517 RemoveProp(hWnd
, ACP_WNDPROP
);
522 switch(LOWORD(wParam
))
529 HWND List
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
530 int Cur
= ListBox_GetCurSel(List
);
531 int Length
= ListBox_GetTextLen(List
, Cur
);
532 CComBSTR
Str(Length
);
533 ListBox_GetText(List
, Cur
, Str
);
534 ListBox_DeleteString(List
, Cur
);
535 HWND Combo
= GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
536 ComboBox_SetCurSel(Combo
, -1);
537 ComboBox_SetText(Combo
, Str
);
538 ListboxChanged(hWnd
);
539 ComboBox_SetEditSel(Combo
, 30000, 30000);
545 HWND List
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
546 ListBox_DeleteString(List
, ListBox_GetCurSel(List
));
547 ListboxChanged(hWnd
);
550 case IDC_COMPATIBILITYMODE
:
551 ListboxChanged(hWnd
);
553 case IDC_NEWCOMPATIBILITYMODE
:
555 EnableWindow(GetDlgItem(hWnd
, IDC_ADD
), ComboHasData(hWnd
));
559 /* Copy from list! */
561 if (ComboHasData(hWnd
))
563 CComBSTR question
, title
;
564 title
.LoadString(g_hModule
, IDS_TABTITLE
);
565 question
.LoadString(g_hModule
, IDS_YOU_DID_NOT_ADD
);
566 int result
= MessageBoxW(hWnd
, question
, title
, MB_YESNOCANCEL
| MB_ICONQUESTION
);
579 page
= (CLayerUIPropPage
*)GetProp(hWnd
, ACP_WNDPROP
);
581 HWND List
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
582 int Count
= ListBox_GetCount(List
);
583 page
->m_CustomLayers
.RemoveAll();
584 for (int Cur
= 0; Cur
< Count
; ++Cur
)
586 int Length
= ListBox_GetTextLen(List
, Cur
);
588 LPWSTR Buffer
= Str
.GetBuffer(Length
+ 1);
589 ListBox_GetText(List
, Cur
, Buffer
);
590 Str
.ReleaseBuffer(Length
);
591 page
->m_CustomLayers
.Add(Str
);
596 EndDialog(hWnd
, LOWORD(wParam
));
601 EndDialog(hWnd
, IDCANCEL
);
607 static BOOL
DisableShellext()
610 LSTATUS ret
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, L
"SOFTWARE\\Policies\\Microsoft\\Windows\\AppCompat", 0, KEY_QUERY_VALUE
, &hkey
);
611 BOOL Disable
= FALSE
;
612 if (ret
== ERROR_SUCCESS
)
615 DWORD type
, size
= sizeof(dwValue
);
616 ret
= RegQueryValueExW(hkey
, L
"DisableEngine", NULL
, &type
, (PBYTE
)&dwValue
, &size
);
617 if (ret
== ERROR_SUCCESS
&& type
== REG_DWORD
)
623 size
= sizeof(dwValue
);
624 ret
= RegQueryValueExW(hkey
, L
"DisablePropPage", NULL
, &type
, (PBYTE
)&dwValue
, &size
);
625 if (ret
== ERROR_SUCCESS
&& type
== REG_DWORD
)
636 STDMETHODIMP
CLayerUIPropPage::Initialize(LPCITEMIDLIST pidlFolder
, LPDATAOBJECT pDataObj
, HKEY hkeyProgID
)
638 FORMATETC etc
= { CF_HDROP
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
641 if (DisableShellext())
642 return E_ACCESSDENIED
;
644 HRESULT hr
= pDataObj
->GetData(&etc
, &stg
);
647 ACDBG(L
"Failed to retrieve Data from pDataObj.\r\n");
651 HDROP hdrop
= (HDROP
)GlobalLock(stg
.hGlobal
);
654 UINT uNumFiles
= DragQueryFileW(hdrop
, 0xFFFFFFFF, NULL
, 0);
657 WCHAR szFile
[MAX_PATH
* 2];
658 if (DragQueryFileW(hdrop
, 0, szFile
, _countof(szFile
)))
661 hr
= InitFile(szFile
);
665 ACDBG(L
"Failed to query the file.\r\n");
670 ACDBG(L
"Invalid number of files: %d\r\n", uNumFiles
);
672 GlobalUnlock(stg
.hGlobal
);
676 ACDBG(L
"Could not lock stg.hGlobal\r\n");
678 ReleaseStgMedium(&stg
);
682 STDMETHODIMP
CLayerUIPropPage::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
)
684 PROPSHEETPAGEW psp
= { 0 };
685 psp
.dwSize
= sizeof(psp
);
686 psp
.dwFlags
= PSP_USEREFPARENT
| PSP_USETITLE
;
687 psp
.hInstance
= g_hModule
;
688 psp
.pszTemplate
= MAKEINTRESOURCE(IDD_ACPPAGESHEET
);
689 psp
.pszTitle
= MAKEINTRESOURCE(IDS_TABTITLE
);
690 psp
.pfnDlgProc
= PropDlgProc
;
691 psp
.lParam
= (LPARAM
)this;
692 psp
.pcRefParent
= (PUINT
)&g_ModuleRefCnt
;
693 HPROPSHEETPAGE hPage
= CreatePropertySheetPageW(&psp
);
694 if (hPage
&& !pfnAddPage(hPage
, lParam
))
695 DestroyPropertySheetPage(hPage
);