blob: 7815e13a2ac9ba0982fbd53f17fff244c232781c [file] [log] [blame]
// ContextMenu.cpp
#include "StdAfx.h"
#include "../../../Common/ComTry.h"
#include "../../../Common/IntToString.h"
#include "../../../Common/StringConvert.h"
#include "../../../Windows/COM.h"
#include "../../../Windows/DLL.h"
#include "../../../Windows/FileDir.h"
#include "../../../Windows/FileName.h"
#include "../../../Windows/Menu.h"
#include "../../../Windows/ProcessUtils.h"
// for IS_INTRESOURCE():
#include "../../../Windows/Window.h"
#include "../../PropID.h"
#include "../Common/ArchiveName.h"
#include "../Common/CompressCall.h"
#include "../Common/ExtractingFilePath.h"
#include "../Common/ZipRegistry.h"
#include "../FileManager/FormatUtils.h"
#include "../FileManager/LangUtils.h"
#include "../FileManager/PropertyName.h"
#include "ContextMenu.h"
#include "ContextMenuFlags.h"
#include "MyMessages.h"
#include "resource.h"
// #define SHOW_DEBUG_CTX_MENU
#ifdef SHOW_DEBUG_CTX_MENU
#include <stdio.h>
#endif
using namespace NWindows;
using namespace NFile;
using namespace NDir;
#ifndef UNDER_CE
#define EMAIL_SUPPORT 1
#endif
extern LONG g_DllRefCount;
#ifdef _WIN32
extern HINSTANCE g_hInstance;
#endif
#ifdef UNDER_CE
#define MY_IS_INTRESOURCE(_r) ((((ULONG_PTR)(_r)) >> 16) == 0)
#else
#define MY_IS_INTRESOURCE(_r) IS_INTRESOURCE(_r)
#endif
#ifdef SHOW_DEBUG_CTX_MENU
static void PrintStringA(const char *name, LPCSTR ptr)
{
AString m;
m += name;
m += ": ";
char s[32];
sprintf(s, "%p", (const void *)ptr);
m += s;
if (!MY_IS_INTRESOURCE(ptr))
{
m += ": \"";
m += ptr;
m += "\"";
}
OutputDebugStringA(m);
}
#if !defined(UNDER_CE)
static void PrintStringW(const char *name, LPCWSTR ptr)
{
UString m;
m += name;
m += ": ";
char s[32];
sprintf(s, "%p", (const void *)ptr);
m += s;
if (!MY_IS_INTRESOURCE(ptr))
{
m += ": \"";
m += ptr;
m += "\"";
}
OutputDebugStringW(m);
}
#endif
static void Print_Ptr(const void *p, const char *s)
{
char temp[32];
sprintf(temp, "%p", (const void *)p);
AString m;
m += temp;
m.Add_Space();
m += s;
OutputDebugStringA(m);
}
static void Print_Number(UInt32 number, const char *s)
{
AString m;
m.Add_UInt32(number);
m.Add_Space();
m += s;
OutputDebugStringA(m);
}
#define ODS(sz) { Print_Ptr(this, sz); }
#define ODS_U(s) { OutputDebugStringW(s); }
#define ODS_(op) { op; }
#define ODS_SPRF_s(x) { char s[256]; x; OutputDebugStringA(s); }
#else
#define ODS(sz)
#define ODS_U(s)
#define ODS_(op)
#define ODS_SPRF_s(x)
#endif
/*
DOCs: In Windows 7 and later, the number of items passed to
a verb is limited to 16 when a shortcut menu is queried.
The verb is then re-created and re-initialized with the full
selection when that verb is invoked.
win10 tests:
if (the number of selected file/dir objects > 16)
{
Explorer does the following actions:
- it creates ctx_menu_1 IContextMenu object
- it calls ctx_menu_1->Initialize() with list of only up to 16 items
- it calls ctx_menu_1->QueryContextMenu(menu_1)
- if (some menu command is pressed)
{
- it gets shown string from selected menu item : shown_menu_1_string
- it creates another ctx_menu_2 IContextMenu object
- it calls ctx_menu_2->Initialize() with list of all items
- it calls ctx_menu_2->QueryContextMenu(menu_2)
- if there is menu item with shown_menu_1_string string in menu_2,
Explorer calls ctx_menu_2->InvokeCommand() for that item.
Explorer probably doesn't use VERB from first object ctx_menu_1.
So we must provide same shown menu strings for both objects:
ctx_menu_1 and ctx_menu_2.
}
}
*/
CZipContextMenu::CZipContextMenu():
_isMenuForFM(true),
_fileNames_WereReduced(true),
_dropMode(false),
_bitmap(NULL),
_writeZone((UInt32)(Int32)-1),
IsSeparator(false),
IsRoot(true),
CurrentSubCommand(0)
{
ODS("== CZipContextMenu()");
InterlockedIncrement(&g_DllRefCount);
}
CZipContextMenu::~CZipContextMenu()
{
ODS("== ~CZipContextMenu");
if (_bitmap)
DeleteObject(_bitmap);
InterlockedDecrement(&g_DllRefCount);
}
// IShellExtInit
/*
IShellExtInit::Initialize()
pidlFolder:
- for property sheet extension:
NULL
- for shortcut menu extensions:
pidl of folder that contains the item whose shortcut menu is being displayed:
- for nondefault drag-and-drop menu extensions:
pidl of target folder: for nondefault drag-and-drop menu extensions
pidlFolder == NULL in (win10): for context menu
*/
Z7_COMWF_B CZipContextMenu::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT dataObject, HKEY /* hkeyProgID */)
{
COM_TRY_BEGIN
ODS("==== CZipContextMenu::Initialize START")
_isMenuForFM = false;
_fileNames_WereReduced = true;
_dropMode = false;
_attribs.Clear();
_fileNames.Clear();
_dropPath.Empty();
if (pidlFolder)
{
ODS("==== CZipContextMenu::Initialize (pidlFolder != 0)")
#ifndef UNDER_CE
if (NShell::GetPathFromIDList(pidlFolder, _dropPath))
{
ODS("==== CZipContextMenu::Initialize path from (pidl):")
ODS_U(_dropPath);
/* win10 : path with "\\\\?\\\" prefix is returned by GetPathFromIDList, if path is long
we can remove super prefix here. But probably prefix
is not problem for following 7-zip code.
so we don't remove super prefix */
NFile::NName::If_IsSuperPath_RemoveSuperPrefix(_dropPath);
NName::NormalizeDirPathPrefix(_dropPath);
_dropMode = !_dropPath.IsEmpty();
}
else
#endif
_dropPath.Empty();
}
if (!dataObject)
return E_INVALIDARG;
#ifndef UNDER_CE
RINOK(NShell::DataObject_GetData_HDROP_or_IDLIST_Names(dataObject, _fileNames))
// for (unsigned y = 0; y < 10000; y++)
if (NShell::DataObject_GetData_FILE_ATTRS(dataObject, _attribs) != S_OK)
_attribs.Clear();
#endif
ODS_SPRF_s(sprintf(s, "==== CZipContextMenu::Initialize END _files=%d",
_fileNames.Size()))
return S_OK;
COM_TRY_END
}
/////////////////////////////
// IContextMenu
static LPCSTR const kMainVerb = "SevenZip";
static LPCSTR const kOpenCascadedVerb = "SevenZip.OpenWithType.";
static LPCSTR const kCheckSumCascadedVerb = "SevenZip.Checksum";
struct CContextMenuCommand
{
UInt32 flag;
CZipContextMenu::enum_CommandInternalID CommandInternalID;
LPCSTR Verb;
UINT ResourceID;
};
#define CMD_REC(cns, verb, ids) { NContextMenuFlags::cns, CZipContextMenu::cns, verb, ids }
static const CContextMenuCommand g_Commands[] =
{
CMD_REC( kOpen, "Open", IDS_CONTEXT_OPEN),
CMD_REC( kExtract, "Extract", IDS_CONTEXT_EXTRACT),
CMD_REC( kExtractHere, "ExtractHere", IDS_CONTEXT_EXTRACT_HERE),
CMD_REC( kExtractTo, "ExtractTo", IDS_CONTEXT_EXTRACT_TO),
CMD_REC( kTest, "Test", IDS_CONTEXT_TEST),
CMD_REC( kCompress, "Compress", IDS_CONTEXT_COMPRESS),
CMD_REC( kCompressEmail, "CompressEmail", IDS_CONTEXT_COMPRESS_EMAIL),
CMD_REC( kCompressTo7z, "CompressTo7z", IDS_CONTEXT_COMPRESS_TO),
CMD_REC( kCompressTo7zEmail, "CompressTo7zEmail", IDS_CONTEXT_COMPRESS_TO_EMAIL),
CMD_REC( kCompressToZip, "CompressToZip", IDS_CONTEXT_COMPRESS_TO),
CMD_REC( kCompressToZipEmail, "CompressToZipEmail", IDS_CONTEXT_COMPRESS_TO_EMAIL)
};
struct CHashCommand
{
CZipContextMenu::enum_CommandInternalID CommandInternalID;
LPCSTR UserName;
LPCSTR MethodName;
};
static const CHashCommand g_HashCommands[] =
{
{ CZipContextMenu::kHash_CRC32, "CRC-32", "CRC32" },
{ CZipContextMenu::kHash_CRC64, "CRC-64", "CRC64" },
{ CZipContextMenu::kHash_SHA1, "SHA-1", "SHA1" },
{ CZipContextMenu::kHash_SHA256, "SHA-256", "SHA256" },
{ CZipContextMenu::kHash_All, "*", "*" },
{ CZipContextMenu::kHash_Generate_SHA256, "SHA-256 -> file.sha256", "SHA256" },
{ CZipContextMenu::kHash_TestArc, "Checksum : Test", "Hash" }
};
static int FindCommand(CZipContextMenu::enum_CommandInternalID &id)
{
for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_Commands); i++)
if (g_Commands[i].CommandInternalID == id)
return (int)i;
return -1;
}
void CZipContextMenu::FillCommand(enum_CommandInternalID id, UString &mainString, CCommandMapItem &cmi) const
{
mainString.Empty();
const int i = FindCommand(id);
if (i < 0)
throw 201908;
const CContextMenuCommand &command = g_Commands[(unsigned)i];
cmi.CommandInternalID = command.CommandInternalID;
cmi.Verb = kMainVerb;
cmi.Verb += command.Verb;
// cmi.HelpString = cmi.Verb;
LangString(command.ResourceID, mainString);
cmi.UserString = mainString;
}
static UString LangStringAlt(UInt32 id, const char *altString)
{
UString s = LangString(id);
if (s.IsEmpty())
s = altString;
return s;
}
void CZipContextMenu::AddCommand(enum_CommandInternalID id, UString &mainString, CCommandMapItem &cmi)
{
FillCommand(id, mainString, cmi);
_commandMap.Add(cmi);
}
/*
note: old msdn article:
Duplicate Menu Items In the File Menu For a Shell Context Menu Extension (214477)
----------
On systems with Shell32.dll version 4.71 or higher, a context menu extension
for a file folder that inserts one or more pop-up menus results in duplicates
of these menu items.
This occurs when the file menu is activated more than once for the selected object.
CAUSE
In a context menu extension, if pop-up menus are inserted using InsertMenu
or AppendMenu, then the ID for the pop-up menu item cannot be specified.
Instead, this field should take in the HMENU of the pop-up menu.
Because the ID is not specified for the pop-up menu item, the Shell does
not keep track of the menu item if the file menu is pulled down multiple times.
As a result, the pop-up menu items are added multiple times in the context menu.
This problem occurs only when the file menu is pulled down, and does not happen
when the context menu is invoked by using the right button or the context menu key.
RESOLUTION
To work around this problem, use InsertMenuItem and specify the ID of the
pop-up menu item in the wID member of the MENUITEMINFO structure.
*/
static void MyInsertMenu(CMenu &menu, unsigned pos, UINT id, const UString &s, HBITMAP bitmap)
{
if (!menu)
return;
CMenuItem mi;
mi.fType = MFT_STRING;
mi.fMask = MIIM_TYPE | MIIM_ID;
if (bitmap)
mi.fMask |= MIIM_CHECKMARKS;
mi.wID = id;
mi.StringValue = s;
mi.hbmpUnchecked = bitmap;
// mi.hbmpChecked = bitmap; // do we need hbmpChecked ???
if (!menu.InsertItem(pos, true, mi))
throw 20190816;
// SetMenuItemBitmaps also works
// ::SetMenuItemBitmaps(menu, pos, MF_BYPOSITION, bitmap, NULL);
}
static void MyAddSubMenu(
CObjectVector<CZipContextMenu::CCommandMapItem> &_commandMap,
const char *verb,
CMenu &menu, unsigned pos, UINT id, const UString &s, HMENU hSubMenu, HBITMAP bitmap)
{
CZipContextMenu::CCommandMapItem cmi;
cmi.CommandInternalID = CZipContextMenu::kCommandNULL;
cmi.Verb = verb;
cmi.IsPopup = true;
// cmi.HelpString = verb;
cmi.UserString = s;
_commandMap.Add(cmi);
if (!menu)
return;
CMenuItem mi;
mi.fType = MFT_STRING;
mi.fMask = MIIM_SUBMENU | MIIM_TYPE | MIIM_ID;
if (bitmap)
mi.fMask |= MIIM_CHECKMARKS;
mi.wID = id;
mi.hSubMenu = hSubMenu;
mi.hbmpUnchecked = bitmap;
mi.StringValue = s;
if (!menu.InsertItem(pos, true, mi))
throw 20190817;
}
static const char * const kArcExts[] =
{
"7z"
, "bz2"
, "gz"
, "rar"
, "zip"
};
static bool IsItArcExt(const UString &ext)
{
for (unsigned i = 0; i < Z7_ARRAY_SIZE(kArcExts); i++)
if (ext.IsEqualTo_Ascii_NoCase(kArcExts[i]))
return true;
return false;
}
UString GetSubFolderNameForExtract(const UString &arcName);
UString GetSubFolderNameForExtract(const UString &arcName)
{
int dotPos = arcName.ReverseFind_Dot();
if (dotPos < 0)
return Get_Correct_FsFile_Name(arcName) + L'~';
const UString ext = arcName.Ptr(dotPos + 1);
UString res = arcName.Left(dotPos);
res.TrimRight();
dotPos = res.ReverseFind_Dot();
if (dotPos > 0)
{
const UString ext2 = res.Ptr(dotPos + 1);
if ((ext.IsEqualTo_Ascii_NoCase("001") && IsItArcExt(ext2))
|| (ext.IsEqualTo_Ascii_NoCase("rar") &&
( ext2.IsEqualTo_Ascii_NoCase("part001")
|| ext2.IsEqualTo_Ascii_NoCase("part01")
|| ext2.IsEqualTo_Ascii_NoCase("part1"))))
res.DeleteFrom(dotPos);
res.TrimRight();
}
return Get_Correct_FsFile_Name(res);
}
static void ReduceString(UString &s)
{
const unsigned kMaxSize = 64;
if (s.Len() <= kMaxSize)
return;
s.Delete(kMaxSize / 2, s.Len() - kMaxSize);
s.Insert(kMaxSize / 2, L" ... ");
}
static UString GetQuotedReducedString(const UString &s)
{
UString s2 = s;
ReduceString(s2);
s2.Replace(L"&", L"&&");
return GetQuotedString(s2);
}
static void MyFormatNew_ReducedName(UString &s, const UString &name)
{
s = MyFormatNew(s, GetQuotedReducedString(name));
}
static const char * const kExtractExcludeExtensions =
" 3gp"
" aac ans ape asc asm asp aspx avi awk"
" bas bat bmp"
" c cs cls clw cmd cpp csproj css ctl cxx"
" def dep dlg dsp dsw"
" eps"
" f f77 f90 f95 fla flac frm"
" gif"
" h hpp hta htm html hxx"
" ico idl inc ini inl"
" java jpeg jpg js"
" la lnk log"
" mak manifest wmv mov mp3 mp4 mpe mpeg mpg m4a"
" ofr ogg"
" pac pas pdf php php3 php4 php5 phptml pl pm png ps py pyo"
" ra rb rc reg rka rm rtf"
" sed sh shn shtml sln sql srt swa"
" tcl tex tiff tta txt"
" vb vcproj vbs"
" wav wma wv"
" xml xsd xsl xslt"
" ";
/*
static const char * const kNoOpenAsExtensions =
" 7z arj bz2 cab chm cpio flv gz lha lzh lzma rar swm tar tbz2 tgz wim xar xz z zip ";
*/
static const char * const kOpenTypes[] =
{
""
, "*"
, "#"
, "#:e"
// , "#:a"
, "7z"
, "zip"
, "cab"
, "rar"
};
bool FindExt(const char *p, const UString &name, CStringFinder &finder);
bool FindExt(const char *p, const UString &name, CStringFinder &finder)
{
const int dotPos = name.ReverseFind_Dot();
if (dotPos < 0 || dotPos == (int)name.Len() - 1)
return false;
return finder.FindWord_In_LowCaseAsciiList_NoCase(p, name.Ptr(dotPos + 1));
}
/* returns false, if extraction of that file extension is not expected */
static bool DoNeedExtract(const UString &name, CStringFinder &finder)
{
// for (int y = 0; y < 1000; y++) FindExt(kExtractExcludeExtensions, name);
return !FindExt(kExtractExcludeExtensions, name, finder);
}
// we must use diferent Verbs for Popup subMenu.
void CZipContextMenu::AddMapItem_ForSubMenu(const char *verb)
{
CCommandMapItem cmi;
cmi.CommandInternalID = kCommandNULL;
cmi.Verb = verb;
// cmi.HelpString = verb;
_commandMap.Add(cmi);
}
static HRESULT RETURN_WIN32_LastError_AS_HRESULT()
{
DWORD lastError = ::GetLastError();
if (lastError == 0)
return E_FAIL;
return HRESULT_FROM_WIN32(lastError);
}
/*
we add CCommandMapItem to _commandMap for each new Menu ID.
so then we use _commandMap[offset].
That way we can execute commands that have menu item.
Another non-implemented way:
We can return the number off all possible commands in QueryContextMenu().
so the caller could call InvokeCommand() via string verb even
without using menu items.
*/
Z7_COMWF_B CZipContextMenu::QueryContextMenu(HMENU hMenu, UINT indexMenu,
UINT commandIDFirst, UINT commandIDLast, UINT flags)
{
ODS("+ QueryContextMenu()")
COM_TRY_BEGIN
try {
_commandMap.Clear();
ODS_SPRF_s(sprintf(s, "QueryContextMenu: index=%u first=%u last=%u flags=%x _files=%u",
indexMenu, commandIDFirst, commandIDLast, flags, _fileNames.Size()))
/*
for (UInt32 i = 0; i < _fileNames.Size(); i++)
{
ODS_U(_fileNames[i])
}
*/
if (_fileNames.Size() == 0)
{
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0);
// return E_INVALIDARG;
}
if (commandIDFirst > commandIDLast)
return E_INVALIDARG;
UINT currentCommandID = commandIDFirst;
if ((flags & 0x000F) != CMF_NORMAL
&& (flags & CMF_VERBSONLY) == 0
&& (flags & CMF_EXPLORE) == 0)
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, currentCommandID - commandIDFirst);
// return MAKE_HRESULT(SEVERITY_SUCCESS, 0, currentCommandID);
// 19.01 : we changed from (currentCommandID) to (currentCommandID - commandIDFirst)
// why it was so before?
#ifdef Z7_LANG
LoadLangOneTime();
#endif
CMenu popupMenu;
CMenuDestroyer menuDestroyer;
ODS("### 40")
CContextMenuInfo ci;
ci.Load();
ODS("### 44")
_elimDup = ci.ElimDup;
_writeZone = ci.WriteZone;
HBITMAP bitmap = NULL;
if (ci.MenuIcons.Val)
{
ODS("### 45")
if (!_bitmap)
_bitmap = ::LoadBitmap(g_hInstance, MAKEINTRESOURCE(IDB_MENU_LOGO));
bitmap = _bitmap;
}
UINT subIndex = indexMenu;
ODS("### 50")
if (ci.Cascaded.Val)
{
if (hMenu)
if (!popupMenu.CreatePopup())
return RETURN_WIN32_LastError_AS_HRESULT();
menuDestroyer.Attach(popupMenu);
/* 9.31: we commented the following code. Probably we don't need.
Check more systems. Maybe it was for old Windows? */
/*
AddMapItem_ForSubMenu();
currentCommandID++;
*/
subIndex = 0;
}
else
{
popupMenu.Attach(hMenu);
CMenuItem mi;
mi.fType = MFT_SEPARATOR;
mi.fMask = MIIM_TYPE;
if (hMenu)
popupMenu.InsertItem(subIndex++, true, mi);
}
const UInt32 contextMenuFlags = ci.Flags;
NFind::CFileInfo fi0;
FString folderPrefix;
if (_fileNames.Size() > 0)
{
const UString &fileName = _fileNames.Front();
#if defined(_WIN32) && !defined(UNDER_CE)
if (NName::IsDevicePath(us2fs(fileName)))
{
// CFileInfo::Find can be slow for device files. So we don't call it.
// we need only name here.
fi0.Name = us2fs(fileName.Ptr(NName::kDevicePathPrefixSize));
folderPrefix =
#ifdef UNDER_CE
"\\";
#else
"C:\\";
#endif
}
else
#endif
{
if (!fi0.Find(us2fs(fileName)))
{
throw 20190820;
// return RETURN_WIN32_LastError_AS_HRESULT();
}
GetOnlyDirPrefix(us2fs(fileName), folderPrefix);
}
}
ODS("### 100")
UString mainString;
CStringFinder finder;
UStringVector fileNames_Reduced;
const unsigned k_Explorer_NumReducedItems = 16;
const bool needReduce = !_isMenuForFM && (_fileNames.Size() >= k_Explorer_NumReducedItems);
_fileNames_WereReduced = needReduce;
// _fileNames_WereReduced = true; // for debug;
const UStringVector *fileNames = &_fileNames;
if (needReduce)
{
for (unsigned i = 0; i < k_Explorer_NumReducedItems
&& i < _fileNames.Size(); i++)
fileNames_Reduced.Add(_fileNames[i]);
fileNames = &fileNames_Reduced;
}
/*
if (_fileNames.Size() == k_Explorer_NumReducedItems) // for debug
{
for (int i = 0; i < 10; i++)
{
CCommandMapItem cmi;
AddCommand(kCompressToZipEmail, mainString, cmi);
MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
}
}
*/
if (_fileNames.Size() == 1 && currentCommandID + 14 <= commandIDLast)
{
if (!fi0.IsDir() && DoNeedExtract(fs2us(fi0.Name), finder))
{
// Open
const bool thereIsMainOpenItem = ((contextMenuFlags & NContextMenuFlags::kOpen) != 0);
if (thereIsMainOpenItem)
{
CCommandMapItem cmi;
AddCommand(kOpen, mainString, cmi);
MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
}
if ((contextMenuFlags & NContextMenuFlags::kOpenAs) != 0
// && (!thereIsMainOpenItem || !FindExt(kNoOpenAsExtensions, fi0.Name))
&& hMenu // we want to reduce number of menu items below 16
)
{
CMenu subMenu;
if (!hMenu || subMenu.CreatePopup())
{
MyAddSubMenu(_commandMap, kOpenCascadedVerb, popupMenu, subIndex++, currentCommandID++, LangString(IDS_CONTEXT_OPEN), subMenu, bitmap);
_commandMap.Back().CtxCommandType = CtxCommandType_OpenRoot;
UINT subIndex2 = 0;
for (unsigned i = (thereIsMainOpenItem ? 1 : 0); i < Z7_ARRAY_SIZE(kOpenTypes); i++)
{
CCommandMapItem cmi;
if (i == 0)
FillCommand(kOpen, mainString, cmi);
else
{
mainString = kOpenTypes[i];
cmi.CommandInternalID = kOpen;
cmi.Verb = kMainVerb;
cmi.Verb += ".Open.";
cmi.Verb += mainString;
// cmi.HelpString = cmi.Verb;
cmi.ArcType = mainString;
cmi.CtxCommandType = CtxCommandType_OpenChild;
}
_commandMap.Add(cmi);
Set_UserString_in_LastCommand(mainString);
MyInsertMenu(subMenu, subIndex2++, currentCommandID++, mainString, bitmap);
}
subMenu.Detach();
}
}
}
}
ODS("### 150")
if (_fileNames.Size() > 0 && currentCommandID + 10 <= commandIDLast)
{
ODS("### needExtract list START")
const bool needExtendedVerbs = ((flags & Z7_WIN_CMF_EXTENDEDVERBS) != 0);
// || _isMenuForFM;
bool needExtract = true;
bool areDirs = fi0.IsDir() || (unsigned)_attribs.FirstDirIndex < k_Explorer_NumReducedItems;
if (!needReduce)
areDirs = areDirs || (_attribs.FirstDirIndex != -1);
if (areDirs)
needExtract = false;
if (!needExtendedVerbs)
if (needExtract)
{
UString name;
const unsigned numItemsCheck = fileNames->Size();
for (unsigned i = 0; i < numItemsCheck; i++)
{
const UString &a = (*fileNames)[i];
const int slash = a.ReverseFind_PathSepar();
name = a.Ptr(slash + 1);
// for (int y = 0; y < 600; y++) // for debug
const bool needExtr2 = DoNeedExtract(name, finder);
if (!needExtr2)
{
needExtract = needExtr2;
break;
}
}
}
ODS("### needExtract list END")
if (needExtract)
{
{
UString baseFolder = fs2us(folderPrefix);
if (_dropMode)
baseFolder = _dropPath;
UString specFolder ('*');
if (_fileNames.Size() == 1)
specFolder = GetSubFolderNameForExtract(fs2us(fi0.Name));
specFolder.Add_PathSepar();
if ((contextMenuFlags & NContextMenuFlags::kExtract) != 0)
{
// Extract
CCommandMapItem cmi;
cmi.Folder = baseFolder + specFolder;
AddCommand(kExtract, mainString, cmi);
MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
}
if ((contextMenuFlags & NContextMenuFlags::kExtractHere) != 0)
{
// Extract Here
CCommandMapItem cmi;
cmi.Folder = baseFolder;
AddCommand(kExtractHere, mainString, cmi);
MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
}
if ((contextMenuFlags & NContextMenuFlags::kExtractTo) != 0)
{
// Extract To
CCommandMapItem cmi;
UString s;
cmi.Folder = baseFolder + specFolder;
AddCommand(kExtractTo, s, cmi);
MyFormatNew_ReducedName(s, specFolder);
Set_UserString_in_LastCommand(s);
MyInsertMenu(popupMenu, subIndex++, currentCommandID++, s, bitmap);
}
}
if ((contextMenuFlags & NContextMenuFlags::kTest) != 0)
{
// Test
CCommandMapItem cmi;
AddCommand(kTest, mainString, cmi);
// if (_fileNames.Size() == 16) mainString += "_[16]"; // for debug
MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
}
}
ODS("### CreateArchiveName START")
UString arcName_base;
const UString arcName = CreateArchiveName(
*fileNames,
false, // isHash
fileNames->Size() == 1 ? &fi0 : NULL,
arcName_base);
ODS("### CreateArchiveName END")
UString arcName_Show = arcName;
if (needReduce)
{
/* we need same arcName_Show for two calls from Explorer:
1) reduced call (only first 16 items)
2) full call with all items (can be >= 16 items)
(fileNames) array was reduced to 16 items.
So we will have same (arcName) in both reduced and full calls.
If caller (Explorer) uses (reduce_to_first_16_items) scheme,
we can use (arcName) here instead of (arcName_base).
(arcName_base) has no number in name.
*/
arcName_Show = arcName_base; // we can comment that line
/* we use "_" in archive name as sign to user
that shows that final archive name can be changed. */
arcName_Show += "_";
}
UString arcName_7z = arcName;
arcName_7z += ".7z";
UString arcName_7z_Show = arcName_Show;
arcName_7z_Show += ".7z";
UString arcName_zip = arcName;
arcName_zip += ".zip";
UString arcName_zip_Show = arcName_Show;
arcName_zip_Show += ".zip";
// Compress
if ((contextMenuFlags & NContextMenuFlags::kCompress) != 0)
{
CCommandMapItem cmi;
if (_dropMode)
cmi.Folder = _dropPath;
else
cmi.Folder = fs2us(folderPrefix);
cmi.ArcName = arcName;
AddCommand(kCompress, mainString, cmi);
MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
}
#ifdef EMAIL_SUPPORT
// CompressEmail
if ((contextMenuFlags & NContextMenuFlags::kCompressEmail) != 0 && !_dropMode)
{
CCommandMapItem cmi;
cmi.ArcName = arcName;
AddCommand(kCompressEmail, mainString, cmi);
MyInsertMenu(popupMenu, subIndex++, currentCommandID++, mainString, bitmap);
}
#endif
// CompressTo7z
if (contextMenuFlags & NContextMenuFlags::kCompressTo7z &&
!arcName_7z.IsEqualTo_NoCase(fs2us(fi0.Name)))
{
CCommandMapItem cmi;
UString s;
if (_dropMode)
cmi.Folder = _dropPath;
else
cmi.Folder = fs2us(folderPrefix);
cmi.ArcName = arcName_7z;
cmi.ArcType = "7z";
AddCommand(kCompressTo7z, s, cmi);
MyFormatNew_ReducedName(s, arcName_7z_Show);
Set_UserString_in_LastCommand(s);
MyInsertMenu(popupMenu, subIndex++, currentCommandID++, s, bitmap);
}
#ifdef EMAIL_SUPPORT
// CompressTo7zEmail
if ((contextMenuFlags & NContextMenuFlags::kCompressTo7zEmail) != 0 && !_dropMode)
{
CCommandMapItem cmi;
UString s;
cmi.ArcName = arcName_7z;
cmi.ArcType = "7z";
AddCommand(kCompressTo7zEmail, s, cmi);
MyFormatNew_ReducedName(s, arcName_7z_Show);
Set_UserString_in_LastCommand(s);
MyInsertMenu(popupMenu, subIndex++, currentCommandID++, s, bitmap);
}
#endif
// CompressToZip
if (contextMenuFlags & NContextMenuFlags::kCompressToZip &&
!arcName_zip.IsEqualTo_NoCase(fs2us(fi0.Name)))
{
CCommandMapItem cmi;
UString s;
if (_dropMode)
cmi.Folder = _dropPath;
else
cmi.Folder = fs2us(folderPrefix);
cmi.ArcName = arcName_zip;
cmi.ArcType = "zip";
AddCommand(kCompressToZip, s, cmi);
MyFormatNew_ReducedName(s, arcName_zip_Show);
Set_UserString_in_LastCommand(s);
MyInsertMenu(popupMenu, subIndex++, currentCommandID++, s, bitmap);
}
#ifdef EMAIL_SUPPORT
// CompressToZipEmail
if ((contextMenuFlags & NContextMenuFlags::kCompressToZipEmail) != 0 && !_dropMode)
{
CCommandMapItem cmi;
UString s;
cmi.ArcName = arcName_zip;
cmi.ArcType = "zip";
AddCommand(kCompressToZipEmail, s, cmi);
MyFormatNew_ReducedName(s, arcName_zip_Show);
Set_UserString_in_LastCommand(s);
MyInsertMenu(popupMenu, subIndex++, currentCommandID++, s, bitmap);
}
#endif
}
ODS("### 300")
// don't use InsertMenu: See MSDN:
// PRB: Duplicate Menu Items In the File Menu For a Shell Context Menu Extension
// ID: Q214477
if (ci.Cascaded.Val)
{
CMenu menu;
menu.Attach(hMenu);
menuDestroyer.Disable();
MyAddSubMenu(_commandMap, kMainVerb, menu, indexMenu++, currentCommandID++, (UString)"7-Zip",
popupMenu, // popupMenu.Detach(),
bitmap);
}
else
{
// popupMenu.Detach();
indexMenu = subIndex;
}
ODS("### 350")
const bool needCrc = ((contextMenuFlags &
(NContextMenuFlags::kCRC |
NContextMenuFlags::kCRC_Cascaded)) != 0);
if (
// !_isMenuForFM && // 21.04: we don't hide CRC SHA menu in 7-Zip FM
needCrc
&& currentCommandID + 1 < commandIDLast)
{
CMenu subMenu;
// CMenuDestroyer menuDestroyer_CRC;
UINT subIndex_CRC = 0;
if (!hMenu || subMenu.CreatePopup())
{
// menuDestroyer_CRC.Attach(subMenu);
const bool insertHashMenuTo7zipMenu = (ci.Cascaded.Val
&& (contextMenuFlags & NContextMenuFlags::kCRC_Cascaded) != 0);
CMenu menu;
{
unsigned indexInParent;
if (insertHashMenuTo7zipMenu)
{
indexInParent = subIndex;
menu.Attach(popupMenu);
}
else
{
indexInParent = indexMenu;
menu.Attach(hMenu);
// menuDestroyer_CRC.Disable();
}
MyAddSubMenu(_commandMap, kCheckSumCascadedVerb, menu, indexInParent++, currentCommandID++, (UString)"CRC SHA", subMenu,
/* insertHashMenuTo7zipMenu ? NULL : */ bitmap);
_commandMap.Back().CtxCommandType = CtxCommandType_CrcRoot;
if (!insertHashMenuTo7zipMenu)
indexMenu = indexInParent;
}
ODS("### HashCommands")
for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_HashCommands); i++)
{
if (currentCommandID >= commandIDLast)
break;
const CHashCommand &hc = g_HashCommands[i];
CCommandMapItem cmi;
cmi.CommandInternalID = hc.CommandInternalID;
cmi.Verb = kCheckSumCascadedVerb;
cmi.Verb.Add_Dot();
UString s;
s += hc.UserName;
if (hc.CommandInternalID == kHash_Generate_SHA256)
{
cmi.Verb += "Generate";
{
popupMenu.Attach(hMenu);
CMenuItem mi;
mi.fType = MFT_SEPARATOR;
mi.fMask = MIIM_TYPE;
subMenu.InsertItem(subIndex_CRC++, true, mi);
}
UString name;
UString showName;
ODS("### Hash CreateArchiveName Start")
// for (int y = 0; y < 10000; y++) // for debug
// if (fileNames->Size() == 1) name = fs2us(fi0.Name); else
name = CreateArchiveName(
*fileNames,
true, // isHash
fileNames->Size() == 1 ? &fi0 : NULL,
showName);
if (needReduce)
showName += "_";
else
showName = name;
ODS("### Hash CreateArchiveName END")
name += ".sha256";
showName += ".sha256";
cmi.Folder = fs2us(folderPrefix);
cmi.ArcName = name;
s = "SHA-256 -> ";
s += showName;
}
else if (hc.CommandInternalID == kHash_TestArc)
{
cmi.Verb += "Test";
s = LangStringAlt(IDS_CONTEXT_TEST, "Test archive");
s += " : ";
s += GetNameOfProperty(kpidChecksum, UString("Checksum"));
}
else
cmi.Verb += "Calc";
cmi.Verb.Add_Dot();
cmi.Verb += hc.MethodName;
// cmi.HelpString = cmi.Verb;
cmi.UserString = s;
cmi.CtxCommandType = CtxCommandType_CrcChild;
_commandMap.Add(cmi);
MyInsertMenu(subMenu, subIndex_CRC++, currentCommandID++, s, bitmap);
ODS("### 380")
}
subMenu.Detach();
}
}
popupMenu.Detach();
/*
if (!ci.Cascaded.Val)
indexMenu = subIndex;
*/
const unsigned numCommands = currentCommandID - commandIDFirst;
ODS("+ QueryContextMenu() END")
ODS_SPRF_s(sprintf(s, "Commands=%u currentCommandID - commandIDFirst = %u",
_commandMap.Size(), numCommands))
if (_commandMap.Size() != numCommands)
throw 20190818;
/*
FOR_VECTOR (k, _commandMap)
{
ODS_U(_commandMap[k].Verb);
}
*/
}
catch(...)
{
ODS_SPRF_s(sprintf(s, "catch() exception: Commands=%u", _commandMap.Size()))
if (_commandMap.Size() == 0)
throw;
}
/* we added some menu items already : num_added_menu_items,
So we MUST return (number_of_defined_ids), where (number_of_defined_ids >= num_added_menu_items)
This will prevent incorrect menu working, when same IDs can be
assigned in multiple menu items from different subhandlers.
And we must add items to _commandMap before adding to menu.
*/
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, _commandMap.Size());
COM_TRY_END
}
int CZipContextMenu::FindVerb(const UString &verb) const
{
FOR_VECTOR (i, _commandMap)
if (_commandMap[i].Verb == verb)
return (int)i;
return -1;
}
static UString Get7zFmPath()
{
return fs2us(NWindows::NDLL::GetModuleDirPrefix()) + L"7zFM.exe";
}
Z7_COMWF_B CZipContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO commandInfo)
{
COM_TRY_BEGIN
ODS("==== CZipContextMenu::InvokeCommand()")
#ifdef SHOW_DEBUG_CTX_MENU
ODS_SPRF_s(sprintf(s, ": InvokeCommand: cbSize=%u flags=%x ",
(unsigned)commandInfo->cbSize, (unsigned)commandInfo->fMask))
PrintStringA("Verb", commandInfo->lpVerb);
PrintStringA("Parameters", commandInfo->lpParameters);
PrintStringA("Directory", commandInfo->lpDirectory);
#endif
int commandOffset = -1;
// xp64 / Win10 : explorer.exe sends 0 in lpVerbW
// MSDN: if (IS_INTRESOURCE(lpVerbW)), we must use LOWORD(lpVerb) as command offset
// FIXME: old MINGW doesn't define CMINVOKECOMMANDINFOEX / CMIC_MASK_UNICODE
#if !defined(UNDER_CE) && defined(CMIC_MASK_UNICODE)
bool unicodeVerb = false;
if (commandInfo->cbSize == sizeof(CMINVOKECOMMANDINFOEX) &&
(commandInfo->fMask & CMIC_MASK_UNICODE) != 0)
{
LPCMINVOKECOMMANDINFOEX commandInfoEx = (LPCMINVOKECOMMANDINFOEX)commandInfo;
if (!MY_IS_INTRESOURCE(commandInfoEx->lpVerbW))
{
unicodeVerb = true;
commandOffset = FindVerb(commandInfoEx->lpVerbW);
}
#ifdef SHOW_DEBUG_CTX_MENU
PrintStringW("VerbW", commandInfoEx->lpVerbW);
PrintStringW("ParametersW", commandInfoEx->lpParametersW);
PrintStringW("DirectoryW", commandInfoEx->lpDirectoryW);
PrintStringW("TitleW", commandInfoEx->lpTitleW);
PrintStringA("Title", commandInfoEx->lpTitle);
#endif
}
if (!unicodeVerb)
#endif
{
ODS("use non-UNICODE verb")
// if (HIWORD(commandInfo->lpVerb) == 0)
if (MY_IS_INTRESOURCE(commandInfo->lpVerb))
commandOffset = LOWORD(commandInfo->lpVerb);
else
commandOffset = FindVerb(GetUnicodeString(commandInfo->lpVerb));
}
ODS_SPRF_s(sprintf(s, "commandOffset=%d", commandOffset))
if (/* commandOffset < 0 || */ (unsigned)commandOffset >= _commandMap.Size())
return E_INVALIDARG;
const CCommandMapItem &cmi = _commandMap[(unsigned)commandOffset];
return InvokeCommandCommon(cmi);
COM_TRY_END
}
HRESULT CZipContextMenu::InvokeCommandCommon(const CCommandMapItem &cmi)
{
const enum_CommandInternalID cmdID = cmi.CommandInternalID;
try
{
switch (cmdID)
{
case kOpen:
{
UString params;
params = GetQuotedString(_fileNames[0]);
if (!cmi.ArcType.IsEmpty())
{
params += " -t";
params += cmi.ArcType;
}
MyCreateProcess(Get7zFmPath(), params);
break;
}
case kExtract:
case kExtractHere:
case kExtractTo:
{
if (_attribs.FirstDirIndex != -1)
{
ShowErrorMessageRes(IDS_SELECT_FILES);
break;
}
ExtractArchives(_fileNames, cmi.Folder,
(cmdID == kExtract), // showDialog
(cmdID == kExtractTo) && _elimDup.Val, // elimDup
_writeZone
);
break;
}
case kTest:
{
TestArchives(_fileNames);
break;
}
case kCompress:
case kCompressEmail:
case kCompressTo7z:
case kCompressTo7zEmail:
case kCompressToZip:
case kCompressToZipEmail:
{
UString arcName = cmi.ArcName;
if (_fileNames_WereReduced)
{
UString arcName_base;
arcName = CreateArchiveName(
_fileNames,
false, // isHash
NULL, // fi0
arcName_base);
const char *postfix = NULL;
if (cmdID == kCompressTo7z ||
cmdID == kCompressTo7zEmail)
postfix = ".7z";
else if (
cmdID == kCompressToZip ||
cmdID == kCompressToZipEmail)
postfix = ".zip";
if (postfix)
arcName += postfix;
}
const bool email =
cmdID == kCompressEmail ||
cmdID == kCompressTo7zEmail ||
cmdID == kCompressToZipEmail;
const bool showDialog =
cmdID == kCompress ||
cmdID == kCompressEmail;
const bool addExtension = showDialog;
CompressFiles(cmi.Folder,
arcName, cmi.ArcType,
addExtension,
_fileNames, email, showDialog,
false // waitFinish
);
break;
}
case kHash_CRC32:
case kHash_CRC64:
case kHash_SHA1:
case kHash_SHA256:
case kHash_All:
case kHash_Generate_SHA256:
case kHash_TestArc:
{
for (unsigned i = 0; i < Z7_ARRAY_SIZE(g_HashCommands); i++)
{
const CHashCommand &hc = g_HashCommands[i];
if (hc.CommandInternalID == cmdID)
{
if (cmdID == kHash_TestArc)
{
TestArchives(_fileNames, true); // hashMode
break;
}
UString generateName;
if (cmdID == kHash_Generate_SHA256)
{
generateName = cmi.ArcName;
if (_fileNames_WereReduced)
{
UString arcName_base;
generateName = CreateArchiveName(_fileNames,
true, // isHash
NULL, // fi0
arcName_base);
generateName += ".sha256";
}
}
CalcChecksum(_fileNames, (UString)hc.MethodName,
cmi.Folder, generateName);
break;
}
}
break;
}
case kCommandNULL:
break;
}
}
catch(...)
{
ShowErrorMessage(NULL, L"Error");
}
return S_OK;
}
static void MyCopyString_isUnicode(void *dest, UINT size, const UString &src, bool writeInUnicode)
{
if (size != 0)
size--;
if (writeInUnicode)
{
UString s = src;
s.DeleteFrom(size);
MyStringCopy((wchar_t *)dest, s);
ODS_U(s)
}
else
{
AString s = GetAnsiString(src);
s.DeleteFrom(size);
MyStringCopy((char *)dest, s);
}
}
Z7_COMWF_B CZipContextMenu::GetCommandString(
#ifdef Z7_OLD_WIN_SDK
UINT
#else
UINT_PTR
#endif
commandOffset,
UINT uType,
UINT * /* pwReserved */ , LPSTR pszName, UINT cchMax)
{
COM_TRY_BEGIN
ODS("GetCommandString")
const int cmdOffset = (int)commandOffset;
ODS_SPRF_s(sprintf(s, "GetCommandString: cmdOffset=%d uType=%d cchMax = %d",
cmdOffset, uType, cchMax))
if ((uType | GCS_UNICODE) == GCS_VALIDATEW)
{
if (/* cmdOffset < 0 || */ (unsigned)cmdOffset >= _commandMap.Size())
return S_FALSE;
return S_OK;
}
if (/* cmdOffset < 0 || */ (unsigned)cmdOffset >= _commandMap.Size())
{
ODS("------ cmdOffset: E_INVALIDARG")
return E_INVALIDARG;
}
// we use Verb as HelpString
if (cchMax != 0)
if ((uType | GCS_UNICODE) == GCS_VERBW ||
(uType | GCS_UNICODE) == GCS_HELPTEXTW)
{
const CCommandMapItem &cmi = _commandMap[(unsigned)cmdOffset];
MyCopyString_isUnicode(pszName, cchMax, cmi.Verb, (uType & GCS_UNICODE) != 0);
return S_OK;
}
return E_INVALIDARG;
COM_TRY_END
}
// ---------- IExplorerCommand ----------
static HRESULT WINAPI My_SHStrDupW(LPCWSTR src, LPWSTR *dest)
{
if (src)
{
const SIZE_T size = (wcslen(src) + 1) * sizeof(WCHAR);
WCHAR *p = (WCHAR *)CoTaskMemAlloc(size);
if (p)
{
memcpy(p, src, size);
*dest = p;
return S_OK;
}
}
*dest = NULL;
return E_OUTOFMEMORY;
}
#define CZipExplorerCommand CZipContextMenu
class CCoTaskWSTR
{
LPWSTR m_str;
Z7_CLASS_NO_COPY(CCoTaskWSTR)
public:
CCoTaskWSTR(): m_str(NULL) {}
~CCoTaskWSTR() { ::CoTaskMemFree(m_str); }
LPWSTR* operator&() { return &m_str; }
operator LPCWSTR () const { return m_str; }
// operator LPCOLESTR() const { return m_str; }
operator bool() const { return m_str != NULL; }
// bool operator!() const { return m_str == NULL; }
/*
void Wipe_and_Free()
{
if (m_str)
{
memset(m_str, 0, ::SysStringLen(m_str) * sizeof(*m_str));
Empty();
}
}
*/
private:
/*
CCoTaskWSTR(LPCOLESTR src) { m_str = ::CoTaskMemAlloc(src); }
CCoTaskWSTR& operator=(LPCOLESTR src)
{
::CoTaskMemFree(m_str);
m_str = ::SysAllocString(src);
return *this;
}
void Empty()
{
::CoTaskMemFree(m_str);
m_str = NULL;
}
*/
};
static HRESULT LoadPaths(IShellItemArray *psiItemArray, UStringVector &paths)
{
if (psiItemArray)
{
DWORD numItems = 0;
RINOK(psiItemArray->GetCount(&numItems))
{
ODS_(Print_Number(numItems, " ==== LoadPaths START === "))
for (DWORD i = 0; i < numItems; i++)
{
CMyComPtr<IShellItem> item;
RINOK(psiItemArray->GetItemAt(i, &item))
if (item)
{
CCoTaskWSTR displayName;
if (item->GetDisplayName(SIGDN_FILESYSPATH, &displayName) == S_OK
&& (bool)displayName)
{
ODS_U(displayName)
paths.Add((LPCWSTR)displayName);
}
}
}
ODS_(Print_Number(numItems, " ==== LoadPaths END === "))
}
}
return S_OK;
}
void CZipExplorerCommand::LoadItems(IShellItemArray *psiItemArray)
{
SubCommands.Clear();
_fileNames.Clear();
{
UStringVector paths;
if (LoadPaths(psiItemArray, paths) != S_OK)
return;
_fileNames = paths;
}
const HRESULT res = QueryContextMenu(
NULL, // hMenu,
0, // indexMenu,
0, // commandIDFirst,
0 + 999, // commandIDLast,
CMF_NORMAL);
if (FAILED(res))
return /* res */;
CZipExplorerCommand *crcHandler = NULL;
CZipExplorerCommand *openHandler = NULL;
bool useCascadedCrc = true; // false;
bool useCascadedOpen = true; // false;
for (unsigned i = 0; i < _commandMap.Size(); i++)
{
const CCommandMapItem &cmi = _commandMap[i];
if (cmi.IsPopup)
if (!cmi.IsSubMenu())
continue;
// if (cmi.IsSubMenu()) continue // for debug
CZipContextMenu *shellExt = new CZipContextMenu();
shellExt->IsRoot = false;
if (cmi.CtxCommandType == CtxCommandType_CrcRoot && !useCascadedCrc)
shellExt->IsSeparator = true;
{
CZipExplorerCommand *handler = this;
if (cmi.CtxCommandType == CtxCommandType_CrcChild && crcHandler)
handler = crcHandler;
else if (cmi.CtxCommandType == CtxCommandType_OpenChild && openHandler)
handler = openHandler;
handler->SubCommands.AddNew() = shellExt;
}
shellExt->_commandMap_Cur.Add(cmi);
ODS_U(cmi.UserString)
if (cmi.CtxCommandType == CtxCommandType_CrcRoot && useCascadedCrc)
crcHandler = shellExt;
if (cmi.CtxCommandType == CtxCommandType_OpenRoot && useCascadedOpen)
{
// ODS("cmi.CtxCommandType == CtxCommandType_OpenRoot");
openHandler = shellExt;
}
}
}
Z7_COMWF_B CZipExplorerCommand::GetTitle(IShellItemArray *psiItemArray, LPWSTR *ppszName)
{
ODS("- GetTitle()")
// COM_TRY_BEGIN
if (IsSeparator)
{
*ppszName = NULL;
return S_FALSE;
}
UString name;
if (IsRoot)
{
LoadItems(psiItemArray);
name = "7-Zip"; // "New"
}
else
name = "7-Zip item";
if (!_commandMap_Cur.IsEmpty())
{
const CCommandMapItem &mi = _commandMap_Cur[0];
// s += mi.Verb;
// s += " : ";
name = mi.UserString;
}
return My_SHStrDupW(name, ppszName);
// return S_OK;
// COM_TRY_END
}
Z7_COMWF_B CZipExplorerCommand::GetIcon(IShellItemArray * /* psiItemArray */, LPWSTR *ppszIcon)
{
ODS("- GetIcon()")
// COM_TRY_BEGIN
*ppszIcon = NULL;
// return E_NOTIMPL;
UString imageName = fs2us(NWindows::NDLL::GetModuleDirPrefix());
// imageName += "7zG.exe";
imageName += "7-zip.dll";
// imageName += ",190";
return My_SHStrDupW(imageName, ppszIcon);
// COM_TRY_END
}
Z7_COMWF_B CZipExplorerCommand::GetToolTip (IShellItemArray * /* psiItemArray */, LPWSTR *ppszInfotip)
{
// COM_TRY_BEGIN
ODS("- GetToolTip()")
*ppszInfotip = NULL;
return E_NOTIMPL;
// COM_TRY_END
}
Z7_COMWF_B CZipExplorerCommand::GetCanonicalName(GUID *pguidCommandName)
{
// COM_TRY_BEGIN
ODS("- GetCanonicalName()")
*pguidCommandName = GUID_NULL;
return E_NOTIMPL;
// COM_TRY_END
}
Z7_COMWF_B CZipExplorerCommand::GetState(IShellItemArray * /* psiItemArray */, BOOL /* fOkToBeSlow */, EXPCMDSTATE *pCmdState)
{
// COM_TRY_BEGIN
ODS("- GetState()")
*pCmdState = ECS_ENABLED;
return S_OK;
// COM_TRY_END
}
Z7_COMWF_B CZipExplorerCommand::Invoke(IShellItemArray *psiItemArray, IBindCtx * /* pbc */)
{
COM_TRY_BEGIN
if (_commandMap_Cur.IsEmpty())
return E_INVALIDARG;
ODS("- Invoke()")
_fileNames.Clear();
UStringVector paths;
RINOK(LoadPaths(psiItemArray, paths))
_fileNames = paths;
return InvokeCommandCommon(_commandMap_Cur[0]);
COM_TRY_END
}
Z7_COMWF_B CZipExplorerCommand::GetFlags(EXPCMDFLAGS *pFlags)
{
ODS("- GetFlags()")
// COM_TRY_BEGIN
EXPCMDFLAGS f = ECF_DEFAULT;
if (IsSeparator)
f = ECF_ISSEPARATOR;
else if (IsRoot)
f = ECF_HASSUBCOMMANDS;
else
{
if (!_commandMap_Cur.IsEmpty())
{
// const CCommandMapItem &cmi = ;
if (_commandMap_Cur[0].IsSubMenu())
{
// ODS("ECF_HASSUBCOMMANDS")
f = ECF_HASSUBCOMMANDS;
}
}
}
*pFlags = f;
return S_OK;
// COM_TRY_END
}
Z7_COMWF_B CZipExplorerCommand::EnumSubCommands(IEnumExplorerCommand **ppEnum)
{
ODS("- EnumSubCommands()")
// COM_TRY_BEGIN
*ppEnum = NULL;
if (!_commandMap_Cur.IsEmpty() && _commandMap_Cur[0].IsSubMenu())
{
}
else
{
if (!IsRoot)
return E_NOTIMPL;
if (SubCommands.IsEmpty())
{
return E_NOTIMPL;
}
}
// shellExt->
return QueryInterface(IID_IEnumExplorerCommand, (void **)ppEnum);
// return S_OK;
// COM_TRY_END
}
Z7_COMWF_B CZipContextMenu::Next(ULONG celt, IExplorerCommand **pUICommand, ULONG *pceltFetched)
{
ODS("CZipContextMenu::Next()")
ODS_(Print_Number(celt, "celt"))
ODS_(Print_Number(CurrentSubCommand, "CurrentSubCommand"))
ODS_(Print_Number(SubCommands.Size(), "SubCommands.Size()"))
COM_TRY_BEGIN
ULONG fetched = 0;
ULONG i;
for (i = 0; i < celt; i++)
{
pUICommand[i] = NULL;
}
for (i = 0; i < celt && CurrentSubCommand < SubCommands.Size(); i++)
{
pUICommand[i] = SubCommands[CurrentSubCommand++];
pUICommand[i]->AddRef();
fetched++;
}
if (pceltFetched)
*pceltFetched = fetched;
ODS(fetched == celt ? " === OK === " : "=== ERROR ===")
// we return S_FALSE for (fetched == 0)
return (fetched == celt) ? S_OK : S_FALSE;
COM_TRY_END
}
Z7_COMWF_B CZipContextMenu::Skip(ULONG /* celt */)
{
ODS("CZipContextMenu::Skip()")
return E_NOTIMPL;
}
Z7_COMWF_B CZipContextMenu::Reset(void)
{
ODS("CZipContextMenu::Reset()")
CurrentSubCommand = 0;
return S_OK;
}
Z7_COMWF_B CZipContextMenu::Clone(IEnumExplorerCommand **ppenum)
{
ODS("CZipContextMenu::Clone()")
*ppenum = NULL;
return E_NOTIMPL;
}