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