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