1 /*
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-2018 Mark Jansen (mark.jansen@reactos.org)
6  */
7 
8 #include "precomp.h"
9 
10 #include <shlwapi.h>
11 #include <shellapi.h>
12 #include <shellutils.h>
13 #include <strsafe.h>
14 #include <apphelp.h>
15 #include <windowsx.h>
16 #include <sfc.h>
17 
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"
20 
21 #define GPLK_USER 1
22 #define GPLK_MACHINE 2
23 #define MAX_LAYER_LENGTH 256
24 
25 static struct {
26     const PCWSTR Display;
27     const PCWSTR Name;
28 } g_CompatModes[] = {
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" },
36     { L"Windows Server 2008 (SP1)", L"WINSRV08SP1" },
37     { L"Windows Vista", L"VISTARTM" },
38     { L"Windows Vista (SP1)", L"VISTASP1" },
39     { L"Windows Vista (SP2)", L"VISTASP2" },
40     { L"Windows 7", L"WIN7RTM" },
41     { L"Windows 7 (SP1)", L"WIN7SP1" },
42     { L"Windows 8", L"WIN8RTM" },
43     { L"Windows 8.1", L"WIN81RTM" },
44     { NULL, NULL }
45 };
46 
47 static struct {
48     const PCWSTR Name;
49     DWORD Id;
50 } g_Layers[] = {
51     { L"256COLOR", IDC_CHKRUNIN256COLORS },
52     { L"640X480", IDC_CHKRUNIN640480RES },
53     { L"DISABLETHEMES", IDC_CHKDISABLEVISUALTHEMES },
54 #if 0
55     { L"DISABLEDWM", IDC_??, TRUE },
56     { L"HIGHDPIAWARE", IDC_??, TRUE },
57     { L"RUNASADMIN", IDC_??, TRUE },
58 #endif
59     { NULL, 0 }
60 };
61 
62 static const WCHAR* g_AllowedExtensions[] = {
63     L".exe",
64     L".msi",
65     L".pif",
66     L".bat",
67     L".cmd",
68     0
69 };
70 
71 BOOL IsBuiltinLayer(PCWSTR Name)
72 {
73     size_t n;
74 
75     for (n = 0; g_Layers[n].Name; ++n)
76     {
77         if (!wcsicmp(g_Layers[n].Name, Name))
78         {
79             return TRUE;
80         }
81     }
82 
83     for (n = 0; g_CompatModes[n].Name; ++n)
84     {
85         if (!wcsicmp(g_CompatModes[n].Name, Name))
86         {
87             return TRUE;
88         }
89     }
90     return FALSE;
91 }
92 
93 
94 void ACDBG_FN(PCSTR FunctionName, PCWSTR Format, ...)
95 {
96     WCHAR Buffer[512];
97     WCHAR* Current = Buffer;
98     size_t Length = _countof(Buffer);
99 
100     StringCchPrintfExW(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, L"[%-20S] ", FunctionName);
101     va_list ArgList;
102     va_start(ArgList, Format);
103     StringCchVPrintfExW(Current, Length, &Current, &Length, STRSAFE_NULL_ON_FAILURE, Format, ArgList);
104     va_end(ArgList);
105     OutputDebugStringW(Buffer);
106 }
107 
108 #define ACDBG(fmt, ...)  ACDBG_FN(__FUNCTION__, fmt, ##__VA_ARGS__ )
109 
110 
111 
112 CLayerUIPropPage::CLayerUIPropPage()
113 : m_IsSfcProtected(FALSE)
114 , m_AllowPermLayer(FALSE)
115 , m_LayerQueryFlags(GPLK_USER)  /* TODO: When do we read from HKLM? */
116 , m_RegistryOSMode(0)
117 , m_OSMode(0)
118 , m_RegistryEnabledLayers(0)
119 , m_EnabledLayers(0)
120 {
121     CComBSTR title;
122     title.LoadString(g_hModule, IDS_COMPAT_TITLE);
123     m_psp.pszTitle = title.Detach();
124     m_psp.dwFlags |= PSP_USETITLE;
125 }
126 
127 CLayerUIPropPage::~CLayerUIPropPage()
128 {
129     CComBSTR title;
130     title.Attach((BSTR)m_psp.pszTitle);
131 }
132 
133 HRESULT CLayerUIPropPage::InitFile(PCWSTR Filename)
134 {
135     CString ExpandedFilename;
136     DWORD dwRequired = ExpandEnvironmentStringsW(Filename, NULL, 0);
137     if (dwRequired > 0)
138     {
139         LPWSTR Buffer = ExpandedFilename.GetBuffer(dwRequired);
140         DWORD dwReturned = ExpandEnvironmentStringsW(Filename, Buffer, dwRequired);
141         if (dwRequired == dwReturned)
142         {
143             ExpandedFilename.ReleaseBufferSetLength(dwReturned - 1);
144             ACDBG(L"Expanded '%s' => '%s'\r\n", Filename, (PCWSTR)ExpandedFilename);
145         }
146         else
147         {
148             ExpandedFilename.ReleaseBufferSetLength(0);
149             ExpandedFilename = Filename;
150             ACDBG(L"Failed during expansion '%s'\r\n", Filename);
151         }
152     }
153     else
154     {
155         ACDBG(L"Failed to expand '%s'\r\n", Filename);
156         ExpandedFilename = Filename;
157     }
158     PCWSTR pwszExt = PathFindExtensionW(ExpandedFilename);
159     if (!pwszExt)
160     {
161         ACDBG(L"Failed to find an extension: '%s'\r\n", (PCWSTR)ExpandedFilename);
162         return E_FAIL;
163     }
164     if (!wcsicmp(pwszExt, L".lnk"))
165     {
166         WCHAR Buffer[MAX_PATH];
167         if (!GetExeFromLnk(ExpandedFilename, Buffer, _countof(Buffer)))
168         {
169             ACDBG(L"Failed to read link target from: '%s'\r\n", (PCWSTR)ExpandedFilename);
170             return E_FAIL;
171         }
172         if (!wcsicmp(Buffer, ExpandedFilename))
173         {
174             ACDBG(L"Link redirects to itself: '%s'\r\n", (PCWSTR)ExpandedFilename);
175             return E_FAIL;
176         }
177         return InitFile(Buffer);
178     }
179 
180     CString tmp;
181     if (tmp.GetEnvironmentVariable(L"SystemRoot"))
182     {
183         tmp += L"\\System32";
184         if (ExpandedFilename.GetLength() >= tmp.GetLength() &&
185             ExpandedFilename.Left(tmp.GetLength()).MakeLower() == tmp.MakeLower())
186         {
187             ACDBG(L"Ignoring System32: %s\r\n", (PCWSTR)ExpandedFilename);
188             return E_FAIL;
189         }
190         tmp.GetEnvironmentVariable(L"SystemRoot");
191         tmp += L"\\WinSxs";
192         if (ExpandedFilename.GetLength() >= tmp.GetLength() &&
193             ExpandedFilename.Left(tmp.GetLength()).MakeLower() == tmp.MakeLower())
194         {
195             ACDBG(L"Ignoring WinSxs: %s\r\n", (PCWSTR)ExpandedFilename);
196             return E_FAIL;
197         }
198     }
199 
200     for (size_t n = 0; g_AllowedExtensions[n]; ++n)
201     {
202         if (!wcsicmp(g_AllowedExtensions[n], pwszExt))
203         {
204             m_Filename = ExpandedFilename;
205             ACDBG(L"Got: %s\r\n", (PCWSTR)ExpandedFilename);
206             m_IsSfcProtected = SfcIsFileProtected(NULL, m_Filename);
207             m_AllowPermLayer = AllowPermLayer(ExpandedFilename);
208             return S_OK;
209         }
210     }
211     ACDBG(L"Extension not included: '%s'\r\n", pwszExt);
212     return E_FAIL;
213 }
214 
215 static BOOL GetLayerInfo(PCWSTR Filename, DWORD QueryFlags, PDWORD OSMode, PDWORD Enabledlayers, CSimpleArray<CString>& customLayers)
216 {
217     WCHAR wszLayers[MAX_LAYER_LENGTH] = { 0 };
218     DWORD dwBytes = sizeof(wszLayers);
219 
220     *OSMode = *Enabledlayers = 0;
221     customLayers.RemoveAll();
222     if (!SdbGetPermLayerKeys(Filename, wszLayers, &dwBytes, QueryFlags))
223         return FALSE;
224 
225     for (PWCHAR Layer = wcstok(wszLayers, L" "); Layer; Layer = wcstok(NULL, L" "))
226     {
227         size_t n;
228         for (n = 0; g_Layers[n].Name; ++n)
229         {
230             if (!wcsicmp(g_Layers[n].Name, Layer))
231             {
232                 *Enabledlayers |= (1<<n);
233                 break;
234             }
235         }
236         /* Did we find it? */
237         if (g_Layers[n].Name)
238             continue;
239 
240         for (n = 0; g_CompatModes[n].Name; ++n)
241         {
242             if (!wcsicmp(g_CompatModes[n].Name, Layer))
243             {
244                 *OSMode = n+1;
245                 break;
246             }
247         }
248         /* Did we find it? */
249         if (g_CompatModes[n].Name)
250             continue;
251 
252         /* Must be a 'custom' layer */
253         customLayers.Add(Layer);
254     }
255     return TRUE;
256 }
257 
258 int CLayerUIPropPage::OnSetActive()
259 {
260     if (!GetLayerInfo(m_Filename, m_LayerQueryFlags, &m_RegistryOSMode, &m_RegistryEnabledLayers, m_RegistryCustomLayers))
261         m_RegistryOSMode = m_RegistryEnabledLayers = 0;
262 
263     for (size_t n = 0; g_Layers[n].Name; ++n)
264         CheckDlgButton(g_Layers[n].Id, (m_RegistryEnabledLayers & (1<<n)) ? BST_CHECKED : BST_UNCHECKED);
265 
266     CheckDlgButton(IDC_CHKRUNCOMPATIBILITY, m_RegistryOSMode ? BST_CHECKED : BST_UNCHECKED);
267 
268     if (m_RegistryOSMode)
269         ComboBox_SetCurSel(GetDlgItem(IDC_COMPATIBILITYMODE), m_RegistryOSMode-1);
270 
271     m_CustomLayers = m_RegistryCustomLayers;
272 
273     UpdateControls();
274 
275     return 0;
276 }
277 
278 
279 static BOOL ArrayEquals(const CSimpleArray<CString>& lhs, const CSimpleArray<CString>& rhs)
280 {
281     if (lhs.GetSize() != rhs.GetSize())
282         return FALSE;
283 
284     for (int n = 0; n < lhs.GetSize(); ++n)
285     {
286         if (lhs[n] != rhs[n])
287             return FALSE;
288     }
289     return TRUE;
290 }
291 
292 BOOL CLayerUIPropPage::HasChanges() const
293 {
294     if (m_RegistryEnabledLayers != m_EnabledLayers)
295         return TRUE;
296 
297     if (m_RegistryOSMode != m_OSMode)
298         return TRUE;
299 
300     if (!ArrayEquals(m_RegistryCustomLayers, m_CustomLayers))
301         return TRUE;
302 
303     return FALSE;
304 }
305 
306 int CLayerUIPropPage::OnApply()
307 {
308     if (HasChanges())
309     {
310         BOOL bMachine = m_LayerQueryFlags == GPLK_MACHINE;
311 
312         for (size_t n = 0; g_CompatModes[n].Name; ++n)
313             SetPermLayerState(m_Filename, g_CompatModes[n].Name, 0, bMachine, (n+1) == m_OSMode);
314 
315         for (size_t n = 0; g_Layers[n].Name; ++n)
316         {
317             SetPermLayerState(m_Filename, g_Layers[n].Name, 0, bMachine, ((1<<n) & m_EnabledLayers) != 0);
318         }
319 
320         /* Disable all old values */
321         for (int j = 0; j < m_RegistryCustomLayers.GetSize(); j++)
322         {
323             SetPermLayerState(m_Filename, m_RegistryCustomLayers[j].GetString(), 0, bMachine, FALSE);
324         }
325 
326         /* Enable all new values */
327         for (int j = 0; j < m_CustomLayers.GetSize(); j++)
328         {
329             SetPermLayerState(m_Filename, m_CustomLayers[j].GetString(), 0, bMachine, TRUE);
330         }
331 
332         SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATHW, (PCWSTR)m_Filename, NULL);
333     }
334 
335     return PSNRET_NOERROR;
336 }
337 
338 LRESULT CLayerUIPropPage::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled)
339 {
340     HWND cboMode = GetDlgItem(IDC_COMPATIBILITYMODE);
341     for (size_t n = 0; g_CompatModes[n].Display; ++n)
342         ComboBox_AddString(cboMode, g_CompatModes[n].Display);
343     ComboBox_SetCurSel(cboMode, 5);
344 
345     CComBSTR explanation;
346     if (!m_AllowPermLayer)
347     {
348         explanation.LoadString(g_hModule, IDS_FAILED_NETWORK);
349         DisableControls();
350         ACDBG(L"AllowPermLayer returned FALSE\r\n");
351     }
352     else if (m_IsSfcProtected)
353     {
354         explanation.LoadString(g_hModule, IDS_FAILED_PROTECTED);
355         DisableControls();
356         ACDBG(L"Protected OS file\r\n");
357     }
358     else
359     {
360         return TRUE;
361     }
362     SetDlgItemTextW(IDC_EXPLANATION, explanation);
363     return TRUE;
364 }
365 
366 INT_PTR CLayerUIPropPage::DisableControls()
367 {
368     ::EnableWindow(GetDlgItem(IDC_COMPATIBILITYMODE), 0);
369     ::EnableWindow(GetDlgItem(IDC_CHKRUNCOMPATIBILITY), 0);
370     for (size_t n = 0; g_Layers[n].Name; ++n)
371         ::EnableWindow(GetDlgItem(g_Layers[n].Id), 0);
372     ::EnableWindow(GetDlgItem(IDC_EDITCOMPATIBILITYMODES), 0);
373     return TRUE;
374 }
375 
376 void CLayerUIPropPage::UpdateControls()
377 {
378     m_OSMode = 0, m_EnabledLayers = 0;
379     BOOL ModeEnabled = IsDlgButtonChecked(IDC_CHKRUNCOMPATIBILITY);
380     if (ModeEnabled)
381         m_OSMode = ComboBox_GetCurSel(GetDlgItem(IDC_COMPATIBILITYMODE))+1;
382     ::EnableWindow(GetDlgItem(IDC_COMPATIBILITYMODE), ModeEnabled);
383 
384     for (size_t n = 0; g_Layers[n].Name; ++n)
385     {
386         m_EnabledLayers |= IsDlgButtonChecked(g_Layers[n].Id) ? (1<<n) : 0;
387         ::ShowWindow(GetDlgItem(g_Layers[n].Id), SW_SHOW);
388     }
389 
390     CStringW customLayers;
391     for (int j = 0; j < m_CustomLayers.GetSize(); ++j)
392     {
393         if (j > 0)
394             customLayers += L", ";
395         customLayers += m_CustomLayers[j];
396     }
397     SetDlgItemTextW(IDC_ENABLED_LAYERS, customLayers);
398 
399     SetModified(HasChanges());
400 }
401 
402 LRESULT CLayerUIPropPage::OnCtrlCommand(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL &bHandled)
403 {
404     UpdateControls();
405     return 0;
406 }
407 
408 LRESULT CLayerUIPropPage::OnEditModes(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL &bHandled)
409 {
410     if (DialogBoxParamW(g_hModule, MAKEINTRESOURCEW(IDD_EDITCOMPATIBILITYMODES), m_hWnd, EditModesProc, (LPARAM)this) == IDOK)
411         UpdateControls();
412     return 0;
413 }
414 
415 LRESULT CLayerUIPropPage::OnClickNotify(INT uCode, LPNMHDR hdr, BOOL& bHandled)
416 {
417     if (hdr->idFrom == IDC_INFOLINK)
418         ShellExecute(NULL, L"open", L"https://www.reactos.org/forum/viewforum.php?f=4", NULL, NULL, SW_SHOW);
419     return 0;
420 }
421 
422 static void ListboxChanged(HWND hWnd)
423 {
424     int Sel = ListBox_GetCurSel(GetDlgItem(hWnd, IDC_COMPATIBILITYMODE));
425     EnableWindow(GetDlgItem(hWnd, IDC_EDIT), Sel >= 0);
426     EnableWindow(GetDlgItem(hWnd, IDC_DELETE), Sel >= 0);
427 }
428 
429 static void OnAdd(HWND hWnd)
430 {
431     HWND Combo = GetDlgItem(hWnd, IDC_NEWCOMPATIBILITYMODE);
432 
433     int Length = ComboBox_GetTextLength(Combo);
434     CComBSTR Str(Length);
435     ComboBox_GetText(Combo, Str, Length+1);
436     HWND List = GetDlgItem(hWnd, IDC_COMPATIBILITYMODE);
437     int Index = ListBox_FindStringExact(List, -1, Str);
438     if (Index == LB_ERR)
439         Index = ListBox_AddString(List, Str);
440     ListBox_SetCurSel(List, Index);
441     ListboxChanged(hWnd);
442     ComboBox_SetCurSel(Combo, -1);
443     SetFocus(Combo);
444 }
445 
446 static BOOL ComboHasData(HWND hWnd)
447 {
448     HWND Combo = GetDlgItem(hWnd, IDC_NEWCOMPATIBILITYMODE);
449     if (ComboBox_GetCurSel(Combo) >= 0)
450         return TRUE;
451     ULONG Len = ComboBox_GetTextLength(Combo);
452     return Len > 0;
453 }
454 
455 INT_PTR CALLBACK CLayerUIPropPage::EditModesProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
456 {
457     CLayerUIPropPage* page = NULL;
458 
459     switch (uMsg)
460     {
461     case WM_INITDIALOG:
462         page = (CLayerUIPropPage*)lParam;
463         page->AddRef();
464         ::SetProp(hWnd, ACP_WNDPROP, page);
465         {
466             HWND Combo = ::GetDlgItem(hWnd, IDC_NEWCOMPATIBILITYMODE);
467             CComObject<CLayerStringList> pList;
468 
469             while (TRUE)
470             {
471                 CComHeapPtr<OLECHAR> str;
472                 HRESULT hr = pList.Next(1, &str, NULL);
473                 if (hr != S_OK)
474                     break;
475                 ComboBox_AddString(Combo, str);
476             }
477 
478             HWND List = ::GetDlgItem(hWnd, IDC_COMPATIBILITYMODE);
479             for (int n = 0; n < page->m_CustomLayers.GetSize(); ++n)
480             {
481                 const WCHAR* Str = page->m_CustomLayers[n].GetString();
482                 int Index = ListBox_FindStringExact(List, -1, Str);
483                 if (Index == LB_ERR)
484                     Index = ListBox_AddString(List, Str);
485             }
486         }
487         break;
488     case WM_ENDSESSION:
489     case WM_DESTROY:
490         page = (CLayerUIPropPage*)::GetProp(hWnd, ACP_WNDPROP);
491         ::RemoveProp(hWnd, ACP_WNDPROP);
492         page->Release();
493         break;
494 
495     case WM_COMMAND:
496         switch(LOWORD(wParam))
497         {
498         case IDC_ADD:
499             OnAdd(hWnd);
500             break;
501         case IDC_EDIT:
502         {
503             HWND List = ::GetDlgItem(hWnd, IDC_COMPATIBILITYMODE);
504             int Cur = ListBox_GetCurSel(List);
505             int Length = ListBox_GetTextLen(List, Cur);
506             CComBSTR Str(Length);
507             ListBox_GetText(List, Cur, Str);
508             ListBox_DeleteString(List, Cur);
509             HWND Combo = ::GetDlgItem(hWnd, IDC_NEWCOMPATIBILITYMODE);
510             ComboBox_SetCurSel(Combo, -1);
511             ::SetWindowText(Combo,Str);
512             ListboxChanged(hWnd);
513             ComboBox_SetEditSel(Combo, 30000, 30000);
514             ::SetFocus(Combo);
515         }
516             break;
517         case IDC_DELETE:
518         {
519             HWND List = ::GetDlgItem(hWnd, IDC_COMPATIBILITYMODE);
520             ListBox_DeleteString(List, ListBox_GetCurSel(List));
521             ListboxChanged(hWnd);
522         }
523             break;
524         case IDC_COMPATIBILITYMODE:
525             ListboxChanged(hWnd);
526             break;
527         case IDC_NEWCOMPATIBILITYMODE:
528         {
529             ::EnableWindow(::GetDlgItem(hWnd, IDC_ADD), ComboHasData(hWnd));
530         }
531             break;
532         case IDOK:
533             /* Copy from list! */
534         {
535             if (ComboHasData(hWnd))
536             {
537                 CComBSTR question, title;
538                 title.LoadString(g_hModule, IDS_COMPAT_TITLE);
539                 question.LoadString(g_hModule, IDS_YOU_DID_NOT_ADD);
540                 int result = ::MessageBoxW(hWnd, question, title, MB_YESNOCANCEL | MB_ICONQUESTION);
541                 switch (result)
542                 {
543                 case IDYES:
544                     OnAdd(hWnd);
545                     break;
546                 case IDNO:
547                     break;
548                 case IDCANCEL:
549                     return FALSE;
550                 }
551             }
552 
553             page = (CLayerUIPropPage*)::GetProp(hWnd, ACP_WNDPROP);
554 
555             HWND List = ::GetDlgItem(hWnd, IDC_COMPATIBILITYMODE);
556             int Count = ListBox_GetCount(List);
557             page->m_CustomLayers.RemoveAll();
558             for (int Cur = 0; Cur < Count; ++Cur)
559             {
560                 int Length = ListBox_GetTextLen(List, Cur);
561                 CString Str;
562                 LPWSTR Buffer = Str.GetBuffer(Length + 1);
563                 ListBox_GetText(List, Cur, Buffer);
564                 Str.ReleaseBuffer(Length);
565                 page->m_CustomLayers.Add(Str);
566             }
567         }
568         /* Fall trough */
569         case IDCANCEL:
570             ::EndDialog(hWnd, LOWORD(wParam));
571             break;
572         }
573         break;
574     case WM_CLOSE:
575         ::EndDialog(hWnd, IDCANCEL);
576         break;
577     }
578     return FALSE;
579 }
580 
581 static BOOL DisableShellext()
582 {
583     HKEY hkey;
584     LSTATUS ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Policies\\Microsoft\\Windows\\AppCompat", 0, KEY_QUERY_VALUE, &hkey);
585     BOOL Disable = FALSE;
586     if (ret == ERROR_SUCCESS)
587     {
588         DWORD dwValue = 0;
589         DWORD type, size = sizeof(dwValue);
590         ret = RegQueryValueExW(hkey, L"DisableEngine", NULL, &type, (PBYTE)&dwValue, &size);
591         if (ret == ERROR_SUCCESS && type == REG_DWORD)
592         {
593             Disable = !!dwValue;
594         }
595         if (!Disable)
596         {
597             size = sizeof(dwValue);
598             ret = RegQueryValueExW(hkey, L"DisablePropPage", NULL, &type, (PBYTE)&dwValue, &size);
599             if (ret == ERROR_SUCCESS && type == REG_DWORD)
600             {
601                 Disable = !!dwValue;
602             }
603         }
604 
605         RegCloseKey(hkey);
606     }
607     return Disable;
608 }
609 
610 STDMETHODIMP CLayerUIPropPage::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hkeyProgID)
611 {
612     FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
613     STGMEDIUM stg;
614 
615     if (DisableShellext())
616         return E_ACCESSDENIED;
617 
618     HRESULT hr = pDataObj->GetData(&etc, &stg);
619     if (FAILED(hr))
620     {
621         ACDBG(L"Failed to retrieve Data from pDataObj.\r\n");
622         return E_INVALIDARG;
623     }
624     hr = E_FAIL;
625     HDROP hdrop = (HDROP)GlobalLock(stg.hGlobal);
626     if (hdrop)
627     {
628         UINT uNumFiles = DragQueryFileW(hdrop, 0xFFFFFFFF, NULL, 0);
629         if (uNumFiles == 1)
630         {
631             WCHAR szFile[MAX_PATH * 2];
632             if (DragQueryFileW(hdrop, 0, szFile, _countof(szFile)))
633             {
634                 this->AddRef();
635                 hr = InitFile(szFile);
636             }
637             else
638             {
639                 ACDBG(L"Failed to query the file.\r\n");
640             }
641         }
642         else
643         {
644             ACDBG(L"Invalid number of files: %d\r\n", uNumFiles);
645         }
646         GlobalUnlock(stg.hGlobal);
647     }
648     else
649     {
650         ACDBG(L"Could not lock stg.hGlobal\r\n");
651     }
652     ReleaseStgMedium(&stg);
653     return hr;
654 }
655