2 * Copyright 2015-2017 Mark Jansen (mark.jansen@reactos.org)
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include <shellutils.h>
29 const GUID CLSID_CLayerUIPropPage
= { 0x513D916F, 0x2A8E, 0x4F51, { 0xAE, 0xAB, 0x0C, 0xBC, 0x76, 0xFB, 0x1A, 0xF8 } };
30 #define ACP_WNDPROP L"{513D916F-2A8E-4F51-AEAB-0CBC76FB1AF8}.Prop"
33 #define GPLK_MACHINE 2
34 #define MAX_LAYER_LENGTH 256
40 { L
"Windows 95", L
"WIN95" },
41 { L
"Windows 98/ME", L
"WIN98" },
42 { L
"Windows NT 4.0 (SP5)", L
"NT4SP5" },
43 { L
"Windows 2000", L
"WIN2000" },
44 { L
"Windows XP (SP2)", L
"WINXPSP2" },
45 { L
"Windows XP (SP3)", L
"WINXPSP3" },
46 { L
"Windows Server 2003 (SP1)", L
"WINSRV03SP1" },
48 { L
"Windows Server 2008 (SP1)", L
"WINSRV08SP1" },
49 { L
"Windows Vista", L
"VISTARTM" },
50 { L
"Windows Vista (SP1)", L
"VISTASP1" },
51 { L
"Windows Vista (SP2)", L
"VISTASP2" },
52 { L
"Windows 7", L
"WIN7RTM" },
61 { L
"256COLOR", IDC_CHKRUNIN256COLORS
},
62 { L
"640X480", IDC_CHKRUNIN640480RES
},
63 { L
"DISABLETHEMES", IDC_CHKDISABLEVISUALTHEMES
},
65 { L
"DISABLEDWM", IDC_
??, TRUE
},
66 { L
"HIGHDPIAWARE", IDC_
??, TRUE
},
67 { L
"RUNASADMIN", IDC_
??, TRUE
},
72 static const WCHAR
* g_AllowedExtensions
[] = {
82 void ACDBG_FN(PCSTR FunctionName
, PCWSTR Format
, ...)
85 WCHAR
* Current
= Buffer
;
86 size_t Length
= _countof(Buffer
);
88 StringCchPrintfExW(Current
, Length
, &Current
, &Length
, STRSAFE_NULL_ON_FAILURE
, L
"[%-20S] ", FunctionName
);
90 va_start(ArgList
, Format
);
91 StringCchVPrintfExW(Current
, Length
, &Current
, &Length
, STRSAFE_NULL_ON_FAILURE
, Format
, ArgList
);
93 OutputDebugStringW(Buffer
);
96 #define ACDBG(fmt, ...) ACDBG_FN(__FUNCTION__, fmt, ##__VA_ARGS__ )
100 CLayerUIPropPage::CLayerUIPropPage()
101 : m_IsSfcProtected(FALSE
)
102 , m_AllowPermLayer(FALSE
)
103 , m_LayerQueryFlags(GPLK_USER
) /* TODO: When do we read from HKLM? */
104 , m_RegistryOSMode(0)
106 , m_RegistryEnabledLayers(0)
111 CLayerUIPropPage::~CLayerUIPropPage()
115 HRESULT
CLayerUIPropPage::InitFile(PCWSTR Filename
)
117 CString ExpandedFilename
;
118 DWORD dwRequired
= ExpandEnvironmentStringsW(Filename
, NULL
, 0);
121 LPWSTR Buffer
= ExpandedFilename
.GetBuffer(dwRequired
);
122 DWORD dwReturned
= ExpandEnvironmentStringsW(Filename
, Buffer
, dwRequired
);
123 if (dwRequired
== dwReturned
)
125 ExpandedFilename
.ReleaseBufferSetLength(dwReturned
- 1);
126 ACDBG(L
"Expanded '%s' => '%s'\r\n", Filename
, (PCWSTR
)ExpandedFilename
);
130 ExpandedFilename
.ReleaseBufferSetLength(0);
131 ExpandedFilename
= Filename
;
132 ACDBG(L
"Failed during expansion '%s'\r\n", Filename
);
137 ACDBG(L
"Failed to expand '%s'\r\n", Filename
);
138 ExpandedFilename
= Filename
;
140 PCWSTR pwszExt
= PathFindExtensionW(ExpandedFilename
);
143 ACDBG(L
"Failed to find an extension: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
146 if (!wcsicmp(pwszExt
, L
".lnk"))
148 WCHAR Buffer
[MAX_PATH
];
149 if (!GetExeFromLnk(ExpandedFilename
, Buffer
, _countof(Buffer
)))
151 ACDBG(L
"Failed to read link target from: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
154 if (!wcsicmp(Buffer
, ExpandedFilename
))
156 ACDBG(L
"Link redirects to itself: '%s'\r\n", (PCWSTR
)ExpandedFilename
);
159 return InitFile(Buffer
);
163 if (tmp
.GetEnvironmentVariable(L
"SystemRoot"))
165 tmp
+= L
"\\System32";
166 if (ExpandedFilename
.GetLength() >= tmp
.GetLength() &&
167 ExpandedFilename
.Left(tmp
.GetLength()).MakeLower() == tmp
.MakeLower())
169 ACDBG(L
"Ignoring System32: %s\r\n", (PCWSTR
)ExpandedFilename
);
172 tmp
.GetEnvironmentVariable(L
"SystemRoot");
174 if (ExpandedFilename
.GetLength() >= tmp
.GetLength() &&
175 ExpandedFilename
.Left(tmp
.GetLength()).MakeLower() == tmp
.MakeLower())
177 ACDBG(L
"Ignoring WinSxs: %s\r\n", (PCWSTR
)ExpandedFilename
);
182 for (size_t n
= 0; g_AllowedExtensions
[n
]; ++n
)
184 if (!wcsicmp(g_AllowedExtensions
[n
], pwszExt
))
186 m_Filename
= ExpandedFilename
;
187 ACDBG(L
"Got: %s\r\n", (PCWSTR
)ExpandedFilename
);
188 m_IsSfcProtected
= SfcIsFileProtected(NULL
, m_Filename
);
189 m_AllowPermLayer
= AllowPermLayer(ExpandedFilename
);
193 ACDBG(L
"Extension not included: '%s'\r\n", pwszExt
);
197 static BOOL
GetLayerInfo(PCWSTR Filename
, DWORD QueryFlags
, PDWORD OSMode
, PDWORD Enabledlayers
, CSimpleArray
<CString
>& customLayers
)
199 WCHAR wszLayers
[MAX_LAYER_LENGTH
] = { 0 };
200 DWORD dwBytes
= sizeof(wszLayers
);
202 *OSMode
= *Enabledlayers
= 0;
203 customLayers
.RemoveAll();
204 if (!SdbGetPermLayerKeys(Filename
, wszLayers
, &dwBytes
, QueryFlags
))
207 for (PWCHAR Layer
= wcstok(wszLayers
, L
" "); Layer
; Layer
= wcstok(NULL
, L
" "))
210 for (n
= 0; g_Layers
[n
].Name
; ++n
)
212 if (!wcsicmp(g_Layers
[n
].Name
, Layer
))
214 *Enabledlayers
|= (1<<n
);
218 /* Did we find it? */
219 if (g_Layers
[n
].Name
)
222 for (n
= 0; g_CompatModes
[n
].Name
; ++n
)
224 if (!wcsicmp(g_CompatModes
[n
].Name
, Layer
))
230 /* Did we find it? */
231 if (g_CompatModes
[n
].Name
)
234 /* Must be a 'custom' layer */
235 customLayers
.Add(Layer
);
240 void CLayerUIPropPage::OnRefresh(HWND hWnd
)
242 if (!GetLayerInfo(m_Filename
, m_LayerQueryFlags
, &m_RegistryOSMode
, &m_RegistryEnabledLayers
, m_RegistryCustomLayers
))
243 m_RegistryOSMode
= m_RegistryEnabledLayers
= 0;
245 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
246 CheckDlgButton(hWnd
, g_Layers
[n
].Id
, (m_RegistryEnabledLayers
& (1<<n
)) ? BST_CHECKED
: BST_UNCHECKED
);
248 CheckDlgButton(hWnd
, IDC_CHKRUNCOMPATIBILITY
, m_RegistryOSMode
? BST_CHECKED
: BST_UNCHECKED
);
250 if (m_RegistryOSMode
)
251 ComboBox_SetCurSel(GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
), m_RegistryOSMode
-1);
253 m_CustomLayers
= m_RegistryCustomLayers
;
255 /* TODO: visualize 'custom' layers! */
257 UpdateControls(hWnd
);
261 static BOOL
ArrayEquals(const CSimpleArray
<CString
>& lhs
, const CSimpleArray
<CString
>& rhs
)
263 if (lhs
.GetSize() != rhs
.GetSize())
266 for (int n
= 0; n
< lhs
.GetSize(); ++n
)
268 if (lhs
[n
] != rhs
[n
])
274 BOOL
CLayerUIPropPage::HasChanges() const
276 if (m_RegistryEnabledLayers
!= m_EnabledLayers
)
279 if (m_RegistryOSMode
!= m_OSMode
)
282 if (!ArrayEquals(m_RegistryCustomLayers
, m_CustomLayers
))
288 void CLayerUIPropPage::OnApply(HWND hWnd
)
292 BOOL bMachine
= m_LayerQueryFlags
== GPLK_MACHINE
;
294 for (size_t n
= 0; g_CompatModes
[n
].Name
; ++n
)
295 SetPermLayerState(m_Filename
, g_CompatModes
[n
].Name
, 0, bMachine
, (n
+1) == m_OSMode
);
297 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
299 SetPermLayerState(m_Filename
, g_Layers
[n
].Name
, 0, bMachine
, ((1<<n
) & m_EnabledLayers
) != 0);
302 /* Disable all old values */
303 for (int j
= 0; j
< m_RegistryCustomLayers
.GetSize(); j
++)
305 SetPermLayerState(m_Filename
, m_RegistryCustomLayers
[j
].GetString(), 0, bMachine
, FALSE
);
308 /* Enable all new values */
309 for (int j
= 0; j
< m_CustomLayers
.GetSize(); j
++)
311 SetPermLayerState(m_Filename
, m_CustomLayers
[j
].GetString(), 0, bMachine
, TRUE
);
314 SHChangeNotify(SHCNE_UPDATEITEM
, SHCNF_PATHW
, (PCWSTR
)m_Filename
, NULL
);
318 INT_PTR
CLayerUIPropPage::InitDialog(HWND hWnd
)
320 HWND cboMode
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
321 for (size_t n
= 0; g_CompatModes
[n
].Display
; ++n
)
322 ComboBox_AddString(cboMode
, g_CompatModes
[n
].Display
);
323 ComboBox_SetCurSel(cboMode
, 5);
325 CComBSTR explanation
;
326 if (!m_AllowPermLayer
)
328 explanation
.LoadString(g_hModule
, IDS_FAILED_NETWORK
);
329 DisableControls(hWnd
);
330 ACDBG(L
"AllowPermLayer returned FALSE\r\n");
332 else if (m_IsSfcProtected
)
334 explanation
.LoadString(g_hModule
, IDS_FAILED_PROTECTED
);
335 DisableControls(hWnd
);
336 ACDBG(L
"Protected OS file\r\n");
342 SetDlgItemTextW(hWnd
, IDC_EXPLANATION
, explanation
);
346 INT_PTR
CLayerUIPropPage::DisableControls(HWND hWnd
)
348 EnableWindow(GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
), 0);
349 EnableWindow(GetDlgItem(hWnd
, IDC_CHKRUNCOMPATIBILITY
), 0);
350 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
351 EnableWindow(GetDlgItem(hWnd
, g_Layers
[n
].Id
), 0);
352 EnableWindow(GetDlgItem(hWnd
, IDC_EDITCOMPATIBILITYMODES
), 0);
356 void CLayerUIPropPage::UpdateControls(HWND hWnd
)
358 m_OSMode
= 0, m_EnabledLayers
= 0;
359 BOOL ModeEnabled
= IsDlgButtonChecked(hWnd
, IDC_CHKRUNCOMPATIBILITY
);
361 m_OSMode
= ComboBox_GetCurSel(GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
))+1;
362 EnableWindow(GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
), ModeEnabled
);
364 for (size_t n
= 0; g_Layers
[n
].Name
; ++n
)
366 m_EnabledLayers
|= IsDlgButtonChecked(hWnd
, g_Layers
[n
].Id
) ? (1<<n
) : 0;
367 ShowWindow(GetDlgItem(hWnd
, g_Layers
[n
].Id
), SW_SHOW
);
372 PropSheet_Changed(GetParent(hWnd
), hWnd
);
376 PropSheet_UnChanged(GetParent(hWnd
), hWnd
);
380 INT_PTR
CLayerUIPropPage::OnCommand(HWND hWnd
, WORD id
)
384 case IDC_CHKRUNCOMPATIBILITY
:
385 UpdateControls(hWnd
);
387 case IDC_COMPATIBILITYMODE
:
388 UpdateControls(hWnd
);
390 case IDC_CHKRUNIN256COLORS
:
391 case IDC_CHKRUNIN640480RES
:
392 case IDC_CHKDISABLEVISUALTHEMES
:
393 UpdateControls(hWnd
);
395 case IDC_EDITCOMPATIBILITYMODES
:
396 if (DialogBoxParam(g_hModule
, MAKEINTRESOURCE(IDD_EDITCOMPATIBILITYMODES
), hWnd
, EditModesProc
, (LPARAM
)this) == IDOK
)
398 UpdateControls(hWnd
);
405 INT_PTR CALLBACK
CLayerUIPropPage::PropDlgProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
407 CLayerUIPropPage
* page
= NULL
;
412 page
= (CLayerUIPropPage
*)((LPPROPSHEETPAGE
)lParam
)->lParam
;
413 SetProp(hWnd
, ACP_WNDPROP
, page
);
414 return page
->InitDialog(hWnd
);
418 page
= (CLayerUIPropPage
*)GetProp(hWnd
, ACP_WNDPROP
);
419 RemoveProp(hWnd
, ACP_WNDPROP
);
424 page
= (CLayerUIPropPage
*)GetProp(hWnd
, ACP_WNDPROP
);
425 return page
->OnCommand(hWnd
, LOWORD(wParam
));
427 switch (((LPNMHDR
)lParam
)->code
)
430 if (((LPNMHDR
)lParam
)->hwndFrom
== GetParent(hWnd
))
432 page
= (CLayerUIPropPage
*)GetProp(hWnd
, ACP_WNDPROP
);
433 page
->OnRefresh(hWnd
);
437 if (((LPNMHDR
)lParam
)->hwndFrom
== GetParent(hWnd
))
439 page
= (CLayerUIPropPage
*)GetProp(hWnd
, ACP_WNDPROP
);
445 if (((LPNMHDR
)lParam
)->idFrom
== IDC_INFOLINK
)
447 ShellExecute(NULL
, L
"open", L
"https://www.reactos.org/forum/viewforum.php?f=4", NULL
, NULL
, SW_SHOW
);
459 static void ListboxChanged(HWND hWnd
)
461 int Sel
= ListBox_GetCurSel(GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
));
462 EnableWindow(GetDlgItem(hWnd
, IDC_EDIT
), Sel
>= 0);
463 EnableWindow(GetDlgItem(hWnd
, IDC_DELETE
), Sel
>= 0);
466 static void OnAdd(HWND hWnd
)
468 HWND Combo
= GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
470 int Length
= ComboBox_GetTextLength(Combo
);
471 CComBSTR
Str(Length
);
472 ComboBox_GetText(Combo
, Str
, Length
+1);
473 HWND List
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
474 int Index
= ListBox_FindStringExact(List
, -1, Str
);
476 Index
= ListBox_AddString(List
, Str
);
477 ListBox_SetCurSel(List
, Index
);
478 ListboxChanged(hWnd
);
479 ComboBox_SetCurSel(Combo
, -1);
483 static BOOL
ComboHasData(HWND hWnd
)
485 HWND Combo
= GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
486 if (ComboBox_GetCurSel(Combo
) >= 0)
488 ULONG Len
= ComboBox_GetTextLength(Combo
);
492 INT_PTR CALLBACK
CLayerUIPropPage::EditModesProc(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
494 CLayerUIPropPage
* page
= NULL
;
499 page
= (CLayerUIPropPage
*)lParam
;
501 SetProp(hWnd
, ACP_WNDPROP
, page
);
503 HWND Combo
= GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
504 CComObject
<CLayerStringList
> pList
;
509 HRESULT hr
= pList
.Next(1, &str
, NULL
);
512 ComboBox_AddString(Combo
, str
);
515 HWND List
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
516 for (int n
= 0; n
< page
->m_CustomLayers
.GetSize(); ++n
)
518 const WCHAR
* Str
= page
->m_CustomLayers
[n
].GetString();
519 int Index
= ListBox_FindStringExact(List
, -1, Str
);
521 Index
= ListBox_AddString(List
, Str
);
527 page
= (CLayerUIPropPage
*)GetProp(hWnd
, ACP_WNDPROP
);
528 RemoveProp(hWnd
, ACP_WNDPROP
);
533 switch(LOWORD(wParam
))
540 HWND List
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
541 int Cur
= ListBox_GetCurSel(List
);
542 int Length
= ListBox_GetTextLen(List
, Cur
);
543 CComBSTR
Str(Length
);
544 ListBox_GetText(List
, Cur
, Str
);
545 ListBox_DeleteString(List
, Cur
);
546 HWND Combo
= GetDlgItem(hWnd
, IDC_NEWCOMPATIBILITYMODE
);
547 ComboBox_SetCurSel(Combo
, -1);
548 ComboBox_SetText(Combo
, Str
);
549 ListboxChanged(hWnd
);
550 ComboBox_SetEditSel(Combo
, 30000, 30000);
556 HWND List
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
557 ListBox_DeleteString(List
, ListBox_GetCurSel(List
));
558 ListboxChanged(hWnd
);
561 case IDC_COMPATIBILITYMODE
:
562 ListboxChanged(hWnd
);
564 case IDC_NEWCOMPATIBILITYMODE
:
566 EnableWindow(GetDlgItem(hWnd
, IDC_ADD
), ComboHasData(hWnd
));
570 /* Copy from list! */
572 if (ComboHasData(hWnd
))
574 CComBSTR question
, title
;
575 title
.LoadString(g_hModule
, IDS_TABTITLE
);
576 question
.LoadString(g_hModule
, IDS_YOU_DID_NOT_ADD
);
577 int result
= MessageBoxW(hWnd
, question
, title
, MB_YESNOCANCEL
| MB_ICONQUESTION
);
590 page
= (CLayerUIPropPage
*)GetProp(hWnd
, ACP_WNDPROP
);
592 HWND List
= GetDlgItem(hWnd
, IDC_COMPATIBILITYMODE
);
593 int Count
= ListBox_GetCount(List
);
594 page
->m_CustomLayers
.RemoveAll();
595 for (int Cur
= 0; Cur
< Count
; ++Cur
)
597 int Length
= ListBox_GetTextLen(List
, Cur
);
599 LPWSTR Buffer
= Str
.GetBuffer(Length
+ 1);
600 ListBox_GetText(List
, Cur
, Buffer
);
601 Str
.ReleaseBuffer(Length
);
602 page
->m_CustomLayers
.Add(Str
);
607 EndDialog(hWnd
, LOWORD(wParam
));
612 EndDialog(hWnd
, IDCANCEL
);
618 static BOOL
DisableShellext()
621 LSTATUS ret
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, L
"SOFTWARE\\Policies\\Microsoft\\Windows\\AppCompat", 0, KEY_QUERY_VALUE
, &hkey
);
622 BOOL Disable
= FALSE
;
623 if (ret
== ERROR_SUCCESS
)
626 DWORD type
, size
= sizeof(dwValue
);
627 ret
= RegQueryValueExW(hkey
, L
"DisableEngine", NULL
, &type
, (PBYTE
)&dwValue
, &size
);
628 if (ret
== ERROR_SUCCESS
&& type
== REG_DWORD
)
634 size
= sizeof(dwValue
);
635 ret
= RegQueryValueExW(hkey
, L
"DisablePropPage", NULL
, &type
, (PBYTE
)&dwValue
, &size
);
636 if (ret
== ERROR_SUCCESS
&& type
== REG_DWORD
)
647 STDMETHODIMP
CLayerUIPropPage::Initialize(LPCITEMIDLIST pidlFolder
, LPDATAOBJECT pDataObj
, HKEY hkeyProgID
)
649 FORMATETC etc
= { CF_HDROP
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
652 if (DisableShellext())
653 return E_ACCESSDENIED
;
655 HRESULT hr
= pDataObj
->GetData(&etc
, &stg
);
658 ACDBG(L
"Failed to retrieve Data from pDataObj.\r\n");
662 HDROP hdrop
= (HDROP
)GlobalLock(stg
.hGlobal
);
665 UINT uNumFiles
= DragQueryFileW(hdrop
, 0xFFFFFFFF, NULL
, 0);
668 WCHAR szFile
[MAX_PATH
* 2];
669 if (DragQueryFileW(hdrop
, 0, szFile
, _countof(szFile
)))
672 hr
= InitFile(szFile
);
676 ACDBG(L
"Failed to query the file.\r\n");
681 ACDBG(L
"Invalid number of files: %d\r\n", uNumFiles
);
683 GlobalUnlock(stg
.hGlobal
);
687 ACDBG(L
"Could not lock stg.hGlobal\r\n");
689 ReleaseStgMedium(&stg
);
693 STDMETHODIMP
CLayerUIPropPage::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage
, LPARAM lParam
)
695 PROPSHEETPAGEW psp
= { 0 };
696 psp
.dwSize
= sizeof(psp
);
697 psp
.dwFlags
= PSP_USEREFPARENT
| PSP_USETITLE
;
698 psp
.hInstance
= g_hModule
;
699 psp
.pszTemplate
= MAKEINTRESOURCE(IDD_ACPPAGESHEET
);
700 psp
.pszTitle
= MAKEINTRESOURCE(IDS_TABTITLE
);
701 psp
.pfnDlgProc
= PropDlgProc
;
702 psp
.lParam
= (LPARAM
)this;
703 psp
.pcRefParent
= (PUINT
)&g_ModuleRefCnt
;
704 HPROPSHEETPAGE hPage
= CreatePropertySheetPageW(&psp
);
705 if (hPage
&& !pfnAddPage(hPage
, lParam
))
706 DestroyPropertySheetPage(hPage
);