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