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