1 // ContextMenu.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../Common/ComTry.h"
6 #include "../../../Common/IntToString.h"
7 #include "../../../Common/StringConvert.h"
8
9 #include "../../../Windows/COM.h"
10 #include "../../../Windows/DLL.h"
11 #include "../../../Windows/FileDir.h"
12 #include "../../../Windows/FileName.h"
13 #include "../../../Windows/Menu.h"
14 #include "../../../Windows/ProcessUtils.h"
15
16 // for IS_INTRESOURCE():
17 #include "../../../Windows/Window.h"
18
19 #include "../../PropID.h"
20
21 #include "../Common/ArchiveName.h"
22 #include "../Common/CompressCall.h"
23 #include "../Common/ExtractingFilePath.h"
24 #include "../Common/ZipRegistry.h"
25
26 #include "../FileManager/FormatUtils.h"
27 #include "../FileManager/LangUtils.h"
28 #include "../FileManager/PropertyName.h"
29
30 #include "ContextMenu.h"
31 #include "ContextMenuFlags.h"
32 #include "MyMessages.h"
33
34 #include "resource.h"
35
36
37 // #define SHOW_DEBUG_CTX_MENU
38
39 #ifdef SHOW_DEBUG_CTX_MENU
40 #include <stdio.h>
41 #endif
42
43 using namespace NWindows;
44 using namespace NFile;
45 using namespace NDir;
46
47 #ifndef UNDER_CE
48 #define EMAIL_SUPPORT 1
49 #endif
50
51 extern LONG g_DllRefCount;
52
53 #ifdef _WIN32
54 extern HINSTANCE g_hInstance;
55 #endif
56
57 #ifdef UNDER_CE
58 #define MY_IS_INTRESOURCE(_r) ((((ULONG_PTR)(_r)) >> 16) == 0)
59 #else
60 #define MY_IS_INTRESOURCE(_r) IS_INTRESOURCE(_r)
61 #endif
62
63
64 #ifdef SHOW_DEBUG_CTX_MENU
65
PrintStringA(const char * name,LPCSTR ptr)66 static void PrintStringA(const char *name, LPCSTR ptr)
67 {
68 AString m;
69 m += name;
70 m += ": ";
71 char s[32];
72 sprintf(s, "%p", (const void *)ptr);
73 m += s;
74 if (!MY_IS_INTRESOURCE(ptr))
75 {
76 m += ": \"";
77 m += ptr;
78 m += "\"";
79 }
80 OutputDebugStringA(m);
81 }
82
83 #if !defined(UNDER_CE)
PrintStringW(const char * name,LPCWSTR ptr)84 static void PrintStringW(const char *name, LPCWSTR ptr)
85 {
86 UString m;
87 m += name;
88 m += ": ";
89 char s[32];
90 sprintf(s, "%p", (const void *)ptr);
91 m += s;
92 if (!MY_IS_INTRESOURCE(ptr))
93 {
94 m += ": \"";
95 m += ptr;
96 m += "\"";
97 }
98 OutputDebugStringW(m);
99 }
100 #endif
101
Print_Ptr(const void * p,const char * s)102 static void Print_Ptr(const void *p, const char *s)
103 {
104 char temp[32];
105 sprintf(temp, "%p", (const void *)p);
106 AString m;
107 m += temp;
108 m.Add_Space();
109 m += s;
110 OutputDebugStringA(m);
111 }
112
Print_Number(UInt32 number,const char * s)113 static void Print_Number(UInt32 number, const char *s)
114 {
115 AString m;
116 m.Add_UInt32(number);
117 m.Add_Space();
118 m += s;
119 OutputDebugStringA(m);
120 }
121
122 #define ODS(sz) { Print_Ptr(this, sz); }
123 #define ODS_U(s) { OutputDebugStringW(s); }
124 #define ODS_(op) { op; }
125 #define ODS_SPRF_s(x) { char s[256]; x; OutputDebugStringA(s); }
126
127 #else
128
129 #define ODS(sz)
130 #define ODS_U(s)
131 #define ODS_(op)
132 #define ODS_SPRF_s(x)
133
134 #endif
135
136
137 /*
138 DOCs: In Windows 7 and later, the number of items passed to
139 a verb is limited to 16 when a shortcut menu is queried.
140 The verb is then re-created and re-initialized with the full
141 selection when that verb is invoked.
142 win10 tests:
143 if (the number of selected file/dir objects > 16)
144 {
145 Explorer does the following actions:
146 - it creates ctx_menu_1 IContextMenu object
147 - it calls ctx_menu_1->Initialize() with list of only up to 16 items
148 - it calls ctx_menu_1->QueryContextMenu(menu_1)
149 - if (some menu command is pressed)
150 {
151 - it gets shown string from selected menu item : shown_menu_1_string
152 - it creates another ctx_menu_2 IContextMenu object
153 - it calls ctx_menu_2->Initialize() with list of all items
154 - it calls ctx_menu_2->QueryContextMenu(menu_2)
155 - if there is menu item with shown_menu_1_string string in menu_2,
156 Explorer calls ctx_menu_2->InvokeCommand() for that item.
157 Explorer probably doesn't use VERB from first object ctx_menu_1.
158 So we must provide same shown menu strings for both objects:
159 ctx_menu_1 and ctx_menu_2.
160 }
161 }
162 */
163
164
CZipContextMenu()165 CZipContextMenu::CZipContextMenu():
166 _isMenuForFM(true),
167 _fileNames_WereReduced(true),
168 _dropMode(false),
169 _bitmap(NULL),
170 _writeZone((UInt32)(Int32)-1),
171 IsSeparator(false),
172 IsRoot(true),
173 CurrentSubCommand(0)
174 {
175 ODS("== CZipContextMenu()");
176 InterlockedIncrement(&g_DllRefCount);
177 }
178
~CZipContextMenu()179 CZipContextMenu::~CZipContextMenu()
180 {
181 ODS("== ~CZipContextMenu");
182 if (_bitmap)
183 DeleteObject(_bitmap);
184 InterlockedDecrement(&g_DllRefCount);
185 }
186
187 // IShellExtInit
188
189 /*
190 IShellExtInit::Initialize()
191 pidlFolder:
192 - for property sheet extension:
193 NULL
194 - for shortcut menu extensions:
195 pidl of folder that contains the item whose shortcut menu is being displayed:
196 - for nondefault drag-and-drop menu extensions:
197 pidl of target folder: for nondefault drag-and-drop menu extensions
198 pidlFolder == NULL in (win10): for context menu
199 */
200
Initialize(LPCITEMIDLIST pidlFolder,LPDATAOBJECT dataObject,HKEY)201 Z7_COMWF_B CZipContextMenu::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT dataObject, HKEY /* hkeyProgID */)
202 {
203 COM_TRY_BEGIN
204 ODS("==== CZipContextMenu::Initialize START")
205 _isMenuForFM = false;
206 _fileNames_WereReduced = true;
207 _dropMode = false;
208 _attribs.Clear();
209 _fileNames.Clear();
210 _dropPath.Empty();
211
212 if (pidlFolder)
213 {
214 ODS("==== CZipContextMenu::Initialize (pidlFolder != 0)")
215 #ifndef UNDER_CE
216 if (NShell::GetPathFromIDList(pidlFolder, _dropPath))
217 {
218 ODS("==== CZipContextMenu::Initialize path from (pidl):")
219 ODS_U(_dropPath);
220 /* win10 : path with "\\\\?\\\" prefix is returned by GetPathFromIDList, if path is long
221 we can remove super prefix here. But probably prefix
222 is not problem for following 7-zip code.
223 so we don't remove super prefix */
224 NFile::NName::If_IsSuperPath_RemoveSuperPrefix(_dropPath);
225 NName::NormalizeDirPathPrefix(_dropPath);
226 _dropMode = !_dropPath.IsEmpty();
227 }
228 else
229 #endif
230 _dropPath.Empty();
231 }
232
233 if (!dataObject)
234 return E_INVALIDARG;
235
236 #ifndef UNDER_CE
237
238 RINOK(NShell::DataObject_GetData_HDROP_or_IDLIST_Names(dataObject, _fileNames))
239 // for (unsigned y = 0; y < 10000; y++)
240 if (NShell::DataObject_GetData_FILE_ATTRS(dataObject, _attribs) != S_OK)
241 _attribs.Clear();
242
243 #endif
244
245 ODS_SPRF_s(sprintf(s, "==== CZipContextMenu::Initialize END _files=%d",
246 _fileNames.Size()))
247
248 return S_OK;
249 COM_TRY_END
250 }
251
252
253 /////////////////////////////
254 // IContextMenu
255
256 static LPCSTR const kMainVerb = "SevenZip";
257 static LPCSTR const kOpenCascadedVerb = "SevenZip.OpenWithType.";
258 static LPCSTR const kCheckSumCascadedVerb = "SevenZip.Checksum";
259
260
261 struct CContextMenuCommand
262 {
263 UInt32 flag;
264 CZipContextMenu::enum_CommandInternalID CommandInternalID;
265 LPCSTR Verb;
266 UINT ResourceID;
267 };
268
269 #define CMD_REC(cns, verb, ids) { NContextMenuFlags::cns, CZipContextMenu::cns, verb, ids }
270
271 static const CContextMenuCommand g_Commands[] =
272 {
273 CMD_REC( kOpen, "Open", IDS_CONTEXT_OPEN),
274 CMD_REC( kExtract, "Extract", IDS_CONTEXT_EXTRACT),
275 CMD_REC( kExtractHere, "ExtractHere", IDS_CONTEXT_EXTRACT_HERE),
276 CMD_REC( kExtractTo, "ExtractTo", IDS_CONTEXT_EXTRACT_TO),
277 CMD_REC( kTest, "Test", IDS_CONTEXT_TEST),
278 CMD_REC( kCompress, "Compress", IDS_CONTEXT_COMPRESS),
279 CMD_REC( kCompressEmail, "CompressEmail", IDS_CONTEXT_COMPRESS_EMAIL),
280 CMD_REC( kCompressTo7z, "CompressTo7z", IDS_CONTEXT_COMPRESS_TO),
281 CMD_REC( kCompressTo7zEmail, "CompressTo7zEmail", IDS_CONTEXT_COMPRESS_TO_EMAIL),
282 CMD_REC( kCompressToZip, "CompressToZip", IDS_CONTEXT_COMPRESS_TO),
283 CMD_REC( kCompressToZipEmail, "CompressToZipEmail", IDS_CONTEXT_COMPRESS_TO_EMAIL)
284 };
285
286
287 struct CHashCommand
288 {
289 CZipContextMenu::enum_CommandInternalID CommandInternalID;
290 LPCSTR UserName;
291 LPCSTR MethodName;
292 };
293
294 static const CHashCommand g_HashCommands[] =
295 {
296 { CZipContextMenu::kHash_CRC32, "CRC-32", "CRC32" },
297 { CZipContextMenu::kHash_CRC64, "CRC-64", "CRC64" },
298 { CZipContextMenu::kHash_XXH64, "XXH64", "XXH64" },
299 { CZipContextMenu::kHash_SHA1, "SHA-1", "SHA1" },
300 { CZipContextMenu::kHash_SHA256, "SHA-256", "SHA256" },
301 { CZipContextMenu::kHash_BLAKE2SP, "BLAKE2sp", "BLAKE2sp" },
302 { CZipContextMenu::kHash_All, "*", "*" },
303 { CZipContextMenu::kHash_Generate_SHA256, "SHA-256 -> file.sha256", "SHA256" },
304 { CZipContextMenu::kHash_TestArc, "Checksum : Test", "Hash" }
305 };
306
307
FindCommand(CZipContextMenu::enum_CommandInternalID & id)308 static int FindCommand(CZipContextMenu::enum_CommandInternalID &id)
309 {
310 for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_Commands); i++)
311 if (g_Commands[i].CommandInternalID == id)
312 return (int)i;
313 return -1;
314 }
315
316
FillCommand(enum_CommandInternalID id,UString & mainString,CCommandMapItem & cmi) const317 void CZipContextMenu::FillCommand(enum_CommandInternalID id, UString &mainString, CCommandMapItem &cmi) const
318 {
319 mainString.Empty();
320 const int i = FindCommand(id);
321 if (i < 0)
322 throw 201908;
323 const CContextMenuCommand &command = g_Commands[(unsigned)i];
324 cmi.CommandInternalID = command.CommandInternalID;
325 cmi.Verb = kMainVerb;
326 cmi.Verb += command.Verb;
327 // cmi.HelpString = cmi.Verb;
328 LangString(command.ResourceID, mainString);
329 cmi.UserString = mainString;
330 }
331
332
LangStringAlt(UInt32 id,const char * altString)333 static UString LangStringAlt(UInt32 id, const char *altString)
334 {
335 UString s = LangString(id);
336 if (s.IsEmpty())
337 s = altString;
338 return s;
339 }
340
341
AddCommand(enum_CommandInternalID id,UString & mainString,CCommandMapItem & cmi)342 void CZipContextMenu::AddCommand(enum_CommandInternalID id, UString &mainString, CCommandMapItem &cmi)
343 {
344 FillCommand(id, mainString, cmi);
345 _commandMap.Add(cmi);
346 }
347
348
349
350 /*
351 note: old msdn article:
352 Duplicate Menu Items In the File Menu For a Shell Context Menu Extension (214477)
353 ----------
354 On systems with Shell32.dll version 4.71 or higher, a context menu extension
355 for a file folder that inserts one or more pop-up menus results in duplicates
356 of these menu items.
357 This occurs when the file menu is activated more than once for the selected object.
358
359 CAUSE
360 In a context menu extension, if pop-up menus are inserted using InsertMenu
361 or AppendMenu, then the ID for the pop-up menu item cannot be specified.
362 Instead, this field should take in the HMENU of the pop-up menu.
363 Because the ID is not specified for the pop-up menu item, the Shell does
364 not keep track of the menu item if the file menu is pulled down multiple times.
365 As a result, the pop-up menu items are added multiple times in the context menu.
366
367 This problem occurs only when the file menu is pulled down, and does not happen
368 when the context menu is invoked by using the right button or the context menu key.
369 RESOLUTION
370 To work around this problem, use InsertMenuItem and specify the ID of the
371 pop-up menu item in the wID member of the MENUITEMINFO structure.
372 */
373
MyInsertMenu(CMenu & menu,unsigned pos,UINT id,const UString & s,HBITMAP bitmap)374 static void MyInsertMenu(CMenu &menu, unsigned pos, UINT id, const UString &s, HBITMAP bitmap)
375 {
376 if (!menu)
377 return;
378 CMenuItem mi;
379 mi.fType = MFT_STRING;
380 mi.fMask = MIIM_TYPE | MIIM_ID;
381 if (bitmap)
382 mi.fMask |= MIIM_CHECKMARKS;
383 mi.wID = id;
384 mi.StringValue = s;
385 mi.hbmpUnchecked = bitmap;
386 // mi.hbmpChecked = bitmap; // do we need hbmpChecked ???
387 if (!menu.InsertItem(pos, true, mi))
388 throw 20190816;
389
390 // SetMenuItemBitmaps also works
391 // ::SetMenuItemBitmaps(menu, pos, MF_BYPOSITION, bitmap, NULL);
392 }
393
394
MyAddSubMenu(CObjectVector<CZipContextMenu::CCommandMapItem> & _commandMap,const char * verb,CMenu & menu,unsigned pos,UINT id,const UString & s,HMENU hSubMenu,HBITMAP bitmap)395 static void MyAddSubMenu(
396 CObjectVector<CZipContextMenu::CCommandMapItem> &_commandMap,
397 const char *verb,
398 CMenu &menu, unsigned pos, UINT id, const UString &s, HMENU hSubMenu, HBITMAP bitmap)
399 {
400 CZipContextMenu::CCommandMapItem cmi;
401 cmi.CommandInternalID = CZipContextMenu::kCommandNULL;
402 cmi.Verb = verb;
403 cmi.IsPopup = true;
404 // cmi.HelpString = verb;
405 cmi.UserString = s;
406 _commandMap.Add(cmi);
407
408 if (!menu)
409 return;
410
411 CMenuItem mi;
412 mi.fType = MFT_STRING;
413 mi.fMask = MIIM_SUBMENU | MIIM_TYPE | MIIM_ID;
414 if (bitmap)
415 mi.fMask |= MIIM_CHECKMARKS;
416 mi.wID = id;
417 mi.hSubMenu = hSubMenu;
418 mi.hbmpUnchecked = bitmap;
419
420 mi.StringValue = s;
421 if (!menu.InsertItem(pos, true, mi))
422 throw 20190817;
423 }
424
425
426 static const char * const kArcExts[] =
427 {
428 "7z"
429 , "bz2"
430 , "gz"
431 , "rar"
432 , "zip"
433 };
434
IsItArcExt(const UString & ext)435 static bool IsItArcExt(const UString &ext)
436 {
437 for (unsigned i = 0; i < Z7_ARRAY_SIZE(kArcExts); i++)
438 if (ext.IsEqualTo_Ascii_NoCase(kArcExts[i]))
439 return true;
440 return false;
441 }
442
443 UString GetSubFolderNameForExtract(const UString &arcName);
GetSubFolderNameForExtract(const UString & arcName)444 UString GetSubFolderNameForExtract(const UString &arcName)
445 {
446 int dotPos = arcName.ReverseFind_Dot();
447 if (dotPos < 0)
448 return Get_Correct_FsFile_Name(arcName) + L'~';
449
450 const UString ext = arcName.Ptr(dotPos + 1);
451 UString res = arcName.Left(dotPos);
452 res.TrimRight();
453 dotPos = res.ReverseFind_Dot();
454 if (dotPos > 0)
455 {
456 const UString ext2 = res.Ptr(dotPos + 1);
457 if ((ext.IsEqualTo_Ascii_NoCase("001") && IsItArcExt(ext2))
458 || (ext.IsEqualTo_Ascii_NoCase("rar") &&
459 ( ext2.IsEqualTo_Ascii_NoCase("part001")
460 || ext2.IsEqualTo_Ascii_NoCase("part01")
461 || ext2.IsEqualTo_Ascii_NoCase("part1"))))
462 res.DeleteFrom(dotPos);
463 res.TrimRight();
464 }
465 return Get_Correct_FsFile_Name(res);
466 }
467
ReduceString(UString & s)468 static void ReduceString(UString &s)
469 {
470 const unsigned kMaxSize = 64;
471 if (s.Len() <= kMaxSize)
472 return;
473 s.Delete(kMaxSize / 2, s.Len() - kMaxSize);
474 s.Insert(kMaxSize / 2, L" ... ");
475 }
476
GetQuotedReducedString(const UString & s)477 static UString GetQuotedReducedString(const UString &s)
478 {
479 UString s2 = s;
480 ReduceString(s2);
481 s2.Replace(L"&", L"&&");
482 return GetQuotedString(s2);
483 }
484
MyFormatNew_ReducedName(UString & s,const UString & name)485 static void MyFormatNew_ReducedName(UString &s, const UString &name)
486 {
487 s = MyFormatNew(s, GetQuotedReducedString(name));
488 }
489
490 static const char * const kExtractExcludeExtensions =
491 " 3gp"
492 " aac ans ape asc asm asp aspx avi awk"
493 " bas bat bmp"
494 " c cs cls clw cmd cpp csproj css ctl cxx"
495 " def dep dlg dsp dsw"
496 " eps"
497 " f f77 f90 f95 fla flac frm"
498 " gif"
499 " h hpp hta htm html hxx"
500 " ico idl inc ini inl"
501 " java jpeg jpg js"
502 " la lnk log"
503 " mak manifest wmv mov mp3 mp4 mpe mpeg mpg m4a"
504 " ofr ogg"
505 " pac pas pdf php php3 php4 php5 phptml pl pm png ps py pyo"
506 " ra rb rc reg rka rm rtf"
507 " sed sh shn shtml sln sql srt swa"
508 " tcl tex tiff tta txt"
509 " vb vcproj vbs"
510 " mkv wav webm wma wv"
511 " xml xsd xsl xslt"
512 " ";
513
514 /*
515 static const char * const kNoOpenAsExtensions =
516 " 7z arj bz2 cab chm cpio flv gz lha lzh lzma rar swm tar tbz2 tgz wim xar xz z zip ";
517 */
518
519 static const char * const kOpenTypes[] =
520 {
521 ""
522 , "*"
523 , "#"
524 , "#:e"
525 // , "#:a"
526 , "7z"
527 , "zip"
528 , "cab"
529 , "rar"
530 };
531
532
533 bool FindExt(const char *p, const UString &name, CStringFinder &finder);
FindExt(const char * p,const UString & name,CStringFinder & finder)534 bool FindExt(const char *p, const UString &name, CStringFinder &finder)
535 {
536 const int dotPos = name.ReverseFind_Dot();
537 if (dotPos < 0 || dotPos == (int)name.Len() - 1)
538 return false;
539 return finder.FindWord_In_LowCaseAsciiList_NoCase(p, name.Ptr(dotPos + 1));
540 }
541
542 /* returns false, if extraction of that file extension is not expected */
DoNeedExtract(const UString & name,CStringFinder & finder)543 static bool DoNeedExtract(const UString &name, CStringFinder &finder)
544 {
545 // for (int y = 0; y < 1000; y++) FindExt(kExtractExcludeExtensions, name);
546 return !FindExt(kExtractExcludeExtensions, name, finder);
547 }
548
549 // we must use diferent Verbs for Popup subMenu.
AddMapItem_ForSubMenu(const char * verb)550 void CZipContextMenu::AddMapItem_ForSubMenu(const char *verb)
551 {
552 CCommandMapItem cmi;
553 cmi.CommandInternalID = kCommandNULL;
554 cmi.Verb = verb;
555 // cmi.HelpString = verb;
556 _commandMap.Add(cmi);
557 }
558
559
RETURN_WIN32_LastError_AS_HRESULT()560 static HRESULT RETURN_WIN32_LastError_AS_HRESULT()
561 {
562 DWORD lastError = ::GetLastError();
563 if (lastError == 0)
564 return E_FAIL;
565 return HRESULT_FROM_WIN32(lastError);
566 }
567
568
569 /*
570 we add CCommandMapItem to _commandMap for each new Menu ID.
571 so then we use _commandMap[offset].
572 That way we can execute commands that have menu item.
573 Another non-implemented way:
574 We can return the number off all possible commands in QueryContextMenu().
575 so the caller could call InvokeCommand() via string verb even
576 without using menu items.
577 */
578
579
QueryContextMenu(HMENU hMenu,UINT indexMenu,UINT commandIDFirst,UINT commandIDLast,UINT flags)580 Z7_COMWF_B CZipContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu,
581 UINT commandIDFirst, UINT commandIDLast, UINT flags)
582 {
583 ODS("+ QueryContextMenu()")
584 COM_TRY_BEGIN
585 try {
586
587 _commandMap.Clear();
588
589 ODS_SPRF_s(sprintf(s, "QueryContextMenu: index=%u first=%u last=%u flags=%x _files=%u",
590 indexMenu, commandIDFirst, commandIDLast, flags, _fileNames.Size()))
591 /*
592 for (UInt32 i = 0; i < _fileNames.Size(); i++)
593 {
594 ODS_U(_fileNames[i])
595 }
596 */
597
598 #define MAKE_HRESULT_SUCCESS_FAC0(code) (HRESULT)(code)
599
600 if (_fileNames.Size() == 0)
601 {
602 return MAKE_HRESULT_SUCCESS_FAC0(0);
603 // return E_INVALIDARG;
604 }
605
606 if (commandIDFirst > commandIDLast)
607 return E_INVALIDARG;
608
609 UINT currentCommandID = commandIDFirst;
610
611 if ((flags & 0x000F) != CMF_NORMAL
612 && (flags & CMF_VERBSONLY) == 0
613 && (flags & CMF_EXPLORE) == 0)
614 return MAKE_HRESULT_SUCCESS_FAC0(currentCommandID - commandIDFirst);
615 // return MAKE_HRESULT_SUCCESS_FAC0(currentCommandID);
616 // 19.01 : we changed from (currentCommandID) to (currentCommandID - commandIDFirst)
617 // why it was so before?
618
619 #ifdef Z7_LANG
620 LoadLangOneTime();
621 #endif
622
623 CMenu popupMenu;
624 CMenuDestroyer menuDestroyer;
625
626 ODS("### 40")
627 CContextMenuInfo ci;
628 ci.Load();
629 ODS("### 44")
630
631 _elimDup = ci.ElimDup;
632 _writeZone = ci.WriteZone;
633
634 HBITMAP bitmap = NULL;
635 if (ci.MenuIcons.Val)
636 {
637 ODS("### 45")
638 if (!_bitmap)
639 _bitmap = ::LoadBitmap(g_hInstance, MAKEINTRESOURCE(IDB_MENU_LOGO));
640 bitmap = _bitmap;
641 }
642
643 UINT subIndex = indexMenu;
644
645 ODS("### 50")
646
647 if (ci.Cascaded.Val)
648 {
649 if (hMenu)
650 if (!popupMenu.CreatePopup())
651 return RETURN_WIN32_LastError_AS_HRESULT();
652 menuDestroyer.Attach(popupMenu);
653
654 /* 9.31: we commented the following code. Probably we don't need.
655 Check more systems. Maybe it was for old Windows? */
656 /*
657 AddMapItem_ForSubMenu();
658 currentCommandID++;
659 */
660 subIndex = 0;
661 }
662 else
663 {
664 popupMenu.Attach(hMenu);
665 CMenuItem mi;
666 mi.fType = MFT_SEPARATOR;
667 mi.fMask = MIIM_TYPE;
668 if (hMenu)
669 popupMenu.InsertItem(subIndex++, true, mi);
670 }
671
672 const UInt32 contextMenuFlags = ci.Flags;
673
674 NFind::CFileInfo fi0;
675 FString folderPrefix;
676
677 if (_fileNames.Size() > 0)
678 {
679 const UString &fileName = _fileNames.Front();
680
681 #if defined(_WIN32) && !defined(UNDER_CE)
682 if (NName::IsDevicePath(us2fs(fileName)))
683 {
684 // CFileInfo::Find can be slow for device files. So we don't call it.
685 // we need only name here.
686 fi0.Name = us2fs(fileName.Ptr(NName::kDevicePathPrefixSize));
687 folderPrefix =
688 #ifdef UNDER_CE
689 "\\";
690 #else
691 "C:\\";
692 #endif
693 }
694 else
695 #endif
696 {
697 if (!fi0.Find(us2fs(fileName)))
698 {
699 throw 20190820;
700 // return RETURN_WIN32_LastError_AS_HRESULT();
701 }
702 GetOnlyDirPrefix(us2fs(fileName), folderPrefix);
703 }
704 }
705
706 ODS("### 100")
707
708 UString mainString;
709 CStringFinder finder;
710 UStringVector fileNames_Reduced;
711 const unsigned k_Explorer_NumReducedItems = 16;
712 const bool needReduce = !_isMenuForFM && (_fileNames.Size() >= k_Explorer_NumReducedItems);
713 _fileNames_WereReduced = needReduce;
714 // _fileNames_WereReduced = true; // for debug;
715 const UStringVector *fileNames = &_fileNames;
716 if (needReduce)
717 {
718 for (unsigned i = 0; i < k_Explorer_NumReducedItems
719 && i < _fileNames.Size(); i++)
720 fileNames_Reduced.Add(_fileNames[i]);
721 fileNames = &fileNames_Reduced;
722 }
723
724 /*
725 if (_fileNames.Size() == k_Explorer_NumReducedItems) // for debug
726 {
727 for (int i = 0; i < 10; i++)
728 {
729 CCommandMapItem cmi;
730 AddCommand(kCompressToZipEmail, mainString, cmi);
731 MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
732 }
733 }
734 */
735
736 if (_fileNames.Size() == 1 && currentCommandID + 14 <= commandIDLast)
737 {
738 if (!fi0.IsDir() && DoNeedExtract(fs2us(fi0.Name), finder))
739 {
740 // Open
741 const bool thereIsMainOpenItem = ((contextMenuFlags & NContextMenuFlags::kOpen) != 0);
742 if (thereIsMainOpenItem)
743 {
744 CCommandMapItem cmi;
745 AddCommand(kOpen, mainString, cmi);
746 MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
747 }
748 if ((contextMenuFlags & NContextMenuFlags::kOpenAs) != 0
749 // && (!thereIsMainOpenItem || !FindExt(kNoOpenAsExtensions, fi0.Name))
750 && hMenu // we want to reduce number of menu items below 16
751 )
752 {
753 CMenu subMenu;
754 if (!hMenu || subMenu.CreatePopup())
755 {
756 MyAddSubMenu(_commandMap, kOpenCascadedVerb, popupMenu, subIndex++, currentCommandID++, LangString(IDS_CONTEXT_OPEN), subMenu, bitmap);
757 _commandMap.Back().CtxCommandType = CtxCommandType_OpenRoot;
758
759 UINT subIndex2 = 0;
760 for (unsigned i = (thereIsMainOpenItem ? 1 : 0); i < Z7_ARRAY_SIZE(kOpenTypes); i++)
761 {
762 CCommandMapItem cmi;
763 if (i == 0)
764 FillCommand(kOpen, mainString, cmi);
765 else
766 {
767 mainString = kOpenTypes[i];
768 cmi.CommandInternalID = kOpen;
769 cmi.Verb = kMainVerb;
770 cmi.Verb += ".Open.";
771 cmi.Verb += mainString;
772 // cmi.HelpString = cmi.Verb;
773 cmi.ArcType = mainString;
774 cmi.CtxCommandType = CtxCommandType_OpenChild;
775 }
776 _commandMap.Add(cmi);
777 Set_UserString_in_LastCommand(mainString);
778 MyInsertMenu(subMenu, subIndex2++, currentCommandID++, mainString, bitmap);
779 }
780
781 subMenu.Detach();
782 }
783 }
784 }
785 }
786
787 ODS("### 150")
788
789 if (_fileNames.Size() > 0 && currentCommandID + 10 <= commandIDLast)
790 {
791 ODS("### needExtract list START")
792 const bool needExtendedVerbs = ((flags & Z7_WIN_CMF_EXTENDEDVERBS) != 0);
793 // || _isMenuForFM;
794 bool needExtract = true;
795 bool areDirs = fi0.IsDir() || (unsigned)_attribs.FirstDirIndex < k_Explorer_NumReducedItems;
796 if (!needReduce)
797 areDirs = areDirs || (_attribs.FirstDirIndex != -1);
798 if (areDirs)
799 needExtract = false;
800
801 if (!needExtendedVerbs)
802 if (needExtract)
803 {
804 UString name;
805 const unsigned numItemsCheck = fileNames->Size();
806 for (unsigned i = 0; i < numItemsCheck; i++)
807 {
808 const UString &a = (*fileNames)[i];
809 const int slash = a.ReverseFind_PathSepar();
810 name = a.Ptr(slash + 1);
811 // for (int y = 0; y < 600; y++) // for debug
812 const bool needExtr2 = DoNeedExtract(name, finder);
813 if (!needExtr2)
814 {
815 needExtract = needExtr2;
816 break;
817 }
818 }
819 }
820 ODS("### needExtract list END")
821
822 if (needExtract)
823 {
824 {
825 UString baseFolder = fs2us(folderPrefix);
826 if (_dropMode)
827 baseFolder = _dropPath;
828
829 UString specFolder ('*');
830 if (_fileNames.Size() == 1)
831 specFolder = GetSubFolderNameForExtract(fs2us(fi0.Name));
832 specFolder.Add_PathSepar();
833
834 if ((contextMenuFlags & NContextMenuFlags::kExtract) != 0)
835 {
836 // Extract
837 CCommandMapItem cmi;
838 cmi.Folder = baseFolder + specFolder;
839 AddCommand(kExtract, mainString, cmi);
840 MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
841 }
842
843 if ((contextMenuFlags & NContextMenuFlags::kExtractHere) != 0)
844 {
845 // Extract Here
846 CCommandMapItem cmi;
847 cmi.Folder = baseFolder;
848 AddCommand(kExtractHere, mainString, cmi);
849 MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
850 }
851
852 if ((contextMenuFlags & NContextMenuFlags::kExtractTo) != 0)
853 {
854 // Extract To
855 CCommandMapItem cmi;
856 UString s;
857 cmi.Folder = baseFolder + specFolder;
858 AddCommand(kExtractTo, s, cmi);
859 MyFormatNew_ReducedName(s, specFolder);
860 Set_UserString_in_LastCommand(s);
861 MyInsertMenu(popupMenu, subIndex++, currentCommandID++, s, bitmap);
862 }
863 }
864
865 if ((contextMenuFlags & NContextMenuFlags::kTest) != 0)
866 {
867 // Test
868 CCommandMapItem cmi;
869 AddCommand(kTest, mainString, cmi);
870 // if (_fileNames.Size() == 16) mainString += "_[16]"; // for debug
871 MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
872 }
873 }
874
875 ODS("### CreateArchiveName START")
876 UString arcName_base;
877 const UString arcName = CreateArchiveName(
878 *fileNames,
879 false, // isHash
880 fileNames->Size() == 1 ? &fi0 : NULL,
881 arcName_base);
882 ODS("### CreateArchiveName END")
883 UString arcName_Show = arcName;
884 if (needReduce)
885 {
886 /* we need same arcName_Show for two calls from Explorer:
887 1) reduced call (only first 16 items)
888 2) full call with all items (can be >= 16 items)
889 (fileNames) array was reduced to 16 items.
890 So we will have same (arcName) in both reduced and full calls.
891 If caller (Explorer) uses (reduce_to_first_16_items) scheme,
892 we can use (arcName) here instead of (arcName_base).
893 (arcName_base) has no number in name.
894 */
895 arcName_Show = arcName_base; // we can comment that line
896 /* we use "_" in archive name as sign to user
897 that shows that final archive name can be changed. */
898 arcName_Show += "_";
899 }
900
901 UString arcName_7z = arcName;
902 arcName_7z += ".7z";
903 UString arcName_7z_Show = arcName_Show;
904 arcName_7z_Show += ".7z";
905 UString arcName_zip = arcName;
906 arcName_zip += ".zip";
907 UString arcName_zip_Show = arcName_Show;
908 arcName_zip_Show += ".zip";
909
910
911 // Compress
912 if ((contextMenuFlags & NContextMenuFlags::kCompress) != 0)
913 {
914 CCommandMapItem cmi;
915 if (_dropMode)
916 cmi.Folder = _dropPath;
917 else
918 cmi.Folder = fs2us(folderPrefix);
919 cmi.ArcName = arcName;
920 AddCommand(kCompress, mainString, cmi);
921 MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
922 }
923
924 #ifdef EMAIL_SUPPORT
925 // CompressEmail
926 if ((contextMenuFlags & NContextMenuFlags::kCompressEmail) != 0 && !_dropMode)
927 {
928 CCommandMapItem cmi;
929 cmi.ArcName = arcName;
930 AddCommand(kCompressEmail, mainString, cmi);
931 MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
932 }
933 #endif
934
935 // CompressTo7z
936 if (contextMenuFlags & NContextMenuFlags::kCompressTo7z &&
937 !arcName_7z.IsEqualTo_NoCase(fs2us(fi0.Name)))
938 {
939 CCommandMapItem cmi;
940 UString s;
941 if (_dropMode)
942 cmi.Folder = _dropPath;
943 else
944 cmi.Folder = fs2us(folderPrefix);
945 cmi.ArcName = arcName_7z;
946 cmi.ArcType = "7z";
947 AddCommand(kCompressTo7z, s, cmi);
948 MyFormatNew_ReducedName(s, arcName_7z_Show);
949 Set_UserString_in_LastCommand(s);
950 MyInsertMenu(popupMenu, subIndex++, currentCommandID++, s, bitmap);
951 }
952
953 #ifdef EMAIL_SUPPORT
954 // CompressTo7zEmail
955 if ((contextMenuFlags & NContextMenuFlags::kCompressTo7zEmail) != 0 && !_dropMode)
956 {
957 CCommandMapItem cmi;
958 UString s;
959 cmi.ArcName = arcName_7z;
960 cmi.ArcType = "7z";
961 AddCommand(kCompressTo7zEmail, s, cmi);
962 MyFormatNew_ReducedName(s, arcName_7z_Show);
963 Set_UserString_in_LastCommand(s);
964 MyInsertMenu(popupMenu, subIndex++, currentCommandID++, s, bitmap);
965 }
966 #endif
967
968 // CompressToZip
969 if (contextMenuFlags & NContextMenuFlags::kCompressToZip &&
970 !arcName_zip.IsEqualTo_NoCase(fs2us(fi0.Name)))
971 {
972 CCommandMapItem cmi;
973 UString s;
974 if (_dropMode)
975 cmi.Folder = _dropPath;
976 else
977 cmi.Folder = fs2us(folderPrefix);
978 cmi.ArcName = arcName_zip;
979 cmi.ArcType = "zip";
980 AddCommand(kCompressToZip, s, cmi);
981 MyFormatNew_ReducedName(s, arcName_zip_Show);
982 Set_UserString_in_LastCommand(s);
983 MyInsertMenu(popupMenu, subIndex++, currentCommandID++, s, bitmap);
984 }
985
986 #ifdef EMAIL_SUPPORT
987 // CompressToZipEmail
988 if ((contextMenuFlags & NContextMenuFlags::kCompressToZipEmail) != 0 && !_dropMode)
989 {
990 CCommandMapItem cmi;
991 UString s;
992 cmi.ArcName = arcName_zip;
993 cmi.ArcType = "zip";
994 AddCommand(kCompressToZipEmail, s, cmi);
995 MyFormatNew_ReducedName(s, arcName_zip_Show);
996 Set_UserString_in_LastCommand(s);
997 MyInsertMenu(popupMenu, subIndex++, currentCommandID++, s, bitmap);
998 }
999 #endif
1000 }
1001
1002 ODS("### 300")
1003
1004 // don't use InsertMenu: See MSDN:
1005 // PRB: Duplicate Menu Items In the File Menu For a Shell Context Menu Extension
1006 // ID: Q214477
1007
1008 if (ci.Cascaded.Val)
1009 {
1010 CMenu menu;
1011 menu.Attach(hMenu);
1012 menuDestroyer.Disable();
1013 MyAddSubMenu(_commandMap, kMainVerb, menu, indexMenu++, currentCommandID++, (UString)"7-Zip",
1014 popupMenu, // popupMenu.Detach(),
1015 bitmap);
1016 }
1017 else
1018 {
1019 // popupMenu.Detach();
1020 indexMenu = subIndex;
1021 }
1022
1023 ODS("### 350")
1024
1025 const bool needCrc = ((contextMenuFlags &
1026 (NContextMenuFlags::kCRC |
1027 NContextMenuFlags::kCRC_Cascaded)) != 0);
1028
1029 if (
1030 // !_isMenuForFM && // 21.04: we don't hide CRC SHA menu in 7-Zip FM
1031 needCrc
1032 && currentCommandID + 1 < commandIDLast)
1033 {
1034 CMenu subMenu;
1035 // CMenuDestroyer menuDestroyer_CRC;
1036
1037 UINT subIndex_CRC = 0;
1038
1039 if (!hMenu || subMenu.CreatePopup())
1040 {
1041 // menuDestroyer_CRC.Attach(subMenu);
1042 const bool insertHashMenuTo7zipMenu = (ci.Cascaded.Val
1043 && (contextMenuFlags & NContextMenuFlags::kCRC_Cascaded) != 0);
1044
1045 CMenu menu;
1046 {
1047 unsigned indexInParent;
1048 if (insertHashMenuTo7zipMenu)
1049 {
1050 indexInParent = subIndex;
1051 menu.Attach(popupMenu);
1052 }
1053 else
1054 {
1055 indexInParent = indexMenu;
1056 menu.Attach(hMenu);
1057 // menuDestroyer_CRC.Disable();
1058 }
1059 MyAddSubMenu(_commandMap, kCheckSumCascadedVerb, menu, indexInParent++, currentCommandID++, (UString)"CRC SHA", subMenu,
1060 /* insertHashMenuTo7zipMenu ? NULL : */ bitmap);
1061 _commandMap.Back().CtxCommandType = CtxCommandType_CrcRoot;
1062 if (!insertHashMenuTo7zipMenu)
1063 indexMenu = indexInParent;
1064 }
1065
1066 ODS("### HashCommands")
1067
1068 for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_HashCommands); i++)
1069 {
1070 if (currentCommandID >= commandIDLast)
1071 break;
1072 const CHashCommand &hc = g_HashCommands[i];
1073 CCommandMapItem cmi;
1074 cmi.CommandInternalID = hc.CommandInternalID;
1075 cmi.Verb = kCheckSumCascadedVerb;
1076 cmi.Verb.Add_Dot();
1077 UString s;
1078 s += hc.UserName;
1079
1080 if (hc.CommandInternalID == kHash_Generate_SHA256)
1081 {
1082 cmi.Verb += "Generate";
1083 {
1084 popupMenu.Attach(hMenu);
1085 CMenuItem mi;
1086 mi.fType = MFT_SEPARATOR;
1087 mi.fMask = MIIM_TYPE;
1088 subMenu.InsertItem(subIndex_CRC++, true, mi);
1089 }
1090
1091 UString name;
1092 UString showName;
1093 ODS("### Hash CreateArchiveName Start")
1094 // for (int y = 0; y < 10000; y++) // for debug
1095 // if (fileNames->Size() == 1) name = fs2us(fi0.Name); else
1096 name = CreateArchiveName(
1097 *fileNames,
1098 true, // isHash
1099 fileNames->Size() == 1 ? &fi0 : NULL,
1100 showName);
1101 if (needReduce)
1102 showName += "_";
1103 else
1104 showName = name;
1105
1106 ODS("### Hash CreateArchiveName END")
1107 name += ".sha256";
1108 showName += ".sha256";
1109 cmi.Folder = fs2us(folderPrefix);
1110 cmi.ArcName = name;
1111 s = "SHA-256 -> ";
1112 s += showName;
1113 }
1114 else if (hc.CommandInternalID == kHash_TestArc)
1115 {
1116 cmi.Verb += "Test";
1117 s = LangStringAlt(IDS_CONTEXT_TEST, "Test archive");
1118 s += " : ";
1119 s += GetNameOfProperty(kpidChecksum, UString("Checksum"));
1120 }
1121 else
1122 cmi.Verb += "Calc";
1123
1124 cmi.Verb.Add_Dot();
1125 cmi.Verb += hc.MethodName;
1126
1127 // cmi.HelpString = cmi.Verb;
1128 cmi.UserString = s;
1129 cmi.CtxCommandType = CtxCommandType_CrcChild;
1130 _commandMap.Add(cmi);
1131 MyInsertMenu(subMenu, subIndex_CRC++, currentCommandID++, s, bitmap);
1132 ODS("### 380")
1133 }
1134
1135 subMenu.Detach();
1136 }
1137 }
1138
1139 popupMenu.Detach();
1140 /*
1141 if (!ci.Cascaded.Val)
1142 indexMenu = subIndex;
1143 */
1144 const unsigned numCommands = currentCommandID - commandIDFirst;
1145 ODS("+ QueryContextMenu() END")
1146 ODS_SPRF_s(sprintf(s, "Commands=%u currentCommandID - commandIDFirst = %u",
1147 _commandMap.Size(), numCommands))
1148 if (_commandMap.Size() != numCommands)
1149 throw 20190818;
1150 /*
1151 FOR_VECTOR (k, _commandMap)
1152 {
1153 ODS_U(_commandMap[k].Verb);
1154 }
1155 */
1156 }
1157 catch(...)
1158 {
1159 ODS_SPRF_s(sprintf(s, "catch() exception: Commands=%u", _commandMap.Size()))
1160 if (_commandMap.Size() == 0)
1161 throw;
1162 }
1163 /* we added some menu items already : num_added_menu_items,
1164 So we MUST return (number_of_defined_ids), where (number_of_defined_ids >= num_added_menu_items)
1165 This will prevent incorrect menu working, when same IDs can be
1166 assigned in multiple menu items from different subhandlers.
1167 And we must add items to _commandMap before adding to menu.
1168 */
1169 return MAKE_HRESULT_SUCCESS_FAC0(_commandMap.Size());
1170 COM_TRY_END
1171 }
1172
1173
FindVerb(const UString & verb) const1174 int CZipContextMenu::FindVerb(const UString &verb) const
1175 {
1176 FOR_VECTOR (i, _commandMap)
1177 if (_commandMap[i].Verb == verb)
1178 return (int)i;
1179 return -1;
1180 }
1181
Get7zFmPath()1182 static UString Get7zFmPath()
1183 {
1184 return fs2us(NWindows::NDLL::GetModuleDirPrefix()) + L"7zFM.exe";
1185 }
1186
1187
InvokeCommand(LPCMINVOKECOMMANDINFO commandInfo)1188 Z7_COMWF_B CZipContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO commandInfo)
1189 {
1190 COM_TRY_BEGIN
1191
1192 ODS("==== CZipContextMenu::InvokeCommand()")
1193
1194 #ifdef SHOW_DEBUG_CTX_MENU
1195
1196 ODS_SPRF_s(sprintf(s, ": InvokeCommand: cbSize=%u flags=%x ",
1197 (unsigned)commandInfo->cbSize, (unsigned)commandInfo->fMask))
1198
1199 PrintStringA("Verb", commandInfo->lpVerb);
1200 PrintStringA("Parameters", commandInfo->lpParameters);
1201 PrintStringA("Directory", commandInfo->lpDirectory);
1202 #endif
1203
1204 int commandOffset = -1;
1205
1206 // xp64 / Win10 : explorer.exe sends 0 in lpVerbW
1207 // MSDN: if (IS_INTRESOURCE(lpVerbW)), we must use LOWORD(lpVerb) as command offset
1208
1209 // FIXME: old MINGW doesn't define CMINVOKECOMMANDINFOEX / CMIC_MASK_UNICODE
1210 #if !defined(UNDER_CE) && defined(CMIC_MASK_UNICODE)
1211 bool unicodeVerb = false;
1212 if (commandInfo->cbSize == sizeof(CMINVOKECOMMANDINFOEX) &&
1213 (commandInfo->fMask & CMIC_MASK_UNICODE) != 0)
1214 {
1215 LPCMINVOKECOMMANDINFOEX commandInfoEx = (LPCMINVOKECOMMANDINFOEX)commandInfo;
1216 if (!MY_IS_INTRESOURCE(commandInfoEx->lpVerbW))
1217 {
1218 unicodeVerb = true;
1219 commandOffset = FindVerb(commandInfoEx->lpVerbW);
1220 }
1221
1222 #ifdef SHOW_DEBUG_CTX_MENU
1223 PrintStringW("VerbW", commandInfoEx->lpVerbW);
1224 PrintStringW("ParametersW", commandInfoEx->lpParametersW);
1225 PrintStringW("DirectoryW", commandInfoEx->lpDirectoryW);
1226 PrintStringW("TitleW", commandInfoEx->lpTitleW);
1227 PrintStringA("Title", commandInfoEx->lpTitle);
1228 #endif
1229 }
1230 if (!unicodeVerb)
1231 #endif
1232 {
1233 ODS("use non-UNICODE verb")
1234 // if (HIWORD(commandInfo->lpVerb) == 0)
1235 if (MY_IS_INTRESOURCE(commandInfo->lpVerb))
1236 commandOffset = LOWORD(commandInfo->lpVerb);
1237 else
1238 commandOffset = FindVerb(GetUnicodeString(commandInfo->lpVerb));
1239 }
1240
1241 ODS_SPRF_s(sprintf(s, "commandOffset=%d", commandOffset))
1242
1243 if (/* commandOffset < 0 || */ (unsigned)commandOffset >= _commandMap.Size())
1244 return E_INVALIDARG;
1245 const CCommandMapItem &cmi = _commandMap[(unsigned)commandOffset];
1246 return InvokeCommandCommon(cmi);
1247 COM_TRY_END
1248 }
1249
1250
InvokeCommandCommon(const CCommandMapItem & cmi)1251 HRESULT CZipContextMenu::InvokeCommandCommon(const CCommandMapItem &cmi)
1252 {
1253 const enum_CommandInternalID cmdID = cmi.CommandInternalID;
1254
1255 try
1256 {
1257 switch (cmdID)
1258 {
1259 case kOpen:
1260 {
1261 UString params;
1262 params = GetQuotedString(_fileNames[0]);
1263 if (!cmi.ArcType.IsEmpty())
1264 {
1265 params += " -t";
1266 params += cmi.ArcType;
1267 }
1268 MyCreateProcess(Get7zFmPath(), params);
1269 break;
1270 }
1271 case kExtract:
1272 case kExtractHere:
1273 case kExtractTo:
1274 {
1275 if (_attribs.FirstDirIndex != -1)
1276 {
1277 ShowErrorMessageRes(IDS_SELECT_FILES);
1278 break;
1279 }
1280 ExtractArchives(_fileNames, cmi.Folder,
1281 (cmdID == kExtract), // showDialog
1282 (cmdID == kExtractTo) && _elimDup.Val, // elimDup
1283 _writeZone
1284 );
1285 break;
1286 }
1287 case kTest:
1288 {
1289 TestArchives(_fileNames);
1290 break;
1291 }
1292 case kCompress:
1293 case kCompressEmail:
1294 case kCompressTo7z:
1295 case kCompressTo7zEmail:
1296 case kCompressToZip:
1297 case kCompressToZipEmail:
1298 {
1299 UString arcName = cmi.ArcName;
1300 if (_fileNames_WereReduced)
1301 {
1302 UString arcName_base;
1303 arcName = CreateArchiveName(
1304 _fileNames,
1305 false, // isHash
1306 NULL, // fi0
1307 arcName_base);
1308 const char *postfix = NULL;
1309 if (cmdID == kCompressTo7z ||
1310 cmdID == kCompressTo7zEmail)
1311 postfix = ".7z";
1312 else if (
1313 cmdID == kCompressToZip ||
1314 cmdID == kCompressToZipEmail)
1315 postfix = ".zip";
1316 if (postfix)
1317 arcName += postfix;
1318 }
1319
1320 const bool email =
1321 cmdID == kCompressEmail ||
1322 cmdID == kCompressTo7zEmail ||
1323 cmdID == kCompressToZipEmail;
1324 const bool showDialog =
1325 cmdID == kCompress ||
1326 cmdID == kCompressEmail;
1327 const bool addExtension = showDialog;
1328 CompressFiles(cmi.Folder,
1329 arcName, cmi.ArcType,
1330 addExtension,
1331 _fileNames, email, showDialog,
1332 false // waitFinish
1333 );
1334 break;
1335 }
1336
1337 case kHash_CRC32:
1338 case kHash_CRC64:
1339 case kHash_XXH64:
1340 case kHash_SHA1:
1341 case kHash_SHA256:
1342 case kHash_BLAKE2SP:
1343 case kHash_All:
1344 case kHash_Generate_SHA256:
1345 case kHash_TestArc:
1346 {
1347 for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_HashCommands); i++)
1348 {
1349 const CHashCommand &hc = g_HashCommands[i];
1350 if (hc.CommandInternalID == cmdID)
1351 {
1352 if (cmdID == kHash_TestArc)
1353 {
1354 TestArchives(_fileNames, true); // hashMode
1355 break;
1356 }
1357 UString generateName;
1358 if (cmdID == kHash_Generate_SHA256)
1359 {
1360 generateName = cmi.ArcName;
1361 if (_fileNames_WereReduced)
1362 {
1363 UString arcName_base;
1364 generateName = CreateArchiveName(_fileNames,
1365 true, // isHash
1366 NULL, // fi0
1367 arcName_base);
1368 generateName += ".sha256";
1369 }
1370 }
1371 CalcChecksum(_fileNames, (UString)hc.MethodName,
1372 cmi.Folder, generateName);
1373 break;
1374 }
1375 }
1376 break;
1377 }
1378 case kCommandNULL:
1379 break;
1380 }
1381 }
1382 catch(...)
1383 {
1384 ShowErrorMessage(NULL, L"Error");
1385 }
1386 return S_OK;
1387 }
1388
1389
1390
MyCopyString_isUnicode(void * dest,UINT size,const UString & src,bool writeInUnicode)1391 static void MyCopyString_isUnicode(void *dest, UINT size, const UString &src, bool writeInUnicode)
1392 {
1393 if (size != 0)
1394 size--;
1395 if (writeInUnicode)
1396 {
1397 UString s = src;
1398 s.DeleteFrom(size);
1399 MyStringCopy((wchar_t *)dest, s);
1400 ODS_U(s)
1401 }
1402 else
1403 {
1404 AString s = GetAnsiString(src);
1405 s.DeleteFrom(size);
1406 MyStringCopy((char *)dest, s);
1407 }
1408 }
1409
1410
GetCommandString(UINT commandOffset,UINT uType,UINT *,LPSTR pszName,UINT cchMax)1411 Z7_COMWF_B CZipContextMenu::GetCommandString(
1412 #ifdef Z7_OLD_WIN_SDK
1413 UINT
1414 #else
1415 UINT_PTR
1416 #endif
1417 commandOffset,
1418 UINT uType,
1419 UINT * /* pwReserved */ , LPSTR pszName, UINT cchMax)
1420 {
1421 COM_TRY_BEGIN
1422
1423 ODS("GetCommandString")
1424
1425 const int cmdOffset = (int)commandOffset;
1426
1427 ODS_SPRF_s(sprintf(s, "GetCommandString: cmdOffset=%d uType=%d cchMax = %d",
1428 cmdOffset, uType, cchMax))
1429
1430 if ((uType | GCS_UNICODE) == GCS_VALIDATEW)
1431 {
1432 if (/* cmdOffset < 0 || */ (unsigned)cmdOffset >= _commandMap.Size())
1433 return S_FALSE;
1434 return S_OK;
1435 }
1436
1437 if (/* cmdOffset < 0 || */ (unsigned)cmdOffset >= _commandMap.Size())
1438 {
1439 ODS("------ cmdOffset: E_INVALIDARG")
1440 return E_INVALIDARG;
1441 }
1442
1443 // we use Verb as HelpString
1444 if (cchMax != 0)
1445 if ((uType | GCS_UNICODE) == GCS_VERBW ||
1446 (uType | GCS_UNICODE) == GCS_HELPTEXTW)
1447 {
1448 const CCommandMapItem &cmi = _commandMap[(unsigned)cmdOffset];
1449 MyCopyString_isUnicode(pszName, cchMax, cmi.Verb, (uType & GCS_UNICODE) != 0);
1450 return S_OK;
1451 }
1452
1453 return E_INVALIDARG;
1454
1455 COM_TRY_END
1456 }
1457
1458
1459
1460 // ---------- IExplorerCommand ----------
1461
My_SHStrDupW(LPCWSTR src,LPWSTR * dest)1462 static HRESULT WINAPI My_SHStrDupW(LPCWSTR src, LPWSTR *dest)
1463 {
1464 if (src)
1465 {
1466 const SIZE_T size = (wcslen(src) + 1) * sizeof(WCHAR);
1467 WCHAR *p = (WCHAR *)CoTaskMemAlloc(size);
1468 if (p)
1469 {
1470 memcpy(p, src, size);
1471 *dest = p;
1472 return S_OK;
1473 }
1474 }
1475 *dest = NULL;
1476 return E_OUTOFMEMORY;
1477 }
1478
1479
1480 #define CZipExplorerCommand CZipContextMenu
1481
1482 class CCoTaskWSTR
1483 {
1484 LPWSTR m_str;
1485 Z7_CLASS_NO_COPY(CCoTaskWSTR)
1486 public:
CCoTaskWSTR()1487 CCoTaskWSTR(): m_str(NULL) {}
~CCoTaskWSTR()1488 ~CCoTaskWSTR() { ::CoTaskMemFree(m_str); }
operator &()1489 LPWSTR* operator&() { return &m_str; }
operator LPCWSTR() const1490 operator LPCWSTR () const { return m_str; }
1491 // operator LPCOLESTR() const { return m_str; }
operator bool() const1492 operator bool() const { return m_str != NULL; }
1493 // bool operator!() const { return m_str == NULL; }
1494
1495 /*
1496 void Wipe_and_Free()
1497 {
1498 if (m_str)
1499 {
1500 memset(m_str, 0, ::SysStringLen(m_str) * sizeof(*m_str));
1501 Empty();
1502 }
1503 }
1504 */
1505
1506 private:
1507 /*
1508 CCoTaskWSTR(LPCOLESTR src) { m_str = ::CoTaskMemAlloc(src); }
1509
1510 CCoTaskWSTR& operator=(LPCOLESTR src)
1511 {
1512 ::CoTaskMemFree(m_str);
1513 m_str = ::SysAllocString(src);
1514 return *this;
1515 }
1516
1517
1518 void Empty()
1519 {
1520 ::CoTaskMemFree(m_str);
1521 m_str = NULL;
1522 }
1523 */
1524 };
1525
LoadPaths(IShellItemArray * psiItemArray,UStringVector & paths)1526 static HRESULT LoadPaths(IShellItemArray *psiItemArray, UStringVector &paths)
1527 {
1528 if (psiItemArray)
1529 {
1530 DWORD numItems = 0;
1531 RINOK(psiItemArray->GetCount(&numItems))
1532 {
1533 ODS_(Print_Number(numItems, " ==== LoadPaths START === "))
1534 for (DWORD i = 0; i < numItems; i++)
1535 {
1536 CMyComPtr<IShellItem> item;
1537 RINOK(psiItemArray->GetItemAt(i, &item))
1538 if (item)
1539 {
1540 CCoTaskWSTR displayName;
1541 if (item->GetDisplayName(SIGDN_FILESYSPATH, &displayName) == S_OK
1542 && (bool)displayName)
1543 {
1544 ODS_U(displayName)
1545 paths.Add((LPCWSTR)displayName);
1546 }
1547 }
1548 }
1549 ODS_(Print_Number(numItems, " ==== LoadPaths END === "))
1550 }
1551 }
1552 return S_OK;
1553 }
1554
1555
LoadItems(IShellItemArray * psiItemArray)1556 void CZipExplorerCommand::LoadItems(IShellItemArray *psiItemArray)
1557 {
1558 SubCommands.Clear();
1559 _fileNames.Clear();
1560 {
1561 UStringVector paths;
1562 if (LoadPaths(psiItemArray, paths) != S_OK)
1563 return;
1564 _fileNames = paths;
1565 }
1566 const HRESULT res = QueryContextMenu(
1567 NULL, // hMenu,
1568 0, // indexMenu,
1569 0, // commandIDFirst,
1570 0 + 999, // commandIDLast,
1571 CMF_NORMAL);
1572
1573 if (FAILED(res))
1574 return /* res */;
1575
1576 CZipExplorerCommand *crcHandler = NULL;
1577 CZipExplorerCommand *openHandler = NULL;
1578
1579 bool useCascadedCrc = true; // false;
1580 bool useCascadedOpen = true; // false;
1581
1582 for (unsigned i = 0; i < _commandMap.Size(); i++)
1583 {
1584 const CCommandMapItem &cmi = _commandMap[i];
1585
1586 if (cmi.IsPopup)
1587 if (!cmi.IsSubMenu())
1588 continue;
1589
1590 // if (cmi.IsSubMenu()) continue // for debug
1591
1592 CZipContextMenu *shellExt = new CZipContextMenu();
1593 shellExt->IsRoot = false;
1594
1595 if (cmi.CtxCommandType == CtxCommandType_CrcRoot && !useCascadedCrc)
1596 shellExt->IsSeparator = true;
1597
1598 {
1599 CZipExplorerCommand *handler = this;
1600 if (cmi.CtxCommandType == CtxCommandType_CrcChild && crcHandler)
1601 handler = crcHandler;
1602 else if (cmi.CtxCommandType == CtxCommandType_OpenChild && openHandler)
1603 handler = openHandler;
1604 handler->SubCommands.AddNew() = shellExt;
1605 }
1606
1607 shellExt->_commandMap_Cur.Add(cmi);
1608
1609 ODS_U(cmi.UserString)
1610
1611 if (cmi.CtxCommandType == CtxCommandType_CrcRoot && useCascadedCrc)
1612 crcHandler = shellExt;
1613 if (cmi.CtxCommandType == CtxCommandType_OpenRoot && useCascadedOpen)
1614 {
1615 // ODS("cmi.CtxCommandType == CtxCommandType_OpenRoot");
1616 openHandler = shellExt;
1617 }
1618 }
1619 }
1620
1621
GetTitle(IShellItemArray * psiItemArray,LPWSTR * ppszName)1622 Z7_COMWF_B CZipExplorerCommand::GetTitle(IShellItemArray *psiItemArray, LPWSTR *ppszName)
1623 {
1624 ODS("- GetTitle()")
1625 // COM_TRY_BEGIN
1626 if (IsSeparator)
1627 {
1628 *ppszName = NULL;
1629 return S_FALSE;
1630 }
1631
1632 UString name;
1633 if (IsRoot)
1634 {
1635 LoadItems(psiItemArray);
1636 name = "7-Zip"; // "New"
1637 }
1638 else
1639 name = "7-Zip item";
1640
1641 if (!_commandMap_Cur.IsEmpty())
1642 {
1643 const CCommandMapItem &mi = _commandMap_Cur[0];
1644 // s += mi.Verb;
1645 // s += " : ";
1646 name = mi.UserString;
1647 }
1648
1649 return My_SHStrDupW(name, ppszName);
1650 // return S_OK;
1651 // COM_TRY_END
1652 }
1653
1654
GetIcon(IShellItemArray *,LPWSTR * ppszIcon)1655 Z7_COMWF_B CZipExplorerCommand::GetIcon(IShellItemArray * /* psiItemArray */, LPWSTR *ppszIcon)
1656 {
1657 ODS("- GetIcon()")
1658 // COM_TRY_BEGIN
1659 *ppszIcon = NULL;
1660 // return E_NOTIMPL;
1661 UString imageName = fs2us(NWindows::NDLL::GetModuleDirPrefix());
1662 // imageName += "7zG.exe";
1663 imageName += "7-zip.dll";
1664 // imageName += ",190";
1665 return My_SHStrDupW(imageName, ppszIcon);
1666 // COM_TRY_END
1667 }
1668
1669
GetToolTip(IShellItemArray *,LPWSTR * ppszInfotip)1670 Z7_COMWF_B CZipExplorerCommand::GetToolTip (IShellItemArray * /* psiItemArray */, LPWSTR *ppszInfotip)
1671 {
1672 // COM_TRY_BEGIN
1673 ODS("- GetToolTip()")
1674 *ppszInfotip = NULL;
1675 return E_NOTIMPL;
1676 // COM_TRY_END
1677 }
1678
1679
GetCanonicalName(GUID * pguidCommandName)1680 Z7_COMWF_B CZipExplorerCommand::GetCanonicalName(GUID *pguidCommandName)
1681 {
1682 // COM_TRY_BEGIN
1683 ODS("- GetCanonicalName()")
1684 *pguidCommandName = GUID_NULL;
1685 return E_NOTIMPL;
1686 // COM_TRY_END
1687 }
1688
1689
GetState(IShellItemArray *,BOOL,EXPCMDSTATE * pCmdState)1690 Z7_COMWF_B CZipExplorerCommand::GetState(IShellItemArray * /* psiItemArray */, BOOL /* fOkToBeSlow */, EXPCMDSTATE *pCmdState)
1691 {
1692 // COM_TRY_BEGIN
1693 ODS("- GetState()")
1694 *pCmdState = ECS_ENABLED;
1695 return S_OK;
1696 // COM_TRY_END
1697 }
1698
1699
1700
1701
Invoke(IShellItemArray * psiItemArray,IBindCtx *)1702 Z7_COMWF_B CZipExplorerCommand::Invoke(IShellItemArray *psiItemArray, IBindCtx * /* pbc */)
1703 {
1704 COM_TRY_BEGIN
1705
1706 if (_commandMap_Cur.IsEmpty())
1707 return E_INVALIDARG;
1708
1709 ODS("- Invoke()")
1710 _fileNames.Clear();
1711 UStringVector paths;
1712 RINOK(LoadPaths(psiItemArray, paths))
1713 _fileNames = paths;
1714 return InvokeCommandCommon(_commandMap_Cur[0]);
1715
1716 COM_TRY_END
1717 }
1718
1719
GetFlags(EXPCMDFLAGS * pFlags)1720 Z7_COMWF_B CZipExplorerCommand::GetFlags(EXPCMDFLAGS *pFlags)
1721 {
1722 ODS("- GetFlags()")
1723 // COM_TRY_BEGIN
1724 EXPCMDFLAGS f = ECF_DEFAULT;
1725 if (IsSeparator)
1726 f = ECF_ISSEPARATOR;
1727 else if (IsRoot)
1728 f = ECF_HASSUBCOMMANDS;
1729 else
1730 {
1731 if (!_commandMap_Cur.IsEmpty())
1732 {
1733 // const CCommandMapItem &cmi = ;
1734 if (_commandMap_Cur[0].IsSubMenu())
1735 {
1736 // ODS("ECF_HASSUBCOMMANDS")
1737 f = ECF_HASSUBCOMMANDS;
1738 }
1739 }
1740 }
1741 *pFlags = f;
1742 return S_OK;
1743 // COM_TRY_END
1744 }
1745
1746
EnumSubCommands(IEnumExplorerCommand ** ppEnum)1747 Z7_COMWF_B CZipExplorerCommand::EnumSubCommands(IEnumExplorerCommand **ppEnum)
1748 {
1749 ODS("- EnumSubCommands()")
1750 // COM_TRY_BEGIN
1751 *ppEnum = NULL;
1752
1753 if (!_commandMap_Cur.IsEmpty() && _commandMap_Cur[0].IsSubMenu())
1754 {
1755 }
1756 else
1757 {
1758 if (!IsRoot)
1759 return E_NOTIMPL;
1760 if (SubCommands.IsEmpty())
1761 {
1762 return E_NOTIMPL;
1763 }
1764 }
1765
1766 // shellExt->
1767 return QueryInterface(IID_IEnumExplorerCommand, (void **)ppEnum);
1768
1769 // return S_OK;
1770 // COM_TRY_END
1771 }
1772
1773
Next(ULONG celt,IExplorerCommand ** pUICommand,ULONG * pceltFetched)1774 Z7_COMWF_B CZipContextMenu::Next(ULONG celt, IExplorerCommand **pUICommand, ULONG *pceltFetched)
1775 {
1776 ODS("CZipContextMenu::Next()")
1777 ODS_(Print_Number(celt, "celt"))
1778 ODS_(Print_Number(CurrentSubCommand, "CurrentSubCommand"))
1779 ODS_(Print_Number(SubCommands.Size(), "SubCommands.Size()"))
1780
1781 COM_TRY_BEGIN
1782 ULONG fetched = 0;
1783
1784 ULONG i;
1785 for (i = 0; i < celt; i++)
1786 {
1787 pUICommand[i] = NULL;
1788 }
1789
1790 for (i = 0; i < celt && CurrentSubCommand < SubCommands.Size(); i++)
1791 {
1792 pUICommand[i] = SubCommands[CurrentSubCommand++];
1793 pUICommand[i]->AddRef();
1794 fetched++;
1795 }
1796
1797 if (pceltFetched)
1798 *pceltFetched = fetched;
1799
1800 ODS(fetched == celt ? " === OK === " : "=== ERROR ===")
1801
1802 // we return S_FALSE for (fetched == 0)
1803 return (fetched == celt) ? S_OK : S_FALSE;
1804 COM_TRY_END
1805 }
1806
1807
Skip(ULONG)1808 Z7_COMWF_B CZipContextMenu::Skip(ULONG /* celt */)
1809 {
1810 ODS("CZipContextMenu::Skip()")
1811 return E_NOTIMPL;
1812 }
1813
1814
Reset(void)1815 Z7_COMWF_B CZipContextMenu::Reset(void)
1816 {
1817 ODS("CZipContextMenu::Reset()")
1818 CurrentSubCommand = 0;
1819 return S_OK;
1820 }
1821
1822
Clone(IEnumExplorerCommand ** ppenum)1823 Z7_COMWF_B CZipContextMenu::Clone(IEnumExplorerCommand **ppenum)
1824 {
1825 ODS("CZipContextMenu::Clone()")
1826 *ppenum = NULL;
1827 return E_NOTIMPL;
1828 }
1829