root / trunk / Pithos.ShellExtensions / Menus / FileContextMenu.cs @ 5120f3cb
History | View | Annotate | Download (23.9 kB)
1 |
using System; |
---|---|
2 |
using System.Collections.Generic; |
3 |
using System.ComponentModel.Composition; |
4 |
using System.Diagnostics; |
5 |
using System.Diagnostics.Contracts; |
6 |
using System.Drawing; |
7 |
using System.Linq; |
8 |
using System.Runtime.InteropServices; |
9 |
using System.Runtime.InteropServices.ComTypes; |
10 |
using System.Text; |
11 |
using Pithos.ShellExtensions.Properties; |
12 |
|
13 |
namespace Pithos.ShellExtensions.Menus |
14 |
{ |
15 |
[ClassInterface(ClassInterfaceType.None)] |
16 |
[Guid("B1F1405D-94A1-4692-B72F-FC8CAF8B8700"), ComVisible(true)] |
17 |
public class FileContextMenu : IShellExtInit, IContextMenu |
18 |
{ |
19 |
private const string MenuHandlername = "CSShellExtContextMenuHandler.FileContextMenuExt"; |
20 |
|
21 |
|
22 |
private readonly Dictionary<string, MenuItem> _items; |
23 |
|
24 |
|
25 |
[Import] |
26 |
public FileContext Context { get; set; } |
27 |
|
28 |
private IntPtr _gotoBitmap=IntPtr.Zero; |
29 |
private IntPtr _versionBitmap = IntPtr.Zero; |
30 |
private IntPtr _propertiesBitmap = IntPtr.Zero; |
31 |
|
32 |
public FileContextMenu() |
33 |
{ |
34 |
_gotoBitmap = GetBitmapPtr(Resources.MenuGoToPithos); |
35 |
_versionBitmap = GetBitmapPtr(Resources.MenuHistory); |
36 |
_propertiesBitmap = GetBitmapPtr(Resources.MenuProperties); |
37 |
|
38 |
|
39 |
|
40 |
|
41 |
_items = new Dictionary<string, MenuItem>{ |
42 |
{"gotoPithos",new MenuItem{ |
43 |
MenuText = "&Go to Pithos", |
44 |
Verb = "gotoPithos", |
45 |
VerbCanonicalName = "PITHOSGoTo", |
46 |
VerbHelpText = "Go to Pithos", |
47 |
MenuDisplayId = 1, |
48 |
MenuCommand=OnGotoPithos, |
49 |
DisplayFlags=DisplayFlags.All, |
50 |
MenuBitmap = _gotoBitmap |
51 |
}}, |
52 |
/*{"showProperties",new MenuItem{ |
53 |
MenuText = "&Pithos File Properties", |
54 |
Verb = "showProperties", |
55 |
VerbCanonicalName = "PITHOSProperties", |
56 |
VerbHelpText = "Pithos File Properties", |
57 |
MenuDisplayId = 2, |
58 |
MenuCommand=OnShowProperties, |
59 |
DisplayFlags=DisplayFlags.File, |
60 |
MenuBitmap = _propertiesBitmap |
61 |
}},*/ |
62 |
{"prevVersions",new MenuItem{ |
63 |
MenuText = "&Show Previous Versions", |
64 |
Verb = "prevVersions", |
65 |
VerbCanonicalName = "PITHOSPrevVersions", |
66 |
VerbHelpText = "Go to Pithos and display previous versions", |
67 |
MenuDisplayId = 3, |
68 |
MenuCommand=OnVerbDisplayFileName, |
69 |
DisplayFlags=DisplayFlags.File, |
70 |
MenuBitmap=_versionBitmap |
71 |
}} |
72 |
}; |
73 |
|
74 |
IoC.Current.Compose(this); |
75 |
|
76 |
|
77 |
} |
78 |
|
79 |
|
80 |
~FileContextMenu() |
81 |
{ |
82 |
if (_gotoBitmap != IntPtr.Zero) |
83 |
{ |
84 |
NativeMethods.DeleteObject(_gotoBitmap); |
85 |
_gotoBitmap= IntPtr.Zero; |
86 |
} |
87 |
if (_versionBitmap != IntPtr.Zero) |
88 |
{ |
89 |
NativeMethods.DeleteObject(_versionBitmap); |
90 |
_versionBitmap = IntPtr.Zero; |
91 |
} |
92 |
if (_propertiesBitmap != IntPtr.Zero) |
93 |
{ |
94 |
NativeMethods.DeleteObject(_propertiesBitmap); |
95 |
_propertiesBitmap = IntPtr.Zero; |
96 |
} |
97 |
|
98 |
} |
99 |
private static IntPtr GetBitmapPtr(Bitmap gotoBitmap) |
100 |
{ |
101 |
gotoBitmap.MakeTransparent(gotoBitmap.GetPixel(0, 0)); |
102 |
var hbitmap = gotoBitmap.GetHbitmap(); |
103 |
return hbitmap; |
104 |
} |
105 |
|
106 |
void OnShowProperties(IntPtr hWnd) |
107 |
{ |
108 |
|
109 |
} |
110 |
|
111 |
void OnVerbDisplayFileName(IntPtr hWnd) |
112 |
{ |
113 |
string message = String.Format("The selected file is {0}\r\n\r\nThe selected Path is {1}", |
114 |
Context.CurrentFile, |
115 |
Context.CurrentFolder); |
116 |
|
117 |
System.Windows.Forms.MessageBox.Show( |
118 |
message, |
119 |
"CSShellExtContextMenuHandler"); |
120 |
NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_ASSOCCHANGED, HChangeNotifyFlags.SHCNF_IDLIST, |
121 |
IntPtr.Zero, IntPtr.Zero); |
122 |
} |
123 |
|
124 |
void OnGotoPithos(IntPtr hWnd) |
125 |
{ |
126 |
var settings = Context.Settings; |
127 |
var activeAccount = settings.Accounts.FirstOrDefault(acc => acc.IsActive); |
128 |
var address = String.Format("{0}/ui/?token={1}&user={2}", |
129 |
settings.PithosSite, |
130 |
activeAccount.ApiKey, |
131 |
Uri.EscapeUriString(activeAccount.AccountName)); |
132 |
|
133 |
settings.Reload(); |
134 |
Process.Start(address); |
135 |
} |
136 |
|
137 |
|
138 |
#region Shell Extension Registration |
139 |
|
140 |
[ComRegisterFunction] |
141 |
public static void Register(Type t) |
142 |
{ |
143 |
try |
144 |
{ |
145 |
ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, ".cs", |
146 |
MenuHandlername); |
147 |
ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, "Directory", |
148 |
MenuHandlername); |
149 |
ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, @"Directory\Background", |
150 |
MenuHandlername); |
151 |
ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, "*", |
152 |
MenuHandlername); |
153 |
|
154 |
//ShellExtReg.MarkApproved(t.GUID, MenuHandlername); |
155 |
} |
156 |
catch (Exception ex) |
157 |
{ |
158 |
Console.WriteLine(ex.Message); // Log the error |
159 |
throw; // Re-throw the exception |
160 |
} |
161 |
} |
162 |
|
163 |
[ComUnregisterFunction] |
164 |
public static void Unregister(Type t) |
165 |
{ |
166 |
try |
167 |
{ |
168 |
ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, ".cs", MenuHandlername); |
169 |
ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, "Directory", MenuHandlername); |
170 |
ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, @"Directory\Background", MenuHandlername); |
171 |
ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, "*", MenuHandlername); |
172 |
|
173 |
//ShellExtReg.RemoveApproved(t.GUID, MenuHandlername); |
174 |
} |
175 |
catch (Exception ex) |
176 |
{ |
177 |
Console.WriteLine(ex.Message); // Log the error |
178 |
throw; // Re-throw the exception |
179 |
} |
180 |
} |
181 |
|
182 |
#endregion |
183 |
|
184 |
|
185 |
#region IShellExtInit Members |
186 |
|
187 |
/// <summary> |
188 |
/// Initialize the context menu handler. |
189 |
/// </summary> |
190 |
/// <param name="pidlFolder"> |
191 |
/// A pointer to an ITEMIDLIST structure that uniquely identifies a folder. |
192 |
/// </param> |
193 |
/// <param name="pDataObj"> |
194 |
/// A pointer to an IDataObject interface object that can be used to retrieve |
195 |
/// the objects being acted upon. |
196 |
/// </param> |
197 |
/// <param name="hKeyProgID"> |
198 |
/// The registry key for the file object or folder type. |
199 |
/// </param> |
200 |
public void Initialize(IntPtr pidlFolder, IntPtr pDataObj, IntPtr hKeyProgID) |
201 |
{ |
202 |
|
203 |
if(pDataObj == IntPtr.Zero && pidlFolder == IntPtr.Zero) |
204 |
{ |
205 |
throw new ArgumentException("pidlFolder and pDataObj shouldn't be null at the same time"); |
206 |
} |
207 |
|
208 |
|
209 |
Debug.WriteLine("Initializing", LogCategories.ShellMenu); |
210 |
|
211 |
if (pDataObj != IntPtr.Zero) |
212 |
{ |
213 |
Debug.WriteLine("Got a data object", LogCategories.ShellMenu); |
214 |
|
215 |
FORMATETC fe = new FORMATETC(); |
216 |
fe.cfFormat = (short)CLIPFORMAT.CF_HDROP; |
217 |
fe.ptd = IntPtr.Zero; |
218 |
fe.dwAspect = DVASPECT.DVASPECT_CONTENT; |
219 |
fe.lindex = -1; |
220 |
fe.tymed = TYMED.TYMED_HGLOBAL; |
221 |
STGMEDIUM stm = new STGMEDIUM(); |
222 |
|
223 |
// The pDataObj pointer contains the objects being acted upon. In this |
224 |
// example, we get an HDROP handle for enumerating the selected files |
225 |
// and folders. |
226 |
IDataObject dataObject = (IDataObject)Marshal.GetObjectForIUnknown(pDataObj); |
227 |
dataObject.GetData(ref fe, out stm); |
228 |
|
229 |
try |
230 |
{ |
231 |
// Get an HDROP handle. |
232 |
IntPtr hDrop = stm.unionmember; |
233 |
if (hDrop == IntPtr.Zero) |
234 |
{ |
235 |
throw new ArgumentException(); |
236 |
} |
237 |
|
238 |
// Determine how many files are involved in this operation. |
239 |
uint nFiles = NativeMethods.DragQueryFile(hDrop, UInt32.MaxValue, null, 0); |
240 |
|
241 |
Debug.WriteLine(String.Format("Got {0} files", nFiles), LogCategories.ShellMenu); |
242 |
// This code sample displays the custom context menu item when only |
243 |
// one file is selected. |
244 |
if (nFiles == 1) |
245 |
{ |
246 |
// Get the path of the file. |
247 |
var fileName = new StringBuilder(260); |
248 |
if (0 == NativeMethods.DragQueryFile(hDrop, 0, fileName, |
249 |
fileName.Capacity)) |
250 |
{ |
251 |
Marshal.ThrowExceptionForHR(WinError.E_FAIL); |
252 |
} |
253 |
Context.CurrentFile = fileName.ToString(); |
254 |
} |
255 |
/* else |
256 |
{ |
257 |
Marshal.ThrowExceptionForHR(WinError.E_FAIL); |
258 |
}*/ |
259 |
|
260 |
// [-or-] |
261 |
|
262 |
// Enumerate the selected files and folders. |
263 |
//if (nFiles > 0) |
264 |
//{ |
265 |
// StringCollection selectedFiles = new StringCollection(); |
266 |
// StringBuilder fileName = new StringBuilder(260); |
267 |
// for (uint i = 0; i < nFiles; i++) |
268 |
// { |
269 |
// // Get the next file name. |
270 |
// if (0 != NativeMethods.DragQueryFile(hDrop, i, fileName, |
271 |
// fileName.Capacity)) |
272 |
// { |
273 |
// // Add the file name to the list. |
274 |
// selectedFiles.Add(fileName.ToString()); |
275 |
// } |
276 |
// } |
277 |
// |
278 |
// // If we did not find any files we can work with, throw |
279 |
// // exception. |
280 |
// if (selectedFiles.Count == 0) |
281 |
// { |
282 |
// Marshal.ThrowExceptionForHR(WinError.E_FAIL); |
283 |
// } |
284 |
//} |
285 |
//else |
286 |
//{ |
287 |
// Marshal.ThrowExceptionForHR(WinError.E_FAIL); |
288 |
//} |
289 |
} |
290 |
finally |
291 |
{ |
292 |
NativeMethods.ReleaseStgMedium(ref stm); |
293 |
} |
294 |
} |
295 |
|
296 |
if (pidlFolder != IntPtr.Zero) |
297 |
{ |
298 |
Debug.WriteLine("Got a folder", LogCategories.ShellMenu); |
299 |
StringBuilder path = new StringBuilder(); |
300 |
if (!NativeMethods.SHGetPathFromIDList(pidlFolder, path)) |
301 |
{ |
302 |
int error = Marshal.GetHRForLastWin32Error(); |
303 |
Marshal.ThrowExceptionForHR(error); |
304 |
} |
305 |
Context.CurrentFolder = path.ToString(); |
306 |
Debug.WriteLine(String.Format("Folder is {0}", Context.CurrentFolder), LogCategories.ShellMenu); |
307 |
} |
308 |
} |
309 |
|
310 |
#endregion |
311 |
|
312 |
|
313 |
#region IContextMenu Members |
314 |
|
315 |
/// <summary> |
316 |
/// Add commands to a shortcut menu. |
317 |
/// </summary> |
318 |
/// <param name="hMenu">A handle to the shortcut menu.</param> |
319 |
/// <param name="iMenu"> |
320 |
/// The zero-based position at which to insert the first new menu item. |
321 |
/// </param> |
322 |
/// <param name="idCmdFirst"> |
323 |
/// The minimum value that the handler can specify for a menu item ID. |
324 |
/// </param> |
325 |
/// <param name="idCmdLast"> |
326 |
/// The maximum value that the handler can specify for a menu item ID. |
327 |
/// </param> |
328 |
/// <param name="uFlags"> |
329 |
/// Optional flags that specify how the shortcut menu can be changed. |
330 |
/// </param> |
331 |
/// <returns> |
332 |
/// If successful, returns an HRESULT value that has its severity value set |
333 |
/// to SEVERITY_SUCCESS and its code value set to the offset of the largest |
334 |
/// command identifier that was assigned, plus one. |
335 |
/// </returns> |
336 |
public int QueryContextMenu( |
337 |
IntPtr hMenu, |
338 |
uint iMenu, |
339 |
uint idCmdFirst, |
340 |
uint idCmdLast, |
341 |
uint uFlags) |
342 |
{ |
343 |
Debug.WriteLine("Start qcm", LogCategories.ShellMenu); |
344 |
// If uFlags include CMF_DEFAULTONLY then we should not do anything. |
345 |
Debug.WriteLine(String.Format("Flags {0}", uFlags), LogCategories.ShellMenu); |
346 |
|
347 |
if (((uint)CMF.CMF_DEFAULTONLY & uFlags) != 0) |
348 |
{ |
349 |
Debug.WriteLine("Default only flag, returning", LogCategories.ShellMenu); |
350 |
return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0, 0); |
351 |
} |
352 |
|
353 |
if (!Context.IsManaged) |
354 |
{ |
355 |
Debug.WriteLine("Not a PITHOS folder",LogCategories.ShellMenu); |
356 |
return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0, 0); |
357 |
} |
358 |
|
359 |
/* |
360 |
if (!selectedFolder.ToLower().Contains("pithos")) |
361 |
return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0, 0); |
362 |
*/ |
363 |
|
364 |
// Use either InsertMenu or InsertMenuItem to add menu items. |
365 |
|
366 |
uint largestID = 0; |
367 |
|
368 |
DisplayFlags itemType = (Context.IsFolder) ? DisplayFlags.Folder : DisplayFlags.File; |
369 |
|
370 |
Debug.WriteLine(String.Format("Item Flags {0}", itemType), LogCategories.ShellMenu); |
371 |
|
372 |
if (!NativeMethods.InsertMenu(hMenu, idCmdFirst, MF.MF_SEPARATOR | MF.MF_BYPOSITION, 0, String.Empty)) |
373 |
{ |
374 |
Log.ErrorFormat("Error adding separator 1\r\n{0}", Marshal.GetLastWin32Error()); |
375 |
return Marshal.GetHRForLastWin32Error(); |
376 |
} |
377 |
|
378 |
foreach (var menuItem in _items.Values) |
379 |
{ |
380 |
Debug.WriteLine(String.Format("Menu Flags {0}", menuItem.DisplayFlags), LogCategories.ShellMenu); |
381 |
if ((itemType & menuItem.DisplayFlags) != DisplayFlags.None) |
382 |
{ |
383 |
Debug.WriteLine("Adding Menu", LogCategories.ShellMenu); |
384 |
|
385 |
MENUITEMINFO mii = menuItem.CreateInfo(idCmdFirst); |
386 |
if (!NativeMethods.InsertMenuItem(hMenu, iMenu, true, ref mii)) |
387 |
{ |
388 |
var lastError = Marshal.GetLastWin32Error(); |
389 |
var lastErrorHR = Marshal.GetHRForLastWin32Error(); |
390 |
return lastErrorHR; |
391 |
} |
392 |
if (largestID < menuItem.MenuDisplayId) |
393 |
largestID = menuItem.MenuDisplayId; |
394 |
} |
395 |
} |
396 |
|
397 |
Debug.Write("Adding Separator 1", LogCategories.ShellMenu); |
398 |
// Add a separator. |
399 |
/* MENUITEMINFO sep = new MENUITEMINFO(); |
400 |
sep.cbSize = (uint)Marshal.SizeOf(sep); |
401 |
sep.fMask = MIIM.MIIM_TYPE; |
402 |
sep.fType = MFT.MFT_SEPARATOR;*/ |
403 |
if (!NativeMethods.InsertMenu(hMenu, (uint)_items.Values.Count + idCmdFirst+1,MF.MF_SEPARATOR|MF.MF_BYPOSITION, 0, String.Empty)) |
404 |
{ |
405 |
Log.ErrorFormat("Error adding separator 1\r\n{0}", Marshal.GetLastWin32Error()); |
406 |
return Marshal.GetHRForLastWin32Error(); |
407 |
} |
408 |
|
409 |
|
410 |
|
411 |
|
412 |
Debug.WriteLine("Menus added", LogCategories.ShellOverlays); |
413 |
// Return an HRESULT value with the severity set to SEVERITY_SUCCESS. |
414 |
// Set the code value to the offset of the largest command identifier |
415 |
// that was assigned, plus one (1). |
416 |
return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0, |
417 |
largestID + 1); |
418 |
} |
419 |
|
420 |
/// <summary> |
421 |
/// Carry out the command associated with a shortcut menu item. |
422 |
/// </summary> |
423 |
/// <param name="pici"> |
424 |
/// A pointer to a CMINVOKECOMMANDINFO or CMINVOKECOMMANDINFOEX structure |
425 |
/// containing information about the command. |
426 |
/// </param> |
427 |
public void InvokeCommand(IntPtr pici) |
428 |
{ |
429 |
bool isUnicode = false; |
430 |
|
431 |
// Determine which structure is being passed in, CMINVOKECOMMANDINFO or |
432 |
// CMINVOKECOMMANDINFOEX based on the cbSize member of lpcmi. Although |
433 |
// the lpcmi parameter is declared in Shlobj.h as a CMINVOKECOMMANDINFO |
434 |
// structure, in practice it often points to a CMINVOKECOMMANDINFOEX |
435 |
// structure. This struct is an extended version of CMINVOKECOMMANDINFO |
436 |
// and has additional members that allow Unicode strings to be passed. |
437 |
CMINVOKECOMMANDINFO ici = (CMINVOKECOMMANDINFO)Marshal.PtrToStructure( |
438 |
pici, typeof(CMINVOKECOMMANDINFO)); |
439 |
CMINVOKECOMMANDINFOEX iciex = new CMINVOKECOMMANDINFOEX(); |
440 |
if (ici.cbSize == Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX))) |
441 |
{ |
442 |
if ((ici.fMask & CMIC.CMIC_MASK_UNICODE) != 0) |
443 |
{ |
444 |
isUnicode = true; |
445 |
iciex = (CMINVOKECOMMANDINFOEX)Marshal.PtrToStructure(pici, |
446 |
typeof(CMINVOKECOMMANDINFOEX)); |
447 |
} |
448 |
} |
449 |
|
450 |
// Determines whether the command is identified by its offset or verb. |
451 |
// There are two ways to identify commands: |
452 |
// |
453 |
// 1) The command's verb string |
454 |
// 2) The command's identifier offset |
455 |
// |
456 |
// If the high-order word of lpcmi->lpVerb (for the ANSI case) or |
457 |
// lpcmi->lpVerbW (for the Unicode case) is nonzero, lpVerb or lpVerbW |
458 |
// holds a verb string. If the high-order word is zero, the command |
459 |
// offset is in the low-order word of lpcmi->lpVerb. |
460 |
|
461 |
// For the ANSI case, if the high-order word is not zero, the command's |
462 |
// verb string is in lpcmi->lpVerb. |
463 |
if (!isUnicode && NativeMethods.HighWord(ici.verb.ToInt32()) != 0) |
464 |
{ |
465 |
// Is the verb supported by this context menu extension? |
466 |
string verbAnsi = Marshal.PtrToStringAnsi(ici.verb); |
467 |
if (_items.ContainsKey(verbAnsi)) |
468 |
{ |
469 |
_items[verbAnsi].MenuCommand(ici.hwnd); |
470 |
} |
471 |
else |
472 |
{ |
473 |
// If the verb is not recognized by the context menu handler, it |
474 |
// must return E_FAIL to allow it to be passed on to the other |
475 |
// context menu handlers that might implement that verb. |
476 |
Marshal.ThrowExceptionForHR(WinError.E_FAIL); |
477 |
} |
478 |
} |
479 |
|
480 |
// For the Unicode case, if the high-order word is not zero, the |
481 |
// command's verb string is in lpcmi->lpVerbW. |
482 |
else if (isUnicode && NativeMethods.HighWord(iciex.verbW.ToInt32()) != 0) |
483 |
{ |
484 |
// Is the verb supported by this context menu extension? |
485 |
string verbUTF = Marshal.PtrToStringUni(iciex.verbW); |
486 |
if (_items.ContainsKey(verbUTF)) |
487 |
{ |
488 |
_items[verbUTF].MenuCommand(ici.hwnd); |
489 |
} |
490 |
else |
491 |
{ |
492 |
// If the verb is not recognized by the context menu handler, it |
493 |
// must return E_FAIL to allow it to be passed on to the other |
494 |
// context menu handlers that might implement that verb. |
495 |
Marshal.ThrowExceptionForHR(WinError.E_FAIL); |
496 |
} |
497 |
} |
498 |
|
499 |
// If the command cannot be identified through the verb string, then |
500 |
// check the identifier offset. |
501 |
else |
502 |
{ |
503 |
// Is the command identifier offset supported by this context menu |
504 |
// extension? |
505 |
int menuID = NativeMethods.LowWord(ici.verb.ToInt32()); |
506 |
var menuItem = _items.FirstOrDefault(item => item.Value.MenuDisplayId == menuID).Value; |
507 |
if (menuItem != null) |
508 |
{ |
509 |
menuItem.MenuCommand(ici.hwnd); |
510 |
} |
511 |
else |
512 |
{ |
513 |
// If the verb is not recognized by the context menu handler, it |
514 |
// must return E_FAIL to allow it to be passed on to the other |
515 |
// context menu handlers that might implement that verb. |
516 |
Marshal.ThrowExceptionForHR(WinError.E_FAIL); |
517 |
} |
518 |
} |
519 |
} |
520 |
|
521 |
/// <summary> |
522 |
/// Get information about a shortcut menu command, including the help string |
523 |
/// and the language-independent, or canonical, name for the command. |
524 |
/// </summary> |
525 |
/// <param name="idCmd">Menu command identifier offset.</param> |
526 |
/// <param name="uFlags"> |
527 |
/// Flags specifying the information to return. This parameter can have one |
528 |
/// of the following values: GCS_HELPTEXTA, GCS_HELPTEXTW, GCS_VALIDATEA, |
529 |
/// GCS_VALIDATEW, GCS_VERBA, GCS_VERBW. |
530 |
/// </param> |
531 |
/// <param name="pReserved">Reserved. Must be IntPtr.Zero</param> |
532 |
/// <param name="pszName"> |
533 |
/// The address of the buffer to receive the null-terminated string being |
534 |
/// retrieved. |
535 |
/// </param> |
536 |
/// <param name="cchMax"> |
537 |
/// Size of the buffer, in characters, to receive the null-terminated string. |
538 |
/// </param> |
539 |
public void GetCommandString( |
540 |
UIntPtr idCmd, |
541 |
uint uFlags, |
542 |
IntPtr pReserved, |
543 |
StringBuilder pszName, |
544 |
uint cchMax) |
545 |
{ |
546 |
uint menuID = idCmd.ToUInt32(); |
547 |
var menuItem = _items.FirstOrDefault(item => item.Value.MenuDisplayId == menuID).Value; |
548 |
if (menuItem != null) |
549 |
{ |
550 |
switch ((GCS)uFlags) |
551 |
{ |
552 |
case GCS.GCS_VERBW: |
553 |
if (menuItem.VerbCanonicalName.Length > cchMax - 1) |
554 |
{ |
555 |
Marshal.ThrowExceptionForHR(WinError.STRSAFE_E_INSUFFICIENT_BUFFER); |
556 |
} |
557 |
else |
558 |
{ |
559 |
pszName.Clear(); |
560 |
pszName.Append(menuItem.VerbCanonicalName); |
561 |
} |
562 |
break; |
563 |
|
564 |
case GCS.GCS_HELPTEXTW: |
565 |
if (menuItem.VerbHelpText.Length > cchMax - 1) |
566 |
{ |
567 |
Marshal.ThrowExceptionForHR(WinError.STRSAFE_E_INSUFFICIENT_BUFFER); |
568 |
} |
569 |
else |
570 |
{ |
571 |
pszName.Clear(); |
572 |
pszName.Append(menuItem.VerbHelpText); |
573 |
} |
574 |
break; |
575 |
} |
576 |
} |
577 |
} |
578 |
|
579 |
#endregion |
580 |
} |
581 |
} |