[SHELLEXT][ZIPFLDR] Implement ZIP creation (#2114)
[reactos.git] / dll / shellext / zipfldr / CZipFolder.hpp
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