xref: /reactos/dll/shellext/zipfldr/CZipFolder.hpp (revision cdf90707)
1 /*
2  * PROJECT:     ReactOS Zip Shell Extension
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Main class
5  * COPYRIGHT:   Copyright 2017 Mark Jansen (mark.jansen@reactos.org)
6  */
7 
8 struct FolderViewColumns
9 {
10     int iResource;
11     DWORD dwDefaultState;
12     int cxChar;
13     int fmt;
14 };
15 
16 static FolderViewColumns g_ColumnDefs[] =
17 {
18     { IDS_COL_NAME,      SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,   25, LVCFMT_LEFT },
19     { IDS_COL_TYPE,      SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,   20, LVCFMT_LEFT },
20     { IDS_COL_COMPRSIZE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,   10, LVCFMT_RIGHT },
21     { IDS_COL_PASSWORD,  SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,   10, LVCFMT_LEFT },
22     { IDS_COL_SIZE,      SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,   10, LVCFMT_RIGHT },
23     { IDS_COL_RATIO,     SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT,   10, LVCFMT_LEFT },
24     { IDS_COL_DATE_MOD,  SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT,  15, LVCFMT_LEFT },
25 };
26 
27 
28 class CZipFolder :
29     public CComCoClass<CZipFolder, &CLSID_ZipFolderStorageHandler>,
30     public CComObjectRootEx<CComMultiThreadModelNoCS>,
31     public IShellFolder2,
32     //public IStorage,
33     public IContextMenu,
34     public IShellExtInit,
35     //public IPersistFile,
36     public IPersistFolder2,
37     public IZip
38 {
39     CStringW m_ZipFile;
40     CStringA m_ZipDir;
41     CComHeapPtr<ITEMIDLIST> m_CurDir;
42     unzFile m_UnzipFile;
43 
44 public:
45     CZipFolder()
46         :m_UnzipFile(NULL)
47     {
48     }
49 
50     ~CZipFolder()
51     {
52         Close();
53     }
54 
55     void Close()
56     {
57         if (m_UnzipFile)
58             unzClose(m_UnzipFile);
59         m_UnzipFile = NULL;
60     }
61 
62     // *** IZip methods ***
63     STDMETHODIMP_(unzFile) getZip()
64     {
65         if (!m_UnzipFile)
66         {
67             m_UnzipFile = unzOpen2_64(m_ZipFile, &g_FFunc);
68         }
69 
70         return m_UnzipFile;
71     }
72 
73     // *** IShellFolder2 methods ***
74     STDMETHODIMP GetDefaultSearchGUID(GUID *pguid)
75     {
76         UNIMPLEMENTED;
77         return E_NOTIMPL;
78     }
79     STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum)
80     {
81         UNIMPLEMENTED;
82         return E_NOTIMPL;
83     }
84     STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
85     {
86         UNIMPLEMENTED;
87         return E_NOTIMPL;
88     }
89     STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pcsFlags)
90     {
91         if (!pcsFlags || iColumn >= _countof(g_ColumnDefs))
92             return E_INVALIDARG;
93         *pcsFlags = g_ColumnDefs[iColumn].dwDefaultState;
94         return S_OK;
95     }
96     STDMETHODIMP GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
97     {
98         UNIMPLEMENTED;
99         return E_NOTIMPL;
100     }
101     // Adapted from CFileDefExt::GetFileTimeString
102     BOOL _GetFileTimeString(LPFILETIME lpFileTime, LPWSTR pwszResult, UINT cchResult)
103     {
104         SYSTEMTIME st;
105 
106         if (!FileTimeToSystemTime(lpFileTime, &st))
107             return FALSE;
108 
109         size_t cchRemaining = cchResult;
110         LPWSTR pwszEnd = pwszResult;
111         int cchWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, pwszEnd, cchRemaining);
112         if (cchWritten)
113             --cchWritten; // GetDateFormatW returns count with terminating zero
114         else
115             return FALSE;
116         cchRemaining -= cchWritten;
117         pwszEnd += cchWritten;
118 
119         StringCchCopyExW(pwszEnd, cchRemaining, L" ", &pwszEnd, &cchRemaining, 0);
120 
121         cchWritten = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, pwszEnd, cchRemaining);
122         if (cchWritten)
123             --cchWritten; // GetTimeFormatW returns count with terminating zero
124         else
125             return FALSE;
126 
127         return TRUE;
128     }
129     STDMETHODIMP GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, SHELLDETAILS *psd)
130     {
131         if (iColumn >= _countof(g_ColumnDefs))
132             return E_FAIL;
133 
134         psd->cxChar = g_ColumnDefs[iColumn].cxChar;
135         psd->fmt = g_ColumnDefs[iColumn].fmt;
136 
137         if (pidl == NULL)
138         {
139             return SHSetStrRet(&psd->str, _AtlBaseModule.GetResourceInstance(), g_ColumnDefs[iColumn].iResource);
140         }
141 
142         PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl);
143         if (curpidl->mkid.cb != 0)
144         {
145             DPRINT1("ERROR, unhandled PIDL!\n");
146             return E_FAIL;
147         }
148 
149         const ZipPidlEntry* zipEntry = _ZipFromIL(pidl);
150         if (!zipEntry)
151             return E_INVALIDARG;
152 
153         WCHAR Buffer[100];
154         bool isDir = zipEntry->ZipType == ZIP_PIDL_DIRECTORY;
155         switch (iColumn)
156         {
157         case 0: /* Name, ReactOS specific? */
158             return GetDisplayNameOf(pidl, 0, &psd->str);
159         case 1: /* Type */
160         {
161             SHFILEINFOA shfi;
162             DWORD dwAttributes = isDir ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL;
163             ULONG_PTR firet = SHGetFileInfoA(zipEntry->Name, dwAttributes, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME);
164             if (!firet)
165                 return E_FAIL;
166             return SHSetStrRet(&psd->str, shfi.szTypeName);
167         }
168         case 2: /* Compressed size */
169         case 4: /* Size */
170         {
171             if (isDir)
172                 return SHSetStrRet(&psd->str, L"");
173 
174             ULONG64 Size = iColumn == 2 ? zipEntry->CompressedSize : zipEntry->UncompressedSize;
175             if (!StrFormatByteSizeW(Size, Buffer, _countof(Buffer)))
176                 return E_FAIL;
177             return SHSetStrRet(&psd->str, Buffer);
178         }
179         case 3: /* Password */
180             if (isDir)
181                 return SHSetStrRet(&psd->str, L"");
182             return SHSetStrRet(&psd->str, _AtlBaseModule.GetResourceInstance(), zipEntry->Password ? IDS_YES : IDS_NO);
183         case 5: /* Ratio */
184         {
185             if (isDir)
186                 return SHSetStrRet(&psd->str, L"");
187 
188             int ratio = 0;
189             if (zipEntry->UncompressedSize)
190                 ratio = 100 - (int)((zipEntry->CompressedSize*100)/zipEntry->UncompressedSize);
191             StringCchPrintfW(Buffer, _countof(Buffer), L"%d%%", ratio);
192             return SHSetStrRet(&psd->str, Buffer);
193         }
194         case 6: /* Date */
195         {
196             if (isDir)
197                 return SHSetStrRet(&psd->str, L"");
198             FILETIME ftLocal;
199             DosDateTimeToFileTime((WORD)(zipEntry->DosDate>>16), (WORD)zipEntry->DosDate, &ftLocal);
200             if (!_GetFileTimeString(&ftLocal, Buffer, _countof(Buffer)))
201                 return E_FAIL;
202             return SHSetStrRet(&psd->str, Buffer);
203         }
204         }
205 
206         UNIMPLEMENTED;
207         return E_NOTIMPL;
208     }
209     STDMETHODIMP MapColumnToSCID(UINT column, SHCOLUMNID *pscid)
210     {
211         UNIMPLEMENTED;
212         return E_NOTIMPL;
213     }
214 
215     // *** IShellFolder methods ***
216     STDMETHODIMP ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLESTR lpszDisplayName, ULONG *pchEaten, PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes)
217     {
218         UNIMPLEMENTED;
219         return E_NOTIMPL;
220     }
221     STDMETHODIMP EnumObjects(HWND hwndOwner, DWORD dwFlags, LPENUMIDLIST *ppEnumIDList)
222     {
223         return _CEnumZipContents_CreateInstance(this, dwFlags, m_ZipDir, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
224     }
225     STDMETHODIMP BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
226     {
227         if (riid == IID_IShellFolder)
228         {
229             CStringA newZipDir = m_ZipDir;
230             PCUIDLIST_RELATIVE curpidl = pidl;
231             while (curpidl->mkid.cb)
232             {
233                 const ZipPidlEntry* zipEntry = _ZipFromIL(curpidl);
234                 if (!zipEntry)
235                 {
236                     return E_FAIL;
237                 }
238                 newZipDir += zipEntry->Name;
239                 newZipDir += '/';
240 
241                 curpidl = ILGetNext(curpidl);
242             }
243             return ShellObjectCreatorInit<CZipFolder>(m_ZipFile, newZipDir, m_CurDir, pidl, riid, ppvOut);
244         }
245         DbgPrint("%s(%S) UNHANDLED\n", __FUNCTION__, guid2string(riid));
246         return E_NOTIMPL;
247     }
248     STDMETHODIMP BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbcReserved, REFIID riid, LPVOID *ppvOut)
249     {
250         UNIMPLEMENTED;
251         return E_NOTIMPL;
252     }
253     STDMETHODIMP CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
254     {
255         const ZipPidlEntry* zipEntry1 = _ZipFromIL(pidl1);
256         const ZipPidlEntry* zipEntry2 = _ZipFromIL(pidl2);
257 
258         if (!zipEntry1 || !zipEntry2)
259             return E_INVALIDARG;
260 
261         int result = 0;
262         if (zipEntry1->ZipType != zipEntry2->ZipType)
263             result = zipEntry1->ZipType - zipEntry2->ZipType;
264         else
265             result = stricmp(zipEntry1->Name, zipEntry2->Name);
266 
267         if (!result && zipEntry1->ZipType == ZIP_PIDL_DIRECTORY)
268         {
269             PCUIDLIST_RELATIVE child1 = ILGetNext(pidl1);
270             PCUIDLIST_RELATIVE child2 = ILGetNext(pidl2);
271 
272             if (child1->mkid.cb && child2->mkid.cb)
273                 return CompareIDs(lParam, child1, child2);
274             else if (child1->mkid.cb)
275                 result = 1;
276             else if (child2->mkid.cb)
277                 result = -1;
278         }
279 
280         return MAKE_COMPARE_HRESULT(result);
281     }
282     STDMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, LPVOID *ppvOut)
283     {
284         static const GUID UnknownIID = // {93F81976-6A0D-42C3-94DD-AA258A155470}
285         {0x93F81976, 0x6A0D, 0x42C3, {0x94, 0xDD, 0xAA, 0x25, 0x8A, 0x15, 0x54, 0x70}};
286         if (riid == IID_IShellView)
287         {
288             SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this};
289             CComPtr<IShellFolderViewCB> pcb;
290 
291             HRESULT hr = _CFolderViewCB_CreateInstance(IID_PPV_ARG(IShellFolderViewCB, &pcb));
292             if (FAILED_UNEXPECTEDLY(hr))
293                 return hr;
294 
295             sfvparams.psfvcb = pcb;
296             hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
297 
298             return hr;
299         }
300         else if (riid == IID_IExplorerCommandProvider)
301         {
302             return _CExplorerCommandProvider_CreateInstance(this, riid, ppvOut);
303         }
304         else if (riid == IID_IContextMenu)
305         {
306             // Folder context menu
307             return QueryInterface(riid, ppvOut);
308         }
309         if (UnknownIID != riid)
310             DbgPrint("%s(%S) UNHANDLED\n", __FUNCTION__, guid2string(riid));
311         return E_NOTIMPL;
312     }
313     STDMETHODIMP GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, DWORD *rgfInOut)
314     {
315         if (!rgfInOut || !cidl || !apidl)
316             return E_INVALIDARG;
317 
318         *rgfInOut = 0;
319 
320         //static DWORD dwFileAttrs = SFGAO_STREAM | SFGAO_HASPROPSHEET | SFGAO_CANDELETE | SFGAO_CANCOPY | SFGAO_CANMOVE;
321         //static DWORD dwFolderAttrs = SFGAO_FOLDER | SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANDELETE | SFGAO_STORAGE | SFGAO_CANCOPY | SFGAO_CANMOVE;
322         static DWORD dwFileAttrs = SFGAO_STREAM;
323         static DWORD dwFolderAttrs = SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_BROWSABLE;
324 
325 
326         while (cidl > 0 && *apidl)
327         {
328             const ZipPidlEntry* zipEntry = _ZipFromIL(*apidl);
329 
330             if (zipEntry)
331             {
332                 if (zipEntry->ZipType == ZIP_PIDL_FILE)
333                     *rgfInOut |= dwFileAttrs;
334                 else
335                     *rgfInOut |= dwFolderAttrs;
336             }
337             else
338             {
339                 *rgfInOut = 0;
340             }
341 
342             apidl++;
343             cidl--;
344         }
345 
346         *rgfInOut &= ~SFGAO_VALIDATE;
347         return S_OK;
348     }
349     static HRESULT CALLBACK ZipFolderMenuCallback(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj,
350                                                   UINT uMsg, WPARAM wParam, LPARAM lParam)
351     {
352         switch (uMsg)
353         {
354         case DFM_MERGECONTEXTMENU:
355         {
356             CComQIIDPtr<I_ID(IContextMenu)> spContextMenu(psf);
357             if (!spContextMenu)
358                 return E_NOINTERFACE;
359 
360             QCMINFO *pqcminfo = (QCMINFO *)lParam;
361             HRESULT hr = spContextMenu->QueryContextMenu(pqcminfo->hmenu,
362                                                  pqcminfo->indexMenu,
363                                                  pqcminfo->idCmdFirst,
364                                                  pqcminfo->idCmdLast,
365                                                  CMF_NORMAL);
366             if (FAILED_UNEXPECTEDLY(hr))
367                 return hr;
368 
369             pqcminfo->idCmdFirst += HRESULT_CODE(hr);
370             return S_OK;
371         }
372         case DFM_INVOKECOMMAND:
373         {
374             CComQIIDPtr<I_ID(IContextMenu)> spContextMenu(psf);
375             if (!spContextMenu)
376                 return E_NOINTERFACE;
377 
378             CMINVOKECOMMANDINFO ici = { sizeof(ici) };
379             ici.lpVerb = MAKEINTRESOURCEA(wParam);
380             return spContextMenu->InvokeCommand(&ici);
381         }
382         case DFM_INVOKECOMMANDEX:
383         case DFM_GETDEFSTATICID: // Required for Windows 7 to pick a default
384             return S_FALSE;
385         }
386         return E_NOTIMPL;
387     }
388     STDMETHODIMP GetUIObjectOf(HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, UINT * prgfInOut, LPVOID * ppvOut)
389     {
390         if ((riid == IID_IExtractIconA || riid == IID_IExtractIconW) && cidl == 1)
391         {
392             const ZipPidlEntry* zipEntry = _ZipFromIL(*apidl);
393             if (zipEntry)
394             {
395                 CComHeapPtr<WCHAR> pathW;
396 
397                 int len = MultiByteToWideChar(CP_ACP, 0, zipEntry->Name, -1, NULL, 0);
398                 pathW.Allocate(len);
399                 MultiByteToWideChar(CP_ACP, 0, zipEntry->Name, -1, pathW, len);
400 
401                 DWORD dwAttributes = (zipEntry->ZipType == ZIP_PIDL_DIRECTORY) ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL;
402                 return SHCreateFileExtractIconW(pathW, dwAttributes, riid, ppvOut);
403             }
404         }
405         else if (riid == IID_IContextMenu && cidl >= 0)
406         {
407             // Context menu of an object inside the zip
408             const ZipPidlEntry* zipEntry = _ZipFromIL(*apidl);
409             if (zipEntry)
410             {
411                 HKEY keys[1] = {0};
412                 int nkeys = 0;
413                 if (zipEntry->ZipType == ZIP_PIDL_DIRECTORY)
414                 {
415                     LSTATUS res = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Folder", 0, KEY_READ | KEY_QUERY_VALUE, keys);
416                     if (res != ERROR_SUCCESS)
417                         return E_FAIL;
418                     nkeys++;
419                 }
420                 return CDefFolderMenu_Create2(NULL, hwndOwner, cidl, apidl, this, ZipFolderMenuCallback, nkeys, keys, (IContextMenu**)ppvOut);
421             }
422         }
423         else if (riid == IID_IDataObject && cidl >= 1)
424         {
425             return CIDLData_CreateFromIDArray(m_CurDir, cidl, apidl, (IDataObject**)ppvOut);
426         }
427 
428         DbgPrint("%s(%S) UNHANDLED\n", __FUNCTION__ , guid2string(riid));
429         return E_NOINTERFACE;
430     }
431     STDMETHODIMP GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD dwFlags, LPSTRRET strRet)
432     {
433         if (!pidl)
434             return S_FALSE;
435 
436         PCUIDLIST_RELATIVE curpidl = ILGetNext(pidl);
437         if (curpidl->mkid.cb != 0)
438         {
439             DPRINT1("ERROR, unhandled PIDL!\n");
440             return E_FAIL;
441         }
442 
443         const ZipPidlEntry* zipEntry = _ZipFromIL(pidl);
444         if (!zipEntry)
445             return E_FAIL;
446 
447         return SHSetStrRet(strRet, (LPCSTR)zipEntry->Name);
448     }
449     STDMETHODIMP SetNameOf(HWND hwndOwner, PCUITEMID_CHILD pidl, LPCOLESTR lpName, DWORD dwFlags, PITEMID_CHILD *pPidlOut)
450     {
451         UNIMPLEMENTED;
452         return E_NOTIMPL;
453     }
454     //// IStorage
455     //STDMETHODIMP CreateStream(LPCOLESTR pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream **ppstm);
456     //STDMETHODIMP OpenStream(LPCOLESTR pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm);
457     //STDMETHODIMP CreateStorage(LPCOLESTR pwcsName, DWORD grfMode, DWORD dwStgFmt, DWORD reserved2, IStorage **ppstg);
458     //STDMETHODIMP OpenStorage(LPCOLESTR pwcsName, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg);
459     //STDMETHODIMP CopyTo(DWORD ciidExclude, const IID *rgiidExclude, SNB snbExclude, IStorage *pstgDest);
460     //STDMETHODIMP MoveElementTo(LPCOLESTR pwcsName, IStorage *pstgDest, LPCOLESTR pwcsNewName, DWORD grfFlags);
461     //STDMETHODIMP Commit(DWORD grfCommitFlags);
462     //STDMETHODIMP Revert();
463     //STDMETHODIMP EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum);
464     //STDMETHODIMP DestroyElement(LPCOLESTR pwcsName);
465     //STDMETHODIMP RenameElement(LPCOLESTR pwcsOldName, LPCOLESTR pwcsNewName);
466     //STDMETHODIMP SetElementTimes(LPCOLESTR pwcsName, const FILETIME *pctime, const FILETIME *patime, const FILETIME *pmtime);
467     //STDMETHODIMP SetClass(REFCLSID clsid);
468     //STDMETHODIMP SetStateBits(DWORD grfStateBits, DWORD grfMask);
469     //STDMETHODIMP Stat(STATSTG *pstatstg, DWORD grfStatFlag);
470 
471     // *** IContextMenu methods ***
472     STDMETHODIMP GetCommandString(UINT_PTR idCmd, UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax)
473     {
474         if (idCmd != 0)
475             return E_INVALIDARG;
476 
477         switch (uFlags)
478         {
479         case GCS_VERBA:
480             return StringCchCopyA(pszName, cchMax, EXTRACT_VERBA);
481         case GCS_VERBW:
482             return StringCchCopyW((LPWSTR)pszName, cchMax, EXTRACT_VERBW);
483         case GCS_HELPTEXTA:
484         {
485             CStringA helpText(MAKEINTRESOURCEA(IDS_HELPTEXT));
486             return StringCchCopyA(pszName, cchMax, helpText);
487         }
488         case GCS_HELPTEXTW:
489         {
490             CStringW helpText(MAKEINTRESOURCEA(IDS_HELPTEXT));
491             return StringCchCopyW((LPWSTR)pszName, cchMax, helpText);
492         }
493         case GCS_VALIDATEA:
494         case GCS_VALIDATEW:
495             return S_OK;
496         }
497 
498         return E_INVALIDARG;
499     }
500     STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO pici)
501     {
502         if (!pici || (pici->cbSize != sizeof(CMINVOKECOMMANDINFO) && pici->cbSize != sizeof(CMINVOKECOMMANDINFOEX)))
503             return E_INVALIDARG;
504 
505         if (pici->lpVerb == MAKEINTRESOURCEA(0) || (HIWORD(pici->lpVerb) && !strcmp(pici->lpVerb, EXTRACT_VERBA)))
506         {
507             BSTR ZipFile = m_ZipFile.AllocSysString();
508             InterlockedIncrement(&g_ModuleRefCnt);
509 
510             DWORD tid;
511             HANDLE hThread = CreateThread(NULL, 0, s_ExtractProc, ZipFile, NULL, &tid);
512             if (hThread)
513             {
514                 CloseHandle(hThread);
515                 return S_OK;
516             }
517         }
518         return E_INVALIDARG;
519     }
520     STDMETHODIMP QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
521     {
522         UINT idCmd = idCmdFirst;
523 
524         if (!(uFlags & CMF_DEFAULTONLY))
525         {
526             CStringW menuText(MAKEINTRESOURCEW(IDS_MENUITEM));
527 
528             if (indexMenu)
529             {
530                 InsertMenuW(hmenu, indexMenu++, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
531             }
532             InsertMenuW(hmenu, indexMenu++, MF_BYPOSITION | MF_STRING, idCmd++, menuText);
533         }
534 
535         return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, idCmd - idCmdFirst);
536     }
537 
538     // *** IShellExtInit methods ***
539     STDMETHODIMP Initialize(PCIDLIST_ABSOLUTE pidlFolder, LPDATAOBJECT pDataObj, HKEY hkeyProgID)
540     {
541         FORMATETC etc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
542         STGMEDIUM stg;
543 
544         HRESULT hr = pDataObj->GetData(&etc, &stg);
545         if (FAILED_UNEXPECTEDLY(hr))
546         {
547             return hr;
548         }
549         hr = E_FAIL;
550         HDROP hdrop = (HDROP)GlobalLock(stg.hGlobal);
551         if (hdrop)
552         {
553             UINT uNumFiles = DragQueryFileW(hdrop, 0xFFFFFFFF, NULL, 0);
554             if (uNumFiles == 1)
555             {
556                 WCHAR szFile[MAX_PATH * 2];
557                 if (DragQueryFileW(hdrop, 0, szFile, _countof(szFile)))
558                 {
559                     CComHeapPtr<ITEMIDLIST> pidl;
560                     hr = SHParseDisplayName(szFile, NULL, &pidl, 0, NULL);
561                     if (!FAILED_UNEXPECTEDLY(hr))
562                     {
563                         hr = Initialize(pidl);
564                     }
565                 }
566                 else
567                 {
568                     DbgPrint("Failed to query the file.\r\n");
569                 }
570             }
571             else
572             {
573                 DbgPrint("Invalid number of files: %d\r\n", uNumFiles);
574             }
575             GlobalUnlock(stg.hGlobal);
576         }
577         else
578         {
579             DbgPrint("Could not lock stg.hGlobal\r\n");
580         }
581         ReleaseStgMedium(&stg);
582         return hr;
583 
584     }
585 
586     //// IPersistFile
587     ////STDMETHODIMP GetClassID(CLSID *pclsid);
588     //STDMETHODIMP IsDirty();
589     //STDMETHODIMP Load(LPCOLESTR pszFileName, DWORD dwMode);
590     //STDMETHODIMP Save(LPCOLESTR pszFileName, BOOL fRemember);
591     //STDMETHODIMP SaveCompleted(LPCOLESTR pszFileName);
592     //STDMETHODIMP GetCurFile(LPOLESTR *ppszFileName);
593 
594     //// *** IPersistFolder2 methods ***
595     STDMETHODIMP GetCurFolder(PIDLIST_ABSOLUTE * pidl)
596     {
597         *pidl = ILClone(m_CurDir);
598         return S_OK;
599     }
600 
601     // *** IPersistFolder methods ***
602     STDMETHODIMP Initialize(PCIDLIST_ABSOLUTE pidl)
603     {
604         WCHAR tmpPath[MAX_PATH];
605 
606         if (SHGetPathFromIDListW(pidl, tmpPath))
607         {
608             m_ZipFile = tmpPath;
609             m_CurDir.Attach(ILClone(pidl));
610             return S_OK;
611         }
612         DbgPrint("%s() => Unable to parse pidl\n", __FUNCTION__);
613         return E_INVALIDARG;
614     }
615 
616     // *** IPersist methods ***
617     STDMETHODIMP GetClassID(CLSID *lpClassId)
618     {
619         DbgPrint("%s\n", __FUNCTION__);
620         return E_NOTIMPL;
621     }
622 
623 
624     STDMETHODIMP Initialize(PCWSTR zipFile, PCSTR zipDir, PCUIDLIST_ABSOLUTE curDir, PCUIDLIST_RELATIVE pidl)
625     {
626         m_ZipFile = zipFile;
627         m_ZipDir = zipDir;
628 
629         m_CurDir.Attach(ILCombine(curDir, pidl));
630         return S_OK;
631     }
632     static DWORD WINAPI s_ExtractProc(LPVOID arg)
633     {
634         CComBSTR ZipFile;
635         ZipFile.Attach((BSTR)arg);
636 
637         _CZipExtract_runWizard(ZipFile);
638 
639         InterlockedDecrement(&g_ModuleRefCnt);
640         return 0;
641     }
642 
643 public:
644     DECLARE_NO_REGISTRY()   // Handled manually because this object is exposed via multiple clsid's
645     DECLARE_NOT_AGGREGATABLE(CZipFolder)
646 
647     DECLARE_PROTECT_FINAL_CONSTRUCT()
648 
649     BEGIN_COM_MAP(CZipFolder)
650         COM_INTERFACE_ENTRY_IID(IID_IShellFolder2, IShellFolder2)
651         COM_INTERFACE_ENTRY_IID(IID_IShellFolder, IShellFolder)
652 //        COM_INTERFACE_ENTRY_IID(IID_IStorage, IStorage)
653         COM_INTERFACE_ENTRY_IID(IID_IContextMenu, IContextMenu)
654         COM_INTERFACE_ENTRY_IID(IID_IShellExtInit, IShellExtInit)
655         //COM_INTERFACE_ENTRY_IID(IID_IPersistFile, IPersistFile)
656         COM_INTERFACE_ENTRY_IID(IID_IPersistFolder2, IPersistFolder2)
657         COM_INTERFACE_ENTRY_IID(IID_IPersistFolder, IPersistFolder)
658         COM_INTERFACE_ENTRY_IID(IID_IPersist, IPersist)
659     END_COM_MAP()
660 };
661 
662