-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathFileContextMenuExt.cpp
More file actions
388 lines (332 loc) · 12.4 KB
/
FileContextMenuExt.cpp
File metadata and controls
388 lines (332 loc) · 12.4 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
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
/****************************** Module Header ******************************\
Module Name: FileContextMenuExt.cpp
Project: CppShellExtContextMenuHandler
Copyright (c) Microsoft Corporation.
The code sample demonstrates creating a Shell context menu handler with C++.
A context menu handler is a shell extension handler that adds commands to an
existing context menu. Context menu handlers are associated with a particular
file class and are called any time a context menu is displayed for a member
of the class. While you can add items to a file class context menu with the
registry, the items will be the same for all members of the class. By
implementing and registering such a handler, you can dynamically add items to
an object's context menu, customized for the particular object.
The example context menu handler adds the menu item "Display File Name (C++)"
to the context menu when you right-click a .cpp file in the Windows Explorer.
Clicking the menu item brings up a message box that displays the full path
of the .cpp file.
This source is subject to the Microsoft Public License.
See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
All other rights reserved.
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
\***************************************************************************/
#include "FileContextMenuExt.h"
#include "resource.h"
#include <strsafe.h>
#include <Shlwapi.h>
#include "common.h"
#pragma comment(lib, "shlwapi.lib")
extern HINSTANCE g_hInst;
extern long g_cDllRef;
#define IDM_DISPLAY 0 // The command's identifier offset
FileContextMenuExt::FileContextMenuExt(void)
: m_cRef(1),
m_pszMenuText(L_Menu_Text),
m_pszVerb(Verb_Name),
m_pwszVerb(L_Verb_Name),
m_pszVerbCanonicalName(Verb_Canonical_Name),
m_pwszVerbCanonicalName(L_Verb_Canonical_Name),
m_pszVerbHelpText(Verb_Help_Text),
m_pwszVerbHelpText(L_Verb_Help_Text)
{
InterlockedIncrement(&g_cDllRef);
// Load the bitmap for the menu item.
// If you want the menu item bitmap to be transparent, the color depth of
// the bitmap must not be greater than 8bpp.
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);
}
void FileContextMenuExt::OnVerbDisplayFileName(HWND hWnd)
{
for (size_t i = 0; i < m_vSelectedFiles.size() && i < 2; i++)
{
wchar_t szMessage[300];
if (SUCCEEDED(StringCchPrintf(szMessage, ARRAYSIZE(szMessage),
L"The selected file is:\r\n\r\n%s", m_vSelectedFiles[i].c_str())))
{
MessageBox(hWnd, szMessage, L_Friendly_Menu_Name, 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.
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. In this
// example, we get an HDROP handle for enumerating the selected files and
// folders.
if (SUCCEEDED(pDataObj->GetData(&fe, &stm)))
{
// Get an HDROP handle.
HDROP hDrop = static_cast<HDROP>(GlobalLock(stm.hGlobal));
if (hDrop != NULL)
{
// Determine how many files are involved in this operation. This
// code sample displays the custom context menu item when only
// one file is selected.
UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
for (size_t i = 0; i < nFiles; i++)
{
wchar_t szSelectedFile[MAX_PATH] = { 0 };
// Get the path of the file.
if (0 != DragQueryFile(hDrop, i, szSelectedFile, ARRAYSIZE(szSelectedFile)))
{
m_vSelectedFiles.push_back(szSelectedFile);
hr = S_OK;
continue;
}
hr = E_FAIL;
break;
}
GlobalUnlock(stm.hGlobal);
}
ReleaseStgMedium(&stm);
}
if (S_OK == hr)
{
for (auto file = m_vSelectedFiles.cbegin(); file != m_vSelectedFiles.cend(); ++file)
{
const wchar_t *dot = wcsrchr(file->c_str(), L'.');
if (dot && 0 != _wcsicmp(dot, L_Associated_Type))
{
hr = E_INVALIDARG;
break;
}
}
}
// 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
MENUITEMINFOW 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.
MENUITEMINFOW 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;
// Determine which structure is being passed in, CMINVOKECOMMANDINFO or
// CMINVOKECOMMANDINFOEX based on the cbSize member of lpcmi. Although
// the lpcmi parameter is declared in Shlobj.h as a CMINVOKECOMMANDINFO
// structure, in practice it often points to a CMINVOKECOMMANDINFOEX
// structure. This struct is an extended version of CMINVOKECOMMANDINFO
// and has additional members that allow Unicode strings to be passed.
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)
{
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)
{
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)
{
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