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