-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathFileContextMenuExt.cpp
More file actions
353 lines (303 loc) · 11.3 KB
/
FileContextMenuExt.cpp
File metadata and controls
353 lines (303 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
#include "FileContextMenuExt.h"
#include "resource.h"
#include <strsafe.h>
#include <cstring>
#include <string>
#pragma comment(lib, "shlwapi.lib")
extern HINSTANCE g_hInst;
extern long g_cDllRef;
#define IDM_DISPLAY 0 // The command's identifier offset
#define STRSIZE_IN_BYTES(x) (_countof(x) * sizeof(TCHAR))
#define StringCopy(x, y) StringCbCopyW(x, STRSIZE_IN_BYTES(x), y);
FileContextMenuExt::FileContextMenuExt(void) : m_cRef(1),
m_pszMenuText(L"&Create sum.log"),
m_pszVerb("calculate"),
m_pwszVerb(L"calculate"),
m_pszVerbCanonicalName("AddToSumLog"),
m_pwszVerbCanonicalName(L"AddToSumLog"),
m_pszVerbHelpText("Create sum.log"),
m_pwszVerbHelpText(L"Create sum.log"),
m_nFiles(0)
{
InterlockedIncrement(&g_cDllRef);
// Load the bitmap for the menu item.
m_hMenuBmp = LoadImage(g_hInst, MAKEINTRESOURCE(IDB_OK),
IMAGE_BITMAP, 0, 0, LR_DEFAULTSIZE | LR_LOADTRANSPARENT);
}
FileContextMenuExt::~FileContextMenuExt(void)
{
if (m_hMenuBmp)
{
DeleteObject(m_hMenuBmp);
m_hMenuBmp = NULL;
}
InterlockedDecrement(&g_cDllRef);
delete [] m_pwszLogFileName;
}
void FileContextMenuExt::OnVerbDisplayFileName(HWND hWnd)
{
wchar_t szMessage[300];
if (m_nFiles == 0) StringCchPrintf(szMessage, ARRAYSIZE(szMessage),
L"Error: can't create log file");
if (m_nFiles == 1) StringCchPrintf(szMessage, ARRAYSIZE(szMessage),
L"1 file was added to sum.log");
else if (m_nFiles > 1) StringCchPrintf(szMessage, ARRAYSIZE(szMessage),
L"%d files were added to sum.log", m_nFiles);
MessageBox(hWnd, szMessage, L"CppShellExtContextMenuHandler", MB_OK);
}
#pragma region IUnknown
// Query to the interface the component supported.
IFACEMETHODIMP FileContextMenuExt::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(FileContextMenuExt, IContextMenu),
QITABENT(FileContextMenuExt, IShellExtInit),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
// Increase the reference count for an interface on an object.
IFACEMETHODIMP_(ULONG) FileContextMenuExt::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
// Decrease the reference count for an interface on an object.
IFACEMETHODIMP_(ULONG) FileContextMenuExt::Release()
{
ULONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
{
delete this;
}
return cRef;
}
#pragma endregion
#pragma region IShellExtInit
// Initialize the context menu handler.
// pidlFolder holds a folder's pointer to an item identifier list (PIDL). This is an absolute PIDL. For property sheet extensions, this value is NULL.
// pDataObject holds a pointer to a data object's IDataObject interface. The data object holds one or more file names in CF_HDROP format.
// hRegKey holds a registry key for the file object or folder type.
IFACEMETHODIMP FileContextMenuExt::Initialize(
LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hKeyProgID)
{
if (NULL == pDataObj)
{
return E_INVALIDARG;
}
HRESULT hr = E_FAIL;
FORMATETC fe = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
STGMEDIUM stm;
// The pDataObj pointer contains the objects being acted upon.
if (SUCCEEDED(pDataObj->GetData(&fe, &stm)))
{
using namespace std;
// Get an HDROP handle.
HDROP hDrop = static_cast<HDROP>(GlobalLock(stm.hGlobal));
if (hDrop != NULL)
{
// Determine how many files are involved in this operation.
m_nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
// add files to filemap
WCHAR wszSelectedFile[MAX_PATH];
for (UINT i = 0; i < m_nFiles; i++) {
UINT buf_size = DragQueryFile(hDrop, i, wszSelectedFile,
ARRAYSIZE(wszSelectedFile));
// loop break if current object is directory
if (GetFileAttributes(wszSelectedFile) & FILE_ATTRIBUTE_DIRECTORY) {
m_nFiles = 0;
break;
}
else // else add this object to filemap
m_fMap.insert(wszSelectedFile);
}
if (m_nFiles > 0) {
hr = S_OK;
std::wstring temp(wszSelectedFile);
temp.resize(temp.rfind(L'\\') + 1);
temp += L"sum.log";
m_pwszLogFileName = new WCHAR[MAX_PATH];
wcscpy(m_pwszLogFileName, temp.c_str());
}
GlobalUnlock(stm.hGlobal);
}
ReleaseStgMedium(&stm);
}
// If any value other than S_OK is returned from the method, the context
// menu item is not displayed.
return hr;
}
#pragma endregion
#pragma region IContextMenu
//
// FUNCTION: FileContextMenuExt::QueryContextMenu
//
// PURPOSE: The Shell calls IContextMenu::QueryContextMenu to allow the
// context menu handler to add its menu items to the menu. It
// passes in the HMENU handle in the hmenu parameter. The
// indexMenu parameter is set to the index to be used for the
// first menu item that is to be added.
IFACEMETHODIMP FileContextMenuExt::QueryContextMenu(
HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
{
// If uFlags include CMF_DEFAULTONLY then we should not do anything.
if (CMF_DEFAULTONLY & uFlags)
{
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(0));
}
// Use either InsertMenu or InsertMenuItem to add menu items.
// Learn how to add sub-menu from:
// http://www.codeproject.com/KB/shell/ctxextsubmenu.aspx
MENUITEMINFO mii = { sizeof(mii) };
mii.fMask = MIIM_BITMAP | MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE;
mii.wID = idCmdFirst + IDM_DISPLAY;
mii.fType = MFT_STRING;
mii.dwTypeData = m_pszMenuText;
mii.fState = MFS_ENABLED;
mii.hbmpItem = static_cast<HBITMAP>(m_hMenuBmp);
if (!InsertMenuItem(hMenu, indexMenu, TRUE, &mii))
{
return HRESULT_FROM_WIN32(GetLastError());
}
// Add a separator.
MENUITEMINFO sep = { sizeof(sep) };
sep.fMask = MIIM_TYPE;
sep.fType = MFT_SEPARATOR;
if (!InsertMenuItem(hMenu, indexMenu + 1, TRUE, &sep))
{
return HRESULT_FROM_WIN32(GetLastError());
}
// Return an HRESULT value with the severity set to SEVERITY_SUCCESS.
// Set the code value to the offset of the largest command identifier
// that was assigned, plus one (1).
return MAKE_HRESULT(SEVERITY_SUCCESS, 0, USHORT(IDM_DISPLAY + 1));
}
//
// FUNCTION: FileContextMenuExt::InvokeCommand
//
// PURPOSE: This method is called when a user clicks a menu item to tell
// the handler to run the associated command. The lpcmi parameter
// points to a structure that contains the needed information.
IFACEMETHODIMP FileContextMenuExt::InvokeCommand(LPCMINVOKECOMMANDINFO pici)
{
BOOL fUnicode = FALSE;
if (pici->cbSize == sizeof(CMINVOKECOMMANDINFOEX))
{
if (pici->fMask & CMIC_MASK_UNICODE)
{
fUnicode = TRUE;
}
}
// Determines whether the command is identified by its offset or verb.
// There are two ways to identify commands:
//
// 1) The command's verb string
// 2) The command's identifier offset
//
// If the high-order word of lpcmi->lpVerb (for the ANSI case) or
// lpcmi->lpVerbW (for the Unicode case) is nonzero, lpVerb or lpVerbW
// holds a verb string. If the high-order word is zero, the command
// offset is in the low-order word of lpcmi->lpVerb.
// For the ANSI case, if the high-order word is not zero, the command's
// verb string is in lpcmi->lpVerb.
if (!fUnicode && HIWORD(pici->lpVerb))
{
// Is the verb supported by this context menu extension?
if (StrCmpIA(pici->lpVerb, m_pszVerb) == 0)
{
//if (m_fLog.WriteLog(m_pwszLogFileName)) m_nFiles = 0;
if (!m_fMap.WriteLog(m_pwszLogFileName)) m_nFiles = 0;
OnVerbDisplayFileName(pici->hwnd);
}
else
{
// If the verb is not recognized by the context menu handler, it
// must return E_FAIL to allow it to be passed on to the other
// context menu handlers that might implement that verb.
return E_FAIL;
}
}
// For the Unicode case, if the high-order word is not zero, the
// command's verb string is in lpcmi->lpVerbW.
else if (fUnicode && HIWORD(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW))
{
// Is the verb supported by this context menu extension?
if (StrCmpIW(((CMINVOKECOMMANDINFOEX*)pici)->lpVerbW, m_pwszVerb) == 0)
{
//if (m_fLog.WriteLog(m_pwszLogFileName)) m_nFiles = 0;
if (!m_fMap.WriteLog(m_pwszLogFileName)) m_nFiles = 0;
OnVerbDisplayFileName(pici->hwnd);
}
else
{
// If the verb is not recognized by the context menu handler, it
// must return E_FAIL to allow it to be passed on to the other
// context menu handlers that might implement that verb.
return E_FAIL;
}
}
// If the command cannot be identified through the verb string, then
// check the identifier offset.
else
{
// Is the command identifier offset supported by this context menu
// extension?
if (LOWORD(pici->lpVerb) == IDM_DISPLAY)
{
//if (m_fLog.WriteLog(m_pwszLogFileName)) m_nFiles = 0;
if (!m_fMap.WriteLog(m_pwszLogFileName)) m_nFiles = 0;
OnVerbDisplayFileName(pici->hwnd);
}
else
{
// If the verb is not recognized by the context menu handler, it
// must return E_FAIL to allow it to be passed on to the other
// context menu handlers that might implement that verb.
return E_FAIL;
}
}
return S_OK;
}
//
// FUNCTION: CFileContextMenuExt::GetCommandString
//
// PURPOSE: If a user highlights one of the items added by a context menu
// handler, the handler's IContextMenu::GetCommandString method is
// called to request a Help text string that will be displayed on
// the Windows Explorer status bar. This method can also be called
// to request the verb string that is assigned to a command.
// Either ANSI or Unicode verb strings can be requested. This
// example only implements support for the Unicode values of
// uFlags, because only those have been used in Windows Explorer
// since Windows 2000.
//
IFACEMETHODIMP FileContextMenuExt::GetCommandString(UINT_PTR idCommand,
UINT uFlags, UINT *pwReserved, LPSTR pszName, UINT cchMax)
{ // âîçâðàùàåò òåêñò, êîòîðûé áóäåò îòîáðàæàòüñÿ íà ïàíåëè ñòàòóñà
HRESULT hr = E_INVALIDARG;
if (idCommand == IDM_DISPLAY)
{
switch (uFlags)
{
case GCS_HELPTEXTW:
// Only useful for pre-Vista versions of Windows that have a
// Status bar.
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
m_pwszVerbHelpText);
break;
case GCS_VERBW:
// GCS_VERBW is an optional feature that enables a caller to
// discover the canonical name for the verb passed in through
// idCommand.
hr = StringCchCopy(reinterpret_cast<PWSTR>(pszName), cchMax,
m_pwszVerbCanonicalName);
break;
default:
hr = S_OK;
}
}
// If the command (idCommand) is not supported by this context menu
// extension handler, return E_INVALIDARG.
return hr;
}
#pragma endregion