6584a1477824110bd5b8d058a04e73788a7cf8b3
[reactos.git] / reactos / dll / shellext / acppage / CLayerUIPropPage.cpp
1 /*
2 * Copyright 2015 Mark Jansen
3 *
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.
8 *
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.
13 *
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
17 */
18
19 #include "precomp.h"
20 #include <windowsx.h>
21 #include <sfc.h>
22
23 const GUID CLSID_CLayerUIPropPage = { 0x513D916F, 0x2A8E, 0x4F51, { 0xAE, 0xAB, 0x0C, 0xBC, 0x76, 0xFB, 0x1A, 0xF8 } };
24 #define ACP_WNDPROP L"{513D916F-2A8E-4F51-AEAB-0CBC76FB1AF8}.Prop"
25
26 #define GPLK_USER 1
27 #define GPLK_MACHINE 2
28 #define MAX_LAYER_LENGTH 256
29
30 void ACDBG_FN(PCSTR FunctionName, PCWSTR Format, ...)
31 {
32 WCHAR Buffer[512];
33 WCHAR* Current = Buffer;
34 size_t Length = _countof(Buffer);
35
36 StringCchPrintfExW(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, L"[%-20S] ", FunctionName);
37 va_list ArgList;
38 va_start(ArgList, Format);
39 StringCchVPrintfExW(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, Format, ArgList);
40 va_end(ArgList);
41 OutputDebugStringW(Buffer);
42 }
43
44 #define ACDBG(fmt, ...) ACDBG_FN(__FUNCTION__, fmt, ##__VA_ARGS__ )
45
46
47
48 CLayerUIPropPage::CLayerUIPropPage()
49 :m_Filename(NULL)
50 , m_IsSfcProtected(FALSE)
51 , m_AllowPermLayer(FALSE)
52 , m_LayerQueryFlags(GPLK_USER)
53 , m_RegistryOSMode(0)
54 , m_OSMode(0)
55 , m_RegistryEnabledLayers(0)
56 , m_EnabledLayers(0)
57 {
58 }
59
60 CLayerUIPropPage::~CLayerUIPropPage()
61 {
62 }
63
64
65 #if 0
66 HKCU\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers
67 WINXPSP3 256COLOR 640X480 DISABLETHEMES DISABLEDWM HIGHDPIAWARE RUNASADMIN
68 #endif
69
70 static struct {
71 const PCWSTR Display;
72 const PCWSTR Name;
73 } g_CompatModes[] = {
74 { L"Windows 95", L"WIN95" },
75 { L"Windows 98", L"WIN98" },
76 { L"Windows NT 4.0 (SP5)", L"NT4SP5" },
77 { L"Windows 2000", L"WIN2000" },
78 { L"Windows XP (SP2)", L"WINXPSP2" },
79 { L"Windows XP (SP3)", L"WINXPSP3" },
80 { L"Windows Server 2003 (SP1)", L"WINSRV03SP1" },
81 #if 0
82 { L"Windows Server 2008 (SP1)", L"WINSRV08SP1" },
83 { L"Windows Vista", L"VISTARTM" },
84 { L"Windows Vista (SP1)", L"VISTASP1" },
85 { L"Windows Vista (SP2)", L"VISTASP2" },
86 { L"Windows 7", L"WIN7RTM" },
87 #endif
88 { NULL, NULL }
89 };
90
91 static struct {
92 const PCWSTR Name;
93 DWORD Id;
94 BOOL Enabled;
95 } g_Layers[] = {
96 { L"256COLOR", IDC_CHKRUNIN256COLORS, TRUE },
97 { L"640X480", IDC_CHKRUNIN640480RES, TRUE },
98 { L"DISABLETHEMES", IDC_CHKDISABLEVISUALTHEMES, TRUE },
99 { NULL, 0, FALSE }
100 };
101
102 static const WCHAR* g_AllowedExtensions[] = {
103 L".exe",
104 L".msi",
105 L".pif",
106 L".bat",
107 L".cmd",
108 0
109 };
110
111 HRESULT CLayerUIPropPage::InitFile(PCWSTR Filename)
112 {
113 PCWSTR pwszExt = PathFindExtensionW(Filename);
114 if (!pwszExt)
115 {
116 ACDBG(L"Failed to find an extension: '%s'\r\n", Filename);
117 return E_FAIL;
118 }
119 if (!wcsicmp(pwszExt, L".lnk"))
120 {
121 WCHAR Buffer[MAX_PATH];
122 if (!GetExeFromLnk(Filename, Buffer, _countof(Buffer)))
123 {
124 ACDBG(L"Failed to read link target from: '%s'\r\n", Filename);
125 return E_FAIL;
126 }
127 if (!wcsicmp(Buffer, Filename))
128 {
129 ACDBG(L"Link redirects to itself: '%s'\r\n", Filename);
130 return E_FAIL;
131 }
132 return InitFile(Buffer);
133 }
134 for (size_t n = 0; g_AllowedExtensions[n]; ++n)
135 {
136 if (!wcsicmp(g_AllowedExtensions[n], pwszExt))
137 {
138 m_Filename = Filename;
139 ACDBG(L"Got: %s\r\n", Filename);
140 m_IsSfcProtected = SfcIsFileProtected(NULL, m_Filename);
141 m_AllowPermLayer = AllowPermLayer(Filename);
142 return S_OK;
143 }
144 }
145 ACDBG(L"Extension not included: '%s'\r\n", pwszExt);
146 return E_FAIL;
147 }
148
149 BOOL GetLayerInfo(BSTR Filename, DWORD QueryFlags, PDWORD OSMode, PDWORD Enabledlayers)
150 {
151 *OSMode = *Enabledlayers = 0;
152 WCHAR wszLayers[MAX_LAYER_LENGTH] = { 0 };
153 DWORD dwBytes = sizeof(wszLayers);
154 if (!SdbGetPermLayerKeys(Filename, wszLayers, &dwBytes, QueryFlags))
155 return FALSE;
156
157 for (PWCHAR Layer = wcstok(wszLayers, L" "); Layer; Layer = wcstok(NULL, L" "))
158 {
159 size_t n;
160 for (n = 0; g_Layers[n].Name; ++n)
161 {
162 if (g_Layers[n].Enabled && !wcsicmp(g_Layers[n].Name, Layer))
163 {
164 *Enabledlayers |= (1<<n);
165 break;
166 }
167 }
168 if (!g_Layers[n].Name)
169 {
170 for (n = 0; g_CompatModes[n].Name; ++n)
171 {
172 if (!wcsicmp(g_CompatModes[n].Name, Layer))
173 {
174 *OSMode = n+1;
175 break;
176 }
177 }
178 }
179 }
180 return TRUE;
181 }
182
183 void CLayerUIPropPage::OnRefresh(HWND hWnd)
184 {
185 if (!GetLayerInfo(m_Filename, m_LayerQueryFlags, &m_RegistryOSMode, &m_RegistryEnabledLayers))
186 m_RegistryOSMode = m_RegistryEnabledLayers = 0;
187
188 for (size_t n = 0; g_Layers[n].Name; ++n)
189 CheckDlgButton(hWnd, g_Layers[n].Id, (m_RegistryEnabledLayers & (1<<n)) ? BST_CHECKED : BST_UNCHECKED);
190 CheckDlgButton(hWnd, IDC_CHKRUNCOMPATIBILITY, m_RegistryOSMode ? BST_CHECKED : BST_UNCHECKED);
191 if (m_RegistryOSMode)
192 ComboBox_SetCurSel(GetDlgItem(hWnd, IDC_COMPATIBILITYMODE), m_RegistryOSMode-1);
193 UpdateControls(hWnd);
194 }
195
196 void CLayerUIPropPage::OnApply(HWND hWnd)
197 {
198 if (m_RegistryEnabledLayers != m_EnabledLayers || m_RegistryOSMode != m_OSMode)
199 {
200 BOOL bMachine = m_LayerQueryFlags == GPLK_MACHINE;
201 for (size_t n = 0; g_CompatModes[n].Name; ++n)
202 SetPermLayerState(m_Filename, g_CompatModes[n].Name, 0, bMachine, (n+1) == m_OSMode);
203 for (size_t n = 0; g_Layers[n].Name; ++n)
204 {
205 if (g_Layers[n].Enabled)
206 SetPermLayerState(m_Filename, g_Layers[n].Name, 0, bMachine, ((1<<n) & m_EnabledLayers) != 0);
207 }
208 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATHW, (BSTR)m_Filename, NULL);
209 }
210 }
211
212 INT_PTR CLayerUIPropPage::InitDialog(HWND hWnd)
213 {
214 HWND cboMode = GetDlgItem(hWnd, IDC_COMPATIBILITYMODE);
215 for (size_t n = 0; g_CompatModes[n].Display; ++n)
216 ComboBox_AddString(cboMode, g_CompatModes[n].Display);
217 ComboBox_SetCurSel(cboMode, 5);
218 EnableWindow(GetDlgItem(hWnd, IDC_EDITCOMPATIBILITYMODES), 0);
219
220 CComBSTR explanation;
221 if (!m_AllowPermLayer)
222 {
223 explanation.LoadString(g_hModule, IDS_FAILED_NETWORK);
224 DisableControls(hWnd);
225 ACDBG(L"AllowPermLayer returned FALSE\r\n");
226 }
227 else if (m_IsSfcProtected)
228 {
229 explanation.LoadString(g_hModule, IDS_FAILED_PROTECTED);
230 DisableControls(hWnd);
231 ACDBG(L"Protected OS file\r\n");
232 }
233 else
234 {
235 return TRUE;
236 }
237 SetDlgItemTextW(hWnd, IDC_EXPLANATION, explanation);
238 return TRUE;
239 }
240
241 INT_PTR CLayerUIPropPage::DisableControls(HWND hWnd)
242 {
243 EnableWindow(GetDlgItem(hWnd, IDC_COMPATIBILITYMODE), 0);
244 EnableWindow(GetDlgItem(hWnd, IDC_CHKRUNCOMPATIBILITY), 0);
245 for (size_t n = 0; g_Layers[n].Name; ++n)
246 EnableWindow(GetDlgItem(hWnd, g_Layers[n].Id), 0);
247 EnableWindow(GetDlgItem(hWnd, IDC_EDITCOMPATIBILITYMODES), 0);
248 return TRUE;
249 }
250
251 void CLayerUIPropPage::UpdateControls(HWND hWnd)
252 {
253 m_OSMode = 0, m_EnabledLayers = 0;
254 BOOL ModeEnabled = IsDlgButtonChecked(hWnd, IDC_CHKRUNCOMPATIBILITY);
255 if (ModeEnabled)
256 m_OSMode = ComboBox_GetCurSel(GetDlgItem(hWnd, IDC_COMPATIBILITYMODE))+1;
257 EnableWindow(GetDlgItem(hWnd, IDC_COMPATIBILITYMODE), ModeEnabled);
258 for (size_t n = 0; g_Layers[n].Name; ++n)
259 {
260 if (g_Layers[n].Enabled)
261 {
262 m_EnabledLayers |= IsDlgButtonChecked(hWnd, g_Layers[n].Id) ? (1<<n) : 0;
263 ShowWindow(GetDlgItem(hWnd, g_Layers[n].Id), SW_SHOW);
264 }
265 else
266 {
267 ShowWindow(GetDlgItem(hWnd, g_Layers[n].Id), SW_HIDE);
268 }
269 }
270 if (m_RegistryOSMode != m_OSMode || m_RegistryEnabledLayers != m_EnabledLayers)
271 {
272 PropSheet_Changed(GetParent(hWnd), hWnd);
273 }
274 else
275 {
276 PropSheet_UnChanged(GetParent(hWnd), hWnd);
277 }
278 }
279
280 INT_PTR CLayerUIPropPage::OnCommand(HWND hWnd, WORD id)
281 {
282 switch (id)
283 {
284 case IDC_CHKRUNCOMPATIBILITY:
285 UpdateControls(hWnd);
286 break;
287 case IDC_COMPATIBILITYMODE:
288 UpdateControls(hWnd);
289 break;
290 case IDC_CHKRUNIN256COLORS:
291 case IDC_CHKRUNIN640480RES:
292 case IDC_CHKDISABLEVISUALTHEMES:
293 UpdateControls(hWnd);
294 break;
295 case IDC_EDITCOMPATIBILITYMODES:
296 break;
297 }
298 return FALSE;
299 }
300
301 INT_PTR CALLBACK CLayerUIPropPage::PropDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
302 {
303 CLayerUIPropPage* page = NULL;
304
305 switch (uMsg)
306 {
307 case WM_INITDIALOG:
308 page = (CLayerUIPropPage*)((LPPROPSHEETPAGE)lParam)->lParam;
309 SetProp(hWnd, ACP_WNDPROP, page);
310 return page->InitDialog(hWnd);
311
312 case WM_ENDSESSION:
313 case WM_DESTROY:
314 page = (CLayerUIPropPage*)GetProp(hWnd, ACP_WNDPROP);
315 RemoveProp(hWnd, ACP_WNDPROP);
316 page->Release();
317 break;
318
319 case WM_COMMAND:
320 page = (CLayerUIPropPage*)GetProp(hWnd, ACP_WNDPROP);
321 return page->OnCommand(hWnd, LOWORD(wParam));
322 case WM_NOTIFY:
323 switch (((LPNMHDR)lParam)->code)
324 {
325 case PSN_SETACTIVE:
326 if (((LPNMHDR)lParam)->hwndFrom == GetParent(hWnd))
327 {
328 page = (CLayerUIPropPage*)GetProp(hWnd, ACP_WNDPROP);
329 page->OnRefresh(hWnd);
330 }
331 break;
332 case PSN_APPLY:
333 if (((LPNMHDR)lParam)->hwndFrom == GetParent(hWnd))
334 {
335 page = (CLayerUIPropPage*)GetProp(hWnd, ACP_WNDPROP);
336 page->OnApply(hWnd);
337 }
338 break;
339 case NM_CLICK:
340 case NM_RETURN:
341 if (((LPNMHDR)lParam)->idFrom == IDC_INFOLINK)
342 {
343 ShellExecute(NULL, L"open", L"https://www.reactos.org/forum/viewforum.php?f=4", NULL, NULL, SW_SHOW);
344 }
345 break;
346 default:
347 break;
348 }
349 break;
350 }
351
352 return FALSE;
353 }
354
355 STDMETHODIMP CLayerUIPropPage::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hkeyProgID)
356 {
357 FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
358 STGMEDIUM stg;
359 HRESULT hr = pDataObj->GetData(&etc, &stg);
360 if (FAILED(hr))
361 {
362 ACDBG(L"Failed to retrieve Data from pDataObj.\r\n");
363 return E_INVALIDARG;
364 }
365 hr = E_FAIL;
366 HDROP hdrop = (HDROP)GlobalLock(stg.hGlobal);
367 if (hdrop)
368 {
369 UINT uNumFiles = DragQueryFileW(hdrop, 0xFFFFFFFF, NULL, 0);
370 if (uNumFiles == 1)
371 {
372 WCHAR szFile[MAX_PATH * 2];
373 if (DragQueryFileW(hdrop, 0, szFile, _countof(szFile)))
374 {
375 this->AddRef();
376 hr = InitFile(szFile);
377 }
378 else
379 {
380 ACDBG(L"Failed to query the file.\r\n");
381 }
382 }
383 else
384 {
385 ACDBG(L"Invalid number of files: %d\r\n", uNumFiles);
386 }
387 GlobalUnlock(stg.hGlobal);
388 }
389 else
390 {
391 ACDBG(L"Could not lock stg.hGlobal\r\n");
392 }
393 ReleaseStgMedium(&stg);
394 return hr;
395 }
396
397 STDMETHODIMP CLayerUIPropPage::AddPages(LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
398 {
399 PROPSHEETPAGEW psp = { 0 };
400 psp.dwSize = sizeof(psp);
401 psp.dwFlags = PSP_USEREFPARENT | PSP_USETITLE;
402 psp.hInstance = g_hModule;
403 psp.pszTemplate = MAKEINTRESOURCE(IDD_ACPPAGESHEET);
404 psp.pszTitle = MAKEINTRESOURCE(IDS_TABTITLE);
405 psp.pfnDlgProc = PropDlgProc;
406 psp.lParam = (LPARAM)this;
407 psp.pcRefParent = (PUINT)&g_ModuleRefCnt;
408 HPROPSHEETPAGE hPage = CreatePropertySheetPageW(&psp);
409 if (hPage && !pfnAddPage(hPage, lParam))
410 DestroyPropertySheetPage(hPage);
411
412 return S_OK;
413 }
414