ShellContextMenu.cs 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584
  1. using System;
  2. using System.Drawing;
  3. using System.IO;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6. using System.Windows.Forms;
  7. namespace GeekDesk.Util
  8. {
  9. /// <summary>
  10. /// "Stand-alone" shell context menu
  11. ///
  12. /// It isn't really debugged but is mostly working.
  13. /// Create an instance and call ShowContextMenu with a list of FileInfo for the files.
  14. /// Limitation is that it only handles files in the same directory but it can be fixed
  15. /// by changing the way files are translated into PIDLs.
  16. ///
  17. /// Based on FileBrowser in C# from CodeProject
  18. /// http://www.codeproject.com/useritems/FileBrowser.asp
  19. ///
  20. /// Hooking class taken from MSDN Magazine Cutting Edge column
  21. /// http://msdn.microsoft.com/msdnmag/issues/02/10/CuttingEdge/
  22. ///
  23. /// Andreas Johansson
  24. /// [email protected]
  25. /// http://afjohansson.spaces.live.com
  26. /// </summary>
  27. /// <example>
  28. /// ShellContextMenu scm = new ShellContextMenu();
  29. /// FileInfo[] files = new FileInfo[1];
  30. /// files[0] = new FileInfo(@"c:\windows\notepad.exe");
  31. /// scm.ShowContextMenu(this.Handle, files, Cursor.Position);
  32. /// </example>
  33. public class ShellContextMenu : NativeWindow
  34. {
  35. #region Constructor
  36. /// <summary>Default constructor</summary>
  37. public ShellContextMenu()
  38. {
  39. this.CreateHandle(new CreateParams());
  40. }
  41. #endregion
  42. #region Destructor
  43. /// <summary>Ensure all resources get released</summary>
  44. ~ShellContextMenu()
  45. {
  46. ReleaseAll();
  47. }
  48. #endregion
  49. #region GetContextMenuInterfaces()
  50. /// <summary>Gets the interfaces to the context menu</summary>
  51. /// <param name="oParentFolder">Parent folder</param>
  52. /// <param name="arrPIDLs">PIDLs</param>
  53. /// <returns>true if it got the interfaces, otherwise false</returns>
  54. private bool GetContextMenuInterfaces(IShellFolder oParentFolder, IntPtr[] arrPIDLs, out IntPtr ctxMenuPtr)
  55. {
  56. int nResult = oParentFolder.GetUIObjectOf(
  57. IntPtr.Zero,
  58. (uint)arrPIDLs.Length,
  59. arrPIDLs,
  60. ref IID_IContextMenu,
  61. IntPtr.Zero,
  62. out ctxMenuPtr);
  63. if (S_OK == nResult)
  64. {
  65. _oContextMenu = (IContextMenu)Marshal.GetTypedObjectForIUnknown(ctxMenuPtr, typeof(IContextMenu));
  66. return true;
  67. }
  68. else
  69. {
  70. ctxMenuPtr = IntPtr.Zero;
  71. _oContextMenu = null;
  72. return false;
  73. }
  74. }
  75. #endregion
  76. #region Override
  77. /// <summary>
  78. /// This method receives WindowMessages. It will make the "Open With" and "Send To" work
  79. /// by calling HandleMenuMsg and HandleMenuMsg2. It will also call the OnContextMenuMouseHover
  80. /// method of Browser when hovering over a ContextMenu item.
  81. /// </summary>
  82. /// <param name="m">the Message of the Browser's WndProc</param>
  83. /// <returns>true if the message has been handled, false otherwise</returns>
  84. protected override void WndProc(ref Message m)
  85. {
  86. #region IContextMenu
  87. if (_oContextMenu != null &&
  88. m.Msg == (int)WM.MENUSELECT &&
  89. ((int)ShellHelper.HiWord(m.WParam) & (int)MFT.SEPARATOR) == 0 &&
  90. ((int)ShellHelper.HiWord(m.WParam) & (int)MFT.POPUP) == 0)
  91. {
  92. string info = string.Empty;
  93. if (ShellHelper.LoWord(m.WParam) == (int)CMD_CUSTOM.ExpandCollapse)
  94. info = "Expands or collapses the current selected item";
  95. else
  96. {
  97. info = "";
  98. }
  99. }
  100. #endregion
  101. #region IContextMenu2
  102. if (_oContextMenu2 != null &&
  103. (m.Msg == (int)WM.INITMENUPOPUP ||
  104. m.Msg == (int)WM.MEASUREITEM ||
  105. m.Msg == (int)WM.DRAWITEM))
  106. {
  107. if (_oContextMenu2.HandleMenuMsg(
  108. (uint)m.Msg, m.WParam, m.LParam) == S_OK)
  109. return;
  110. }
  111. #endregion
  112. #region IContextMenu3
  113. if (_oContextMenu3 != null &&
  114. m.Msg == (int)WM.MENUCHAR)
  115. {
  116. if (_oContextMenu3.HandleMenuMsg2(
  117. (uint)m.Msg, m.WParam, m.LParam, IntPtr.Zero) == S_OK)
  118. return;
  119. }
  120. #endregion
  121. base.WndProc(ref m);
  122. }
  123. #endregion
  124. #region InvokeCommand
  125. private void InvokeCommand(IContextMenu oContextMenu, uint nCmd, string strFolder, Point pointInvoke)
  126. {
  127. CMINVOKECOMMANDINFOEX invoke = new CMINVOKECOMMANDINFOEX();
  128. invoke.cbSize = cbInvokeCommand;
  129. invoke.lpVerb = (IntPtr)(nCmd - CMD_FIRST);
  130. invoke.lpDirectory = strFolder;
  131. invoke.lpVerbW = (IntPtr)(nCmd - CMD_FIRST);
  132. invoke.lpDirectoryW = strFolder;
  133. invoke.fMask = CMIC.UNICODE | CMIC.PTINVOKE |
  134. ((System.Windows.Forms.Control.ModifierKeys & Keys.Control) != 0 ? CMIC.CONTROL_DOWN : 0) |
  135. ((System.Windows.Forms.Control.ModifierKeys & Keys.Shift) != 0 ? CMIC.SHIFT_DOWN : 0);
  136. invoke.ptInvoke = new POINT(pointInvoke.X, pointInvoke.Y);
  137. invoke.nShow = SW.SHOWNORMAL;
  138. oContextMenu.InvokeCommand(ref invoke);
  139. }
  140. #endregion
  141. #region ReleaseAll()
  142. /// <summary>
  143. /// Release all allocated interfaces, PIDLs
  144. /// </summary>
  145. private void ReleaseAll()
  146. {
  147. if (null != _oContextMenu)
  148. {
  149. Marshal.ReleaseComObject(_oContextMenu);
  150. _oContextMenu = null;
  151. }
  152. if (null != _oContextMenu2)
  153. {
  154. Marshal.ReleaseComObject(_oContextMenu2);
  155. _oContextMenu2 = null;
  156. }
  157. if (null != _oContextMenu3)
  158. {
  159. Marshal.ReleaseComObject(_oContextMenu3);
  160. _oContextMenu3 = null;
  161. }
  162. if (null != _oDesktopFolder)
  163. {
  164. Marshal.ReleaseComObject(_oDesktopFolder);
  165. _oDesktopFolder = null;
  166. }
  167. if (null != _oParentFolder)
  168. {
  169. Marshal.ReleaseComObject(_oParentFolder);
  170. _oParentFolder = null;
  171. }
  172. if (null != _arrPIDLs)
  173. {
  174. FreePIDLs(_arrPIDLs);
  175. _arrPIDLs = null;
  176. }
  177. }
  178. #endregion
  179. #region GetDesktopFolder()
  180. /// <summary>
  181. /// Gets the desktop folder
  182. /// </summary>
  183. /// <returns>IShellFolder for desktop folder</returns>
  184. private IShellFolder GetDesktopFolder()
  185. {
  186. IntPtr pUnkownDesktopFolder = IntPtr.Zero;
  187. if (null == _oDesktopFolder)
  188. {
  189. // Get desktop IShellFolder
  190. int nResult = SHGetDesktopFolder(out pUnkownDesktopFolder);
  191. if (S_OK != nResult)
  192. {
  193. throw new ShellContextMenuException("Failed to get the desktop shell folder");
  194. }
  195. _oDesktopFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnkownDesktopFolder, typeof(IShellFolder));
  196. }
  197. return _oDesktopFolder;
  198. }
  199. #endregion
  200. #region GetParentFolder()
  201. /// <summary>
  202. /// Gets the parent folder
  203. /// </summary>
  204. /// <param name="folderName">Folder path</param>
  205. /// <returns>IShellFolder for the folder (relative from the desktop)</returns>
  206. private IShellFolder GetParentFolder(string folderName)
  207. {
  208. if (null == _oParentFolder)
  209. {
  210. IShellFolder oDesktopFolder = GetDesktopFolder();
  211. if (null == oDesktopFolder)
  212. {
  213. return null;
  214. }
  215. // Get the PIDL for the folder file is in
  216. IntPtr pPIDL = IntPtr.Zero;
  217. uint pchEaten = 0;
  218. SFGAO pdwAttributes = 0;
  219. int nResult = oDesktopFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, folderName, ref pchEaten, out pPIDL, ref pdwAttributes);
  220. if (S_OK != nResult)
  221. {
  222. return null;
  223. }
  224. IntPtr pStrRet = Marshal.AllocCoTaskMem(MAX_PATH * 2 + 4);
  225. Marshal.WriteInt32(pStrRet, 0, 0);
  226. nResult = _oDesktopFolder.GetDisplayNameOf(pPIDL, SHGNO.FORPARSING, pStrRet);
  227. StringBuilder strFolder = new StringBuilder(MAX_PATH);
  228. StrRetToBuf(pStrRet, pPIDL, strFolder, MAX_PATH);
  229. Marshal.FreeCoTaskMem(pStrRet);
  230. pStrRet = IntPtr.Zero;
  231. _strParentFolder = strFolder.ToString();
  232. // Get the IShellFolder for folder
  233. IntPtr pUnknownParentFolder = IntPtr.Zero;
  234. nResult = oDesktopFolder.BindToObject(pPIDL, IntPtr.Zero, ref IID_IShellFolder, out pUnknownParentFolder);
  235. // Free the PIDL first
  236. Marshal.FreeCoTaskMem(pPIDL);
  237. if (S_OK != nResult)
  238. {
  239. return null;
  240. }
  241. _oParentFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(pUnknownParentFolder, typeof(IShellFolder));
  242. }
  243. return _oParentFolder;
  244. }
  245. #endregion
  246. #region GetPIDLs()
  247. /// <summary>
  248. /// Get the PIDLs
  249. /// </summary>
  250. /// <param name="arrFI">Array of FileInfo</param>
  251. /// <returns>Array of PIDLs</returns>
  252. protected IntPtr[] GetPIDLs(FileInfo[] arrFI)
  253. {
  254. if (null == arrFI || 0 == arrFI.Length)
  255. {
  256. return null;
  257. }
  258. IShellFolder oParentFolder = GetParentFolder(arrFI[0].DirectoryName);
  259. if (null == oParentFolder)
  260. {
  261. return null;
  262. }
  263. IntPtr[] arrPIDLs = new IntPtr[arrFI.Length];
  264. int n = 0;
  265. foreach (FileInfo fi in arrFI)
  266. {
  267. // Get the file relative to folder
  268. uint pchEaten = 0;
  269. SFGAO pdwAttributes = 0;
  270. IntPtr pPIDL = IntPtr.Zero;
  271. int nResult = oParentFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, fi.Name, ref pchEaten, out pPIDL, ref pdwAttributes);
  272. if (S_OK != nResult)
  273. {
  274. FreePIDLs(arrPIDLs);
  275. return null;
  276. }
  277. arrPIDLs[n] = pPIDL;
  278. n++;
  279. }
  280. return arrPIDLs;
  281. }
  282. /// <summary>
  283. /// Get the PIDLs
  284. /// </summary>
  285. /// <param name="arrFI">Array of DirectoryInfo</param>
  286. /// <returns>Array of PIDLs</returns>
  287. protected IntPtr[] GetPIDLs(DirectoryInfo[] arrFI)
  288. {
  289. if (null == arrFI || 0 == arrFI.Length)
  290. {
  291. return null;
  292. }
  293. IShellFolder oParentFolder = GetParentFolder(arrFI[0].Parent.FullName);
  294. if (null == oParentFolder)
  295. {
  296. return null;
  297. }
  298. IntPtr[] arrPIDLs = new IntPtr[arrFI.Length];
  299. int n = 0;
  300. foreach (DirectoryInfo fi in arrFI)
  301. {
  302. // Get the file relative to folder
  303. uint pchEaten = 0;
  304. SFGAO pdwAttributes = 0;
  305. IntPtr pPIDL = IntPtr.Zero;
  306. int nResult = oParentFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, fi.Name, ref pchEaten, out pPIDL, ref pdwAttributes);
  307. if (S_OK != nResult)
  308. {
  309. FreePIDLs(arrPIDLs);
  310. return null;
  311. }
  312. arrPIDLs[n] = pPIDL;
  313. n++;
  314. }
  315. return arrPIDLs;
  316. }
  317. #endregion
  318. #region FreePIDLs()
  319. /// <summary>
  320. /// Free the PIDLs
  321. /// </summary>
  322. /// <param name="arrPIDLs">Array of PIDLs (IntPtr)</param>
  323. protected void FreePIDLs(IntPtr[] arrPIDLs)
  324. {
  325. if (null != arrPIDLs)
  326. {
  327. for (int n = 0; n < arrPIDLs.Length; n++)
  328. {
  329. if (arrPIDLs[n] != IntPtr.Zero)
  330. {
  331. Marshal.FreeCoTaskMem(arrPIDLs[n]);
  332. arrPIDLs[n] = IntPtr.Zero;
  333. }
  334. }
  335. }
  336. }
  337. #endregion
  338. #region InvokeContextMenuDefault
  339. private void InvokeContextMenuDefault(FileInfo[] arrFI)
  340. {
  341. // Release all resources first.
  342. ReleaseAll();
  343. IntPtr pMenu = IntPtr.Zero,
  344. iContextMenuPtr = IntPtr.Zero;
  345. try
  346. {
  347. _arrPIDLs = GetPIDLs(arrFI);
  348. if (null == _arrPIDLs)
  349. {
  350. ReleaseAll();
  351. return;
  352. }
  353. if (false == GetContextMenuInterfaces(_oParentFolder, _arrPIDLs, out iContextMenuPtr))
  354. {
  355. ReleaseAll();
  356. return;
  357. }
  358. pMenu = CreatePopupMenu();
  359. int nResult = _oContextMenu.QueryContextMenu(
  360. pMenu,
  361. 0,
  362. CMD_FIRST,
  363. CMD_LAST,
  364. CMF.DEFAULTONLY |
  365. ((System.Windows.Forms.Control.ModifierKeys & Keys.Shift) != 0 ? CMF.EXTENDEDVERBS : 0));
  366. uint nDefaultCmd = (uint)GetMenuDefaultItem(pMenu, false, 0);
  367. if (nDefaultCmd >= CMD_FIRST)
  368. {
  369. InvokeCommand(_oContextMenu, nDefaultCmd, arrFI[0].DirectoryName, System.Windows.Forms.Control.MousePosition);
  370. }
  371. DestroyMenu(pMenu);
  372. pMenu = IntPtr.Zero;
  373. }
  374. catch
  375. {
  376. throw;
  377. }
  378. finally
  379. {
  380. if (pMenu != IntPtr.Zero)
  381. {
  382. DestroyMenu(pMenu);
  383. }
  384. ReleaseAll();
  385. }
  386. }
  387. #endregion
  388. #region ShowContextMenu()
  389. /// <summary>
  390. /// Shows the context menu
  391. /// </summary>
  392. /// <param name="files">FileInfos (should all be in same directory)</param>
  393. /// <param name="pointScreen">Where to show the menu</param>
  394. public void ShowContextMenu(FileInfo[] files, Point pointScreen)
  395. {
  396. // Release all resources first.
  397. ReleaseAll();
  398. _arrPIDLs = GetPIDLs(files);
  399. this.ShowContextMenu(pointScreen);
  400. }
  401. /// <summary>
  402. /// Shows the context menu
  403. /// </summary>
  404. /// <param name="dirs">DirectoryInfos (should all be in same directory)</param>
  405. /// <param name="pointScreen">Where to show the menu</param>
  406. public void ShowContextMenu(DirectoryInfo[] dirs, Point pointScreen)
  407. {
  408. // Release all resources first.
  409. ReleaseAll();
  410. _arrPIDLs = GetPIDLs(dirs);
  411. this.ShowContextMenu(pointScreen);
  412. }
  413. /// <summary>
  414. /// Shows the context menu
  415. /// </summary>
  416. /// <param name="arrFI">FileInfos (should all be in same directory)</param>
  417. /// <param name="pointScreen">Where to show the menu</param>
  418. private void ShowContextMenu(Point pointScreen)
  419. {
  420. IntPtr pMenu = IntPtr.Zero,
  421. iContextMenuPtr = IntPtr.Zero,
  422. iContextMenuPtr2 = IntPtr.Zero,
  423. iContextMenuPtr3 = IntPtr.Zero;
  424. try
  425. {
  426. if (null == _arrPIDLs)
  427. {
  428. ReleaseAll();
  429. return;
  430. }
  431. if (false == GetContextMenuInterfaces(_oParentFolder, _arrPIDLs, out iContextMenuPtr))
  432. {
  433. ReleaseAll();
  434. return;
  435. }
  436. pMenu = CreatePopupMenu();
  437. int nResult = _oContextMenu.QueryContextMenu(
  438. pMenu,
  439. 0,
  440. CMD_FIRST,
  441. CMD_LAST,
  442. CMF.EXPLORE |
  443. CMF.NORMAL |
  444. ((System.Windows.Forms.Control.ModifierKeys & Keys.Shift) != 0 ? CMF.EXTENDEDVERBS : 0));
  445. Marshal.QueryInterface(iContextMenuPtr, ref IID_IContextMenu2, out iContextMenuPtr2);
  446. Marshal.QueryInterface(iContextMenuPtr, ref IID_IContextMenu3, out iContextMenuPtr3);
  447. _oContextMenu2 = (IContextMenu2)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr2, typeof(IContextMenu2));
  448. _oContextMenu3 = (IContextMenu3)Marshal.GetTypedObjectForIUnknown(iContextMenuPtr3, typeof(IContextMenu3));
  449. uint nSelected = TrackPopupMenuEx(
  450. pMenu,
  451. TPM.RETURNCMD,
  452. pointScreen.X,
  453. pointScreen.Y,
  454. this.Handle,
  455. IntPtr.Zero);
  456. DestroyMenu(pMenu);
  457. pMenu = IntPtr.Zero;
  458. if (nSelected != 0)
  459. {
  460. InvokeCommand(_oContextMenu, nSelected, _strParentFolder, pointScreen);
  461. }
  462. }
  463. catch
  464. {
  465. throw;
  466. }
  467. finally
  468. {
  469. //hook.Uninstall();
  470. if (pMenu != IntPtr.Zero)
  471. {
  472. DestroyMenu(pMenu);
  473. }
  474. if (iContextMenuPtr != IntPtr.Zero)
  475. Marshal.Release(iContextMenuPtr);
  476. if (iContextMenuPtr2 != IntPtr.Zero)
  477. Marshal.Release(iContextMenuPtr2);
  478. if (iContextMenuPtr3 != IntPtr.Zero)
  479. Marshal.Release(iContextMenuPtr3);
  480. ReleaseAll();
  481. }
  482. }
  483. #endregion
  484. #region Local variabled
  485. private IContextMenu _oContextMenu;
  486. private IContextMenu2 _oContextMenu2;
  487. private IContextMenu3 _oContextMenu3;
  488. private IShellFolder _oDesktopFolder;
  489. private IShellFolder _oParentFolder;
  490. private IntPtr[] _arrPIDLs;
  491. private string _strParentFolder;
  492. #endregion
  493. #region Variables and Constants
  494. private const int MAX_PATH = 260;
  495. private const uint CMD_FIRST = 1;
  496. private const uint CMD_LAST = 30000;
  497. private const int S_OK = 0;
  498. private const int S_FALSE = 1;
  499. private static int cbMenuItemInfo = Marshal.SizeOf(typeof(MENUITEMINFO));
  500. private static int cbInvokeCommand = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX));
  501. #endregion
  502. #region DLL Import
  503. // Retrieves the IShellFolder interface for the desktop folder, which is the root of the Shell's namespace.
  504. [DllImport("shell32.dll")]
  505. private static extern Int32 SHGetDesktopFolder(out IntPtr ppshf);
  506. // Takes a STRRET structure returned by IShellFolder::GetDisplayNameOf, converts it to a string, and places the result in a buffer.
  507. [DllImport("shlwapi.dll", EntryPoint = "StrRetToBuf", ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
  508. private static extern Int32 StrRetToBuf(IntPtr pstr, IntPtr pidl, StringBuilder pszBuf, int cchBuf);
  509. // The TrackPopupMenuEx function displays a shortcut menu at the specified location and tracks the selection of items on the shortcut menu. The shortcut menu can appear anywhere on the screen.
  510. [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
  511. private static extern uint TrackPopupMenuEx(IntPtr hmenu, TPM flags, int x, int y, IntPtr hwnd, IntPtr lptpm);
  512. // The CreatePopupMenu function creates a drop-down menu, submenu, or shortcut menu. The menu is initially empty. You can insert or append menu items by using the InsertMenuItem function. You can also use the InsertMenu function to insert menu items and the AppendMenu function to append menu items.
  513. [DllImport("user32", SetLastError = true, CharSet = CharSet.Auto)]
  514. private static extern IntPtr CreatePopupMenu();
  515. // The DestroyMenu function destroys the specified menu and frees any memory that the menu occupies.
  516. [DllImport("user32", SetLastError = true, CharSet = CharSet.Auto)]
  517. private static extern bool DestroyMenu(IntPtr hMenu);
  518. // Determines the default menu item on the specified menu
  519. [DllImport("user32", SetLastError = true, CharSet = CharSet.Auto)]
  520. private static extern int GetMenuDefaultItem(IntPtr hMenu, bool fByPos, uint gmdiFlags);
  521. #endregion
  522. #region Shell GUIDs
  523. private static Guid IID_IShellFolder = new Guid("{000214E6-0000-0000-C000-000000000046}");
  524. private static Guid IID_IContextMenu = new Guid("{000214e4-0000-0000-c000-000000000046}");
  525. private static Guid IID_IContextMenu2 = new Guid("{000214f4-0000-0000-c000-000000000046}");
  526. private static Guid IID_IContextMenu3 = new Guid("{bcfce0a0-ec17-11d0-8d10-00a0c90f2719}");
  527. #endregion
  528. #region Structs
  529. [StructLayout(LayoutKind.Sequential)]
  530. private struct CWPSTRUCT
  531. {
  532. public IntPtr lparam;
  533. public IntPtr wparam;
  534. public int message;
  535. public IntPtr hwnd;
  536. }
  537. // Contains extended information about a shortcut menu command
  538. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  539. private struct CMINVOKECOMMANDINFOEX
  540. {
  541. public int cbSize;
  542. public CMIC fMask;
  543. public IntPtr hwnd;
  544. public IntPtr lpVerb;
  545. [MarshalAs(UnmanagedType.LPStr)]
  546. public string lpParameters;
  547. [MarshalAs(UnmanagedType.LPStr)]
  548. public string lpDirectory;
  549. public SW nShow;
  550. public int dwHotKey;
  551. public IntPtr hIcon;
  552. [MarshalAs(UnmanagedType.LPStr)]
  553. public string lpTitle;
  554. public IntPtr lpVerbW;
  555. [MarshalAs(UnmanagedType.LPWStr)]
  556. public string lpParametersW;
  557. [MarshalAs(UnmanagedType.LPWStr)]
  558. public string lpDirectoryW;
  559. [MarshalAs(UnmanagedType.LPWStr)]
  560. public string lpTitleW;
  561. public POINT ptInvoke;
  562. }
  563. // Contains information about a menu item
  564. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
  565. private struct MENUITEMINFO
  566. {
  567. public MENUITEMINFO(string text)
  568. {
  569. cbSize = cbMenuItemInfo;
  570. dwTypeData = text;
  571. cch = text.Length;
  572. fMask = 0;
  573. fType = 0;
  574. fState = 0;
  575. wID = 0;
  576. hSubMenu = IntPtr.Zero;
  577. hbmpChecked = IntPtr.Zero;
  578. hbmpUnchecked = IntPtr.Zero;
  579. dwItemData = IntPtr.Zero;
  580. hbmpItem = IntPtr.Zero;
  581. }
  582. public int cbSize;
  583. public MIIM fMask;
  584. public MFT fType;
  585. public MFS fState;
  586. public uint wID;
  587. public IntPtr hSubMenu;
  588. public IntPtr hbmpChecked;
  589. public IntPtr hbmpUnchecked;
  590. public IntPtr dwItemData;
  591. [MarshalAs(UnmanagedType.LPTStr)]
  592. public string dwTypeData;
  593. public int cch;
  594. public IntPtr hbmpItem;
  595. }
  596. // A generalized global memory handle used for data transfer operations by the
  597. // IAdviseSink, IDataObject, and IOleCache interfaces
  598. [StructLayout(LayoutKind.Sequential)]
  599. private struct STGMEDIUM
  600. {
  601. public TYMED tymed;
  602. public IntPtr hBitmap;
  603. public IntPtr hMetaFilePict;
  604. public IntPtr hEnhMetaFile;
  605. public IntPtr hGlobal;
  606. public IntPtr lpszFileName;
  607. public IntPtr pstm;
  608. public IntPtr pstg;
  609. public IntPtr pUnkForRelease;
  610. }
  611. // Defines the x- and y-coordinates of a point
  612. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
  613. private struct POINT
  614. {
  615. public POINT(int x, int y)
  616. {
  617. this.x = x;
  618. this.y = y;
  619. }
  620. public int x;
  621. public int y;
  622. }
  623. #endregion
  624. #region Enums
  625. // Defines the values used with the IShellFolder::GetDisplayNameOf and IShellFolder::SetNameOf
  626. // methods to specify the type of file or folder names used by those methods
  627. [Flags]
  628. private enum SHGNO
  629. {
  630. NORMAL = 0x0000,
  631. INFOLDER = 0x0001,
  632. FOREDITING = 0x1000,
  633. FORADDRESSBAR = 0x4000,
  634. FORPARSING = 0x8000
  635. }
  636. // The attributes that the caller is requesting, when calling IShellFolder::GetAttributesOf
  637. [Flags]
  638. private enum SFGAO : uint
  639. {
  640. BROWSABLE = 0x8000000,
  641. CANCOPY = 1,
  642. CANDELETE = 0x20,
  643. CANLINK = 4,
  644. CANMONIKER = 0x400000,
  645. CANMOVE = 2,
  646. CANRENAME = 0x10,
  647. CAPABILITYMASK = 0x177,
  648. COMPRESSED = 0x4000000,
  649. CONTENTSMASK = 0x80000000,
  650. DISPLAYATTRMASK = 0xfc000,
  651. DROPTARGET = 0x100,
  652. ENCRYPTED = 0x2000,
  653. FILESYSANCESTOR = 0x10000000,
  654. FILESYSTEM = 0x40000000,
  655. FOLDER = 0x20000000,
  656. GHOSTED = 0x8000,
  657. HASPROPSHEET = 0x40,
  658. HASSTORAGE = 0x400000,
  659. HASSUBFOLDER = 0x80000000,
  660. HIDDEN = 0x80000,
  661. ISSLOW = 0x4000,
  662. LINK = 0x10000,
  663. NEWCONTENT = 0x200000,
  664. NONENUMERATED = 0x100000,
  665. READONLY = 0x40000,
  666. REMOVABLE = 0x2000000,
  667. SHARE = 0x20000,
  668. STORAGE = 8,
  669. STORAGEANCESTOR = 0x800000,
  670. STORAGECAPMASK = 0x70c50008,
  671. STREAM = 0x400000,
  672. VALIDATE = 0x1000000
  673. }
  674. // Determines the type of items included in an enumeration.
  675. // These values are used with the IShellFolder::EnumObjects method
  676. [Flags]
  677. private enum SHCONTF
  678. {
  679. FOLDERS = 0x0020,
  680. NONFOLDERS = 0x0040,
  681. INCLUDEHIDDEN = 0x0080,
  682. INIT_ON_FIRST_NEXT = 0x0100,
  683. NETPRINTERSRCH = 0x0200,
  684. SHAREABLE = 0x0400,
  685. STORAGE = 0x0800,
  686. }
  687. // Specifies how the shortcut menu can be changed when calling IContextMenu::QueryContextMenu
  688. [Flags]
  689. private enum CMF : uint
  690. {
  691. NORMAL = 0x00000000,
  692. DEFAULTONLY = 0x00000001,
  693. VERBSONLY = 0x00000002,
  694. EXPLORE = 0x00000004,
  695. NOVERBS = 0x00000008,
  696. CANRENAME = 0x00000010,
  697. NODEFAULT = 0x00000020,
  698. INCLUDESTATIC = 0x00000040,
  699. EXTENDEDVERBS = 0x00000100,
  700. RESERVED = 0xffff0000
  701. }
  702. // Flags specifying the information to return when calling IContextMenu::GetCommandString
  703. [Flags]
  704. private enum GCS : uint
  705. {
  706. VERBA = 0,
  707. HELPTEXTA = 1,
  708. VALIDATEA = 2,
  709. VERBW = 4,
  710. HELPTEXTW = 5,
  711. VALIDATEW = 6
  712. }
  713. // Specifies how TrackPopupMenuEx positions the shortcut menu horizontally
  714. [Flags]
  715. private enum TPM : uint
  716. {
  717. LEFTBUTTON = 0x0000,
  718. RIGHTBUTTON = 0x0002,
  719. LEFTALIGN = 0x0000,
  720. CENTERALIGN = 0x0004,
  721. RIGHTALIGN = 0x0008,
  722. TOPALIGN = 0x0000,
  723. VCENTERALIGN = 0x0010,
  724. BOTTOMALIGN = 0x0020,
  725. HORIZONTAL = 0x0000,
  726. VERTICAL = 0x0040,
  727. NONOTIFY = 0x0080,
  728. RETURNCMD = 0x0100,
  729. RECURSE = 0x0001,
  730. HORPOSANIMATION = 0x0400,
  731. HORNEGANIMATION = 0x0800,
  732. VERPOSANIMATION = 0x1000,
  733. VERNEGANIMATION = 0x2000,
  734. NOANIMATION = 0x4000,
  735. LAYOUTRTL = 0x8000
  736. }
  737. // The cmd for a custom added menu item
  738. private enum CMD_CUSTOM
  739. {
  740. ExpandCollapse = (int)CMD_LAST + 1
  741. }
  742. // Flags used with the CMINVOKECOMMANDINFOEX structure
  743. [Flags]
  744. private enum CMIC : uint
  745. {
  746. HOTKEY = 0x00000020,
  747. ICON = 0x00000010,
  748. FLAG_NO_UI = 0x00000400,
  749. UNICODE = 0x00004000,
  750. NO_CONSOLE = 0x00008000,
  751. ASYNCOK = 0x00100000,
  752. NOZONECHECKS = 0x00800000,
  753. SHIFT_DOWN = 0x10000000,
  754. CONTROL_DOWN = 0x40000000,
  755. FLAG_LOG_USAGE = 0x04000000,
  756. PTINVOKE = 0x20000000
  757. }
  758. // Specifies how the window is to be shown
  759. [Flags]
  760. private enum SW
  761. {
  762. HIDE = 0,
  763. SHOWNORMAL = 1,
  764. NORMAL = 1,
  765. SHOWMINIMIZED = 2,
  766. SHOWMAXIMIZED = 3,
  767. MAXIMIZE = 3,
  768. SHOWNOACTIVATE = 4,
  769. SHOW = 5,
  770. MINIMIZE = 6,
  771. SHOWMINNOACTIVE = 7,
  772. SHOWNA = 8,
  773. RESTORE = 9,
  774. SHOWDEFAULT = 10,
  775. }
  776. // Window message flags
  777. [Flags]
  778. private enum WM : uint
  779. {
  780. ACTIVATE = 0x6,
  781. ACTIVATEAPP = 0x1C,
  782. AFXFIRST = 0x360,
  783. AFXLAST = 0x37F,
  784. APP = 0x8000,
  785. ASKCBFORMATNAME = 0x30C,
  786. CANCELJOURNAL = 0x4B,
  787. CANCELMODE = 0x1F,
  788. CAPTURECHANGED = 0x215,
  789. CHANGECBCHAIN = 0x30D,
  790. CHAR = 0x102,
  791. CHARTOITEM = 0x2F,
  792. CHILDACTIVATE = 0x22,
  793. CLEAR = 0x303,
  794. CLOSE = 0x10,
  795. COMMAND = 0x111,
  796. COMPACTING = 0x41,
  797. COMPAREITEM = 0x39,
  798. CONTEXTMENU = 0x7B,
  799. COPY = 0x301,
  800. COPYDATA = 0x4A,
  801. CREATE = 0x1,
  802. CTLCOLORBTN = 0x135,
  803. CTLCOLORDLG = 0x136,
  804. CTLCOLOREDIT = 0x133,
  805. CTLCOLORLISTBOX = 0x134,
  806. CTLCOLORMSGBOX = 0x132,
  807. CTLCOLORSCROLLBAR = 0x137,
  808. CTLCOLORSTATIC = 0x138,
  809. CUT = 0x300,
  810. DEADCHAR = 0x103,
  811. DELETEITEM = 0x2D,
  812. DESTROY = 0x2,
  813. DESTROYCLIPBOARD = 0x307,
  814. DEVICECHANGE = 0x219,
  815. DEVMODECHANGE = 0x1B,
  816. DISPLAYCHANGE = 0x7E,
  817. DRAWCLIPBOARD = 0x308,
  818. DRAWITEM = 0x2B,
  819. DROPFILES = 0x233,
  820. ENABLE = 0xA,
  821. ENDSESSION = 0x16,
  822. ENTERIDLE = 0x121,
  823. ENTERMENULOOP = 0x211,
  824. ENTERSIZEMOVE = 0x231,
  825. ERASEBKGND = 0x14,
  826. EXITMENULOOP = 0x212,
  827. EXITSIZEMOVE = 0x232,
  828. FONTCHANGE = 0x1D,
  829. GETDLGCODE = 0x87,
  830. GETFONT = 0x31,
  831. GETHOTKEY = 0x33,
  832. GETICON = 0x7F,
  833. GETMINMAXINFO = 0x24,
  834. GETOBJECT = 0x3D,
  835. GETSYSMENU = 0x313,
  836. GETTEXT = 0xD,
  837. GETTEXTLENGTH = 0xE,
  838. HANDHELDFIRST = 0x358,
  839. HANDHELDLAST = 0x35F,
  840. HELP = 0x53,
  841. HOTKEY = 0x312,
  842. HSCROLL = 0x114,
  843. HSCROLLCLIPBOARD = 0x30E,
  844. ICONERASEBKGND = 0x27,
  845. IME_CHAR = 0x286,
  846. IME_COMPOSITION = 0x10F,
  847. IME_COMPOSITIONFULL = 0x284,
  848. IME_CONTROL = 0x283,
  849. IME_ENDCOMPOSITION = 0x10E,
  850. IME_KEYDOWN = 0x290,
  851. IME_KEYLAST = 0x10F,
  852. IME_KEYUP = 0x291,
  853. IME_NOTIFY = 0x282,
  854. IME_REQUEST = 0x288,
  855. IME_SELECT = 0x285,
  856. IME_SETCONTEXT = 0x281,
  857. IME_STARTCOMPOSITION = 0x10D,
  858. INITDIALOG = 0x110,
  859. INITMENU = 0x116,
  860. INITMENUPOPUP = 0x117,
  861. INPUTLANGCHANGE = 0x51,
  862. INPUTLANGCHANGEREQUEST = 0x50,
  863. KEYDOWN = 0x100,
  864. KEYFIRST = 0x100,
  865. KEYLAST = 0x108,
  866. KEYUP = 0x101,
  867. KILLFOCUS = 0x8,
  868. LBUTTONDBLCLK = 0x203,
  869. LBUTTONDOWN = 0x201,
  870. LBUTTONUP = 0x202,
  871. LVM_GETEDITCONTROL = 0x1018,
  872. LVM_SETIMAGELIST = 0x1003,
  873. MBUTTONDBLCLK = 0x209,
  874. MBUTTONDOWN = 0x207,
  875. MBUTTONUP = 0x208,
  876. MDIACTIVATE = 0x222,
  877. MDICASCADE = 0x227,
  878. MDICREATE = 0x220,
  879. MDIDESTROY = 0x221,
  880. MDIGETACTIVE = 0x229,
  881. MDIICONARRANGE = 0x228,
  882. MDIMAXIMIZE = 0x225,
  883. MDINEXT = 0x224,
  884. MDIREFRESHMENU = 0x234,
  885. MDIRESTORE = 0x223,
  886. MDISETMENU = 0x230,
  887. MDITILE = 0x226,
  888. MEASUREITEM = 0x2C,
  889. MENUCHAR = 0x120,
  890. MENUCOMMAND = 0x126,
  891. MENUDRAG = 0x123,
  892. MENUGETOBJECT = 0x124,
  893. MENURBUTTONUP = 0x122,
  894. MENUSELECT = 0x11F,
  895. MOUSEACTIVATE = 0x21,
  896. MOUSEFIRST = 0x200,
  897. MOUSEHOVER = 0x2A1,
  898. MOUSELAST = 0x20A,
  899. MOUSELEAVE = 0x2A3,
  900. MOUSEMOVE = 0x200,
  901. MOUSEWHEEL = 0x20A,
  902. MOVE = 0x3,
  903. MOVING = 0x216,
  904. NCACTIVATE = 0x86,
  905. NCCALCSIZE = 0x83,
  906. NCCREATE = 0x81,
  907. NCDESTROY = 0x82,
  908. NCHITTEST = 0x84,
  909. NCLBUTTONDBLCLK = 0xA3,
  910. NCLBUTTONDOWN = 0xA1,
  911. NCLBUTTONUP = 0xA2,
  912. NCMBUTTONDBLCLK = 0xA9,
  913. NCMBUTTONDOWN = 0xA7,
  914. NCMBUTTONUP = 0xA8,
  915. NCMOUSEHOVER = 0x2A0,
  916. NCMOUSELEAVE = 0x2A2,
  917. NCMOUSEMOVE = 0xA0,
  918. NCPAINT = 0x85,
  919. NCRBUTTONDBLCLK = 0xA6,
  920. NCRBUTTONDOWN = 0xA4,
  921. NCRBUTTONUP = 0xA5,
  922. NEXTDLGCTL = 0x28,
  923. NEXTMENU = 0x213,
  924. NOTIFY = 0x4E,
  925. NOTIFYFORMAT = 0x55,
  926. NULL = 0x0,
  927. PAINT = 0xF,
  928. PAINTCLIPBOARD = 0x309,
  929. PAINTICON = 0x26,
  930. PALETTECHANGED = 0x311,
  931. PALETTEISCHANGING = 0x310,
  932. PARENTNOTIFY = 0x210,
  933. PASTE = 0x302,
  934. PENWINFIRST = 0x380,
  935. PENWINLAST = 0x38F,
  936. POWER = 0x48,
  937. PRINT = 0x317,
  938. PRINTCLIENT = 0x318,
  939. QUERYDRAGICON = 0x37,
  940. QUERYENDSESSION = 0x11,
  941. QUERYNEWPALETTE = 0x30F,
  942. QUERYOPEN = 0x13,
  943. QUEUESYNC = 0x23,
  944. QUIT = 0x12,
  945. RBUTTONDBLCLK = 0x206,
  946. RBUTTONDOWN = 0x204,
  947. RBUTTONUP = 0x205,
  948. RENDERALLFORMATS = 0x306,
  949. RENDERFORMAT = 0x305,
  950. SETCURSOR = 0x20,
  951. SETFOCUS = 0x7,
  952. SETFONT = 0x30,
  953. SETHOTKEY = 0x32,
  954. SETICON = 0x80,
  955. SETMARGINS = 0xD3,
  956. SETREDRAW = 0xB,
  957. SETTEXT = 0xC,
  958. SETTINGCHANGE = 0x1A,
  959. SHOWWINDOW = 0x18,
  960. SIZE = 0x5,
  961. SIZECLIPBOARD = 0x30B,
  962. SIZING = 0x214,
  963. SPOOLERSTATUS = 0x2A,
  964. STYLECHANGED = 0x7D,
  965. STYLECHANGING = 0x7C,
  966. SYNCPAINT = 0x88,
  967. SYSCHAR = 0x106,
  968. SYSCOLORCHANGE = 0x15,
  969. SYSCOMMAND = 0x112,
  970. SYSDEADCHAR = 0x107,
  971. SYSKEYDOWN = 0x104,
  972. SYSKEYUP = 0x105,
  973. TCARD = 0x52,
  974. TIMECHANGE = 0x1E,
  975. TIMER = 0x113,
  976. TVM_GETEDITCONTROL = 0x110F,
  977. TVM_SETIMAGELIST = 0x1109,
  978. UNDO = 0x304,
  979. UNINITMENUPOPUP = 0x125,
  980. USER = 0x400,
  981. USERCHANGED = 0x54,
  982. VKEYTOITEM = 0x2E,
  983. VSCROLL = 0x115,
  984. VSCROLLCLIPBOARD = 0x30A,
  985. WINDOWPOSCHANGED = 0x47,
  986. WINDOWPOSCHANGING = 0x46,
  987. WININICHANGE = 0x1A,
  988. SH_NOTIFY = 0x0401
  989. }
  990. // Specifies the content of the new menu item
  991. [Flags]
  992. private enum MFT : uint
  993. {
  994. GRAYED = 0x00000003,
  995. DISABLED = 0x00000003,
  996. CHECKED = 0x00000008,
  997. SEPARATOR = 0x00000800,
  998. RADIOCHECK = 0x00000200,
  999. BITMAP = 0x00000004,
  1000. OWNERDRAW = 0x00000100,
  1001. MENUBARBREAK = 0x00000020,
  1002. MENUBREAK = 0x00000040,
  1003. RIGHTORDER = 0x00002000,
  1004. BYCOMMAND = 0x00000000,
  1005. BYPOSITION = 0x00000400,
  1006. POPUP = 0x00000010
  1007. }
  1008. // Specifies the state of the new menu item
  1009. [Flags]
  1010. private enum MFS : uint
  1011. {
  1012. GRAYED = 0x00000003,
  1013. DISABLED = 0x00000003,
  1014. CHECKED = 0x00000008,
  1015. HILITE = 0x00000080,
  1016. ENABLED = 0x00000000,
  1017. UNCHECKED = 0x00000000,
  1018. UNHILITE = 0x00000000,
  1019. DEFAULT = 0x00001000
  1020. }
  1021. // Specifies the content of the new menu item
  1022. [Flags]
  1023. private enum MIIM : uint
  1024. {
  1025. BITMAP = 0x80,
  1026. CHECKMARKS = 0x08,
  1027. DATA = 0x20,
  1028. FTYPE = 0x100,
  1029. ID = 0x02,
  1030. STATE = 0x01,
  1031. STRING = 0x40,
  1032. SUBMENU = 0x04,
  1033. TYPE = 0x10
  1034. }
  1035. // Indicates the type of storage medium being used in a data transfer
  1036. [Flags]
  1037. private enum TYMED
  1038. {
  1039. ENHMF = 0x40,
  1040. FILE = 2,
  1041. GDI = 0x10,
  1042. HGLOBAL = 1,
  1043. ISTORAGE = 8,
  1044. ISTREAM = 4,
  1045. MFPICT = 0x20,
  1046. NULL = 0
  1047. }
  1048. #endregion
  1049. #region IShellFolder
  1050. [ComImport]
  1051. [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  1052. [Guid("000214E6-0000-0000-C000-000000000046")]
  1053. private interface IShellFolder
  1054. {
  1055. // Translates a file object's or folder's display name into an item identifier list.
  1056. // Return value: error code, if any
  1057. [PreserveSig]
  1058. Int32 ParseDisplayName(
  1059. IntPtr hwnd,
  1060. IntPtr pbc,
  1061. [MarshalAs(UnmanagedType.LPWStr)]
  1062. string pszDisplayName,
  1063. ref uint pchEaten,
  1064. out IntPtr ppidl,
  1065. ref SFGAO pdwAttributes);
  1066. // Allows a client to determine the contents of a folder by creating an item
  1067. // identifier enumeration object and returning its IEnumIDList interface.
  1068. // Return value: error code, if any
  1069. [PreserveSig]
  1070. Int32 EnumObjects(
  1071. IntPtr hwnd,
  1072. SHCONTF grfFlags,
  1073. out IntPtr enumIDList);
  1074. // Retrieves an IShellFolder object for a subfolder.
  1075. // Return value: error code, if any
  1076. [PreserveSig]
  1077. Int32 BindToObject(
  1078. IntPtr pidl,
  1079. IntPtr pbc,
  1080. ref Guid riid,
  1081. out IntPtr ppv);
  1082. // Requests a pointer to an object's storage interface.
  1083. // Return value: error code, if any
  1084. [PreserveSig]
  1085. Int32 BindToStorage(
  1086. IntPtr pidl,
  1087. IntPtr pbc,
  1088. ref Guid riid,
  1089. out IntPtr ppv);
  1090. // Determines the relative order of two file objects or folders, given their
  1091. // item identifier lists. Return value: If this method is successful, the
  1092. // CODE field of the HRESULT contains one of the following values (the code
  1093. // can be retrived using the helper function GetHResultCode): Negative A
  1094. // negative return value indicates that the first item should precede
  1095. // the second (pidl1 < pidl2).
  1096. // Positive A positive return value indicates that the first item should
  1097. // follow the second (pidl1 > pidl2). Zero A return value of zero
  1098. // indicates that the two items are the same (pidl1 = pidl2).
  1099. [PreserveSig]
  1100. Int32 CompareIDs(
  1101. IntPtr lParam,
  1102. IntPtr pidl1,
  1103. IntPtr pidl2);
  1104. // Requests an object that can be used to obtain information from or interact
  1105. // with a folder object.
  1106. // Return value: error code, if any
  1107. [PreserveSig]
  1108. Int32 CreateViewObject(
  1109. IntPtr hwndOwner,
  1110. Guid riid,
  1111. out IntPtr ppv);
  1112. // Retrieves the attributes of one or more file objects or subfolders.
  1113. // Return value: error code, if any
  1114. [PreserveSig]
  1115. Int32 GetAttributesOf(
  1116. uint cidl,
  1117. [MarshalAs(UnmanagedType.LPArray)]
  1118. IntPtr[] apidl,
  1119. ref SFGAO rgfInOut);
  1120. // Retrieves an OLE interface that can be used to carry out actions on the
  1121. // specified file objects or folders.
  1122. // Return value: error code, if any
  1123. [PreserveSig]
  1124. Int32 GetUIObjectOf(
  1125. IntPtr hwndOwner,
  1126. uint cidl,
  1127. [MarshalAs(UnmanagedType.LPArray)]
  1128. IntPtr[] apidl,
  1129. ref Guid riid,
  1130. IntPtr rgfReserved,
  1131. out IntPtr ppv);
  1132. // Retrieves the display name for the specified file object or subfolder.
  1133. // Return value: error code, if any
  1134. [PreserveSig()]
  1135. Int32 GetDisplayNameOf(
  1136. IntPtr pidl,
  1137. SHGNO uFlags,
  1138. IntPtr lpName);
  1139. // Sets the display name of a file object or subfolder, changing the item
  1140. // identifier in the process.
  1141. // Return value: error code, if any
  1142. [PreserveSig]
  1143. Int32 SetNameOf(
  1144. IntPtr hwnd,
  1145. IntPtr pidl,
  1146. [MarshalAs(UnmanagedType.LPWStr)]
  1147. string pszName,
  1148. SHGNO uFlags,
  1149. out IntPtr ppidlOut);
  1150. }
  1151. #endregion
  1152. #region IContextMenu
  1153. [ComImport()]
  1154. [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  1155. [GuidAttribute("000214e4-0000-0000-c000-000000000046")]
  1156. private interface IContextMenu
  1157. {
  1158. // Adds commands to a shortcut menu
  1159. [PreserveSig()]
  1160. Int32 QueryContextMenu(
  1161. IntPtr hmenu,
  1162. uint iMenu,
  1163. uint idCmdFirst,
  1164. uint idCmdLast,
  1165. CMF uFlags);
  1166. // Carries out the command associated with a shortcut menu item
  1167. [PreserveSig()]
  1168. Int32 InvokeCommand(
  1169. ref CMINVOKECOMMANDINFOEX info);
  1170. // Retrieves information about a shortcut menu command,
  1171. // including the help string and the language-independent,
  1172. // or canonical, name for the command
  1173. [PreserveSig()]
  1174. Int32 GetCommandString(
  1175. uint idcmd,
  1176. GCS uflags,
  1177. uint reserved,
  1178. [MarshalAs(UnmanagedType.LPArray)]
  1179. byte[] commandstring,
  1180. int cch);
  1181. }
  1182. [ComImport, Guid("000214f4-0000-0000-c000-000000000046")]
  1183. [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  1184. private interface IContextMenu2
  1185. {
  1186. // Adds commands to a shortcut menu
  1187. [PreserveSig()]
  1188. Int32 QueryContextMenu(
  1189. IntPtr hmenu,
  1190. uint iMenu,
  1191. uint idCmdFirst,
  1192. uint idCmdLast,
  1193. CMF uFlags);
  1194. // Carries out the command associated with a shortcut menu item
  1195. [PreserveSig()]
  1196. Int32 InvokeCommand(
  1197. ref CMINVOKECOMMANDINFOEX info);
  1198. // Retrieves information about a shortcut menu command,
  1199. // including the help string and the language-independent,
  1200. // or canonical, name for the command
  1201. [PreserveSig()]
  1202. Int32 GetCommandString(
  1203. uint idcmd,
  1204. GCS uflags,
  1205. uint reserved,
  1206. [MarshalAs(UnmanagedType.LPWStr)]
  1207. StringBuilder commandstring,
  1208. int cch);
  1209. // Allows client objects of the IContextMenu interface to
  1210. // handle messages associated with owner-drawn menu items
  1211. [PreserveSig]
  1212. Int32 HandleMenuMsg(
  1213. uint uMsg,
  1214. IntPtr wParam,
  1215. IntPtr lParam);
  1216. }
  1217. [ComImport, Guid("bcfce0a0-ec17-11d0-8d10-00a0c90f2719")]
  1218. [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  1219. private interface IContextMenu3
  1220. {
  1221. // Adds commands to a shortcut menu
  1222. [PreserveSig()]
  1223. Int32 QueryContextMenu(
  1224. IntPtr hmenu,
  1225. uint iMenu,
  1226. uint idCmdFirst,
  1227. uint idCmdLast,
  1228. CMF uFlags);
  1229. // Carries out the command associated with a shortcut menu item
  1230. [PreserveSig()]
  1231. Int32 InvokeCommand(
  1232. ref CMINVOKECOMMANDINFOEX info);
  1233. // Retrieves information about a shortcut menu command,
  1234. // including the help string and the language-independent,
  1235. // or canonical, name for the command
  1236. [PreserveSig()]
  1237. Int32 GetCommandString(
  1238. uint idcmd,
  1239. GCS uflags,
  1240. uint reserved,
  1241. [MarshalAs(UnmanagedType.LPWStr)]
  1242. StringBuilder commandstring,
  1243. int cch);
  1244. // Allows client objects of the IContextMenu interface to
  1245. // handle messages associated with owner-drawn menu items
  1246. [PreserveSig]
  1247. Int32 HandleMenuMsg(
  1248. uint uMsg,
  1249. IntPtr wParam,
  1250. IntPtr lParam);
  1251. // Allows client objects of the IContextMenu3 interface to
  1252. // handle messages associated with owner-drawn menu items
  1253. [PreserveSig]
  1254. Int32 HandleMenuMsg2(
  1255. uint uMsg,
  1256. IntPtr wParam,
  1257. IntPtr lParam,
  1258. IntPtr plResult);
  1259. }
  1260. #endregion
  1261. }
  1262. #region ShellContextMenuException
  1263. public class ShellContextMenuException : Exception
  1264. {
  1265. /// <summary>Default contructor</summary>
  1266. public ShellContextMenuException()
  1267. {
  1268. }
  1269. /// <summary>Constructor with message</summary>
  1270. /// <param name="message">Message</param>
  1271. public ShellContextMenuException(string message)
  1272. : base(message)
  1273. {
  1274. }
  1275. }
  1276. #endregion
  1277. #region Class HookEventArgs
  1278. public class HookEventArgs : EventArgs
  1279. {
  1280. public int HookCode; // Hook code
  1281. public IntPtr wParam; // WPARAM argument
  1282. public IntPtr lParam; // LPARAM argument
  1283. }
  1284. #endregion
  1285. #region Enum HookType
  1286. // Hook Types
  1287. public enum HookType : int
  1288. {
  1289. WH_JOURNALRECORD = 0,
  1290. WH_JOURNALPLAYBACK = 1,
  1291. WH_KEYBOARD = 2,
  1292. WH_GETMESSAGE = 3,
  1293. WH_CALLWNDPROC = 4,
  1294. WH_CBT = 5,
  1295. WH_SYSMSGFILTER = 6,
  1296. WH_MOUSE = 7,
  1297. WH_HARDWARE = 8,
  1298. WH_DEBUG = 9,
  1299. WH_SHELL = 10,
  1300. WH_FOREGROUNDIDLE = 11,
  1301. WH_CALLWNDPROCRET = 12,
  1302. WH_KEYBOARD_LL = 13,
  1303. WH_MOUSE_LL = 14
  1304. }
  1305. #endregion
  1306. #region Class LocalWindowsHook
  1307. public class LocalWindowsHook
  1308. {
  1309. // ************************************************************************
  1310. // Filter function delegate
  1311. public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
  1312. // ************************************************************************
  1313. // ************************************************************************
  1314. // Internal properties
  1315. protected IntPtr m_hhook = IntPtr.Zero;
  1316. protected HookProc m_filterFunc = null;
  1317. protected HookType m_hookType;
  1318. // ************************************************************************
  1319. // ************************************************************************
  1320. // Event delegate
  1321. public delegate void HookEventHandler(object sender, HookEventArgs e);
  1322. // ************************************************************************
  1323. // ************************************************************************
  1324. // Event: HookInvoked
  1325. public event HookEventHandler HookInvoked;
  1326. protected void OnHookInvoked(HookEventArgs e)
  1327. {
  1328. if (HookInvoked != null)
  1329. HookInvoked(this, e);
  1330. }
  1331. // ************************************************************************
  1332. // ************************************************************************
  1333. // Class constructor(s)
  1334. public LocalWindowsHook(HookType hook)
  1335. {
  1336. m_hookType = hook;
  1337. m_filterFunc = new HookProc(this.CoreHookProc);
  1338. }
  1339. public LocalWindowsHook(HookType hook, HookProc func)
  1340. {
  1341. m_hookType = hook;
  1342. m_filterFunc = func;
  1343. }
  1344. // ************************************************************************
  1345. // ************************************************************************
  1346. // Default filter function
  1347. protected int CoreHookProc(int code, IntPtr wParam, IntPtr lParam)
  1348. {
  1349. if (code < 0)
  1350. return CallNextHookEx(m_hhook, code, wParam, lParam);
  1351. // Let clients determine what to do
  1352. HookEventArgs e = new HookEventArgs();
  1353. e.HookCode = code;
  1354. e.wParam = wParam;
  1355. e.lParam = lParam;
  1356. OnHookInvoked(e);
  1357. // Yield to the next hook in the chain
  1358. return CallNextHookEx(m_hhook, code, wParam, lParam);
  1359. }
  1360. // ************************************************************************
  1361. // ************************************************************************
  1362. // Install the hook
  1363. public void Install()
  1364. {
  1365. m_hhook = SetWindowsHookEx(
  1366. m_hookType,
  1367. m_filterFunc,
  1368. IntPtr.Zero,
  1369. #pragma warning disable CS0618 // “AppDomain.GetCurrentThreadId()”已过时:“AppDomain.GetCurrentThreadId has been deprecated because it does not provide a stable Id when managed threads are running on fibers (aka lightweight threads). To get a stable identifier for a managed thread, use the ManagedThreadId property on Thread. http://go.microsoft.com/fwlink/?linkid=14202”
  1370. (int)AppDomain.GetCurrentThreadId());
  1371. #pragma warning restore CS0618 // “AppDomain.GetCurrentThreadId()”已过时:“AppDomain.GetCurrentThreadId has been deprecated because it does not provide a stable Id when managed threads are running on fibers (aka lightweight threads). To get a stable identifier for a managed thread, use the ManagedThreadId property on Thread. http://go.microsoft.com/fwlink/?linkid=14202”
  1372. }
  1373. // ************************************************************************
  1374. // ************************************************************************
  1375. // Uninstall the hook
  1376. public void Uninstall()
  1377. {
  1378. UnhookWindowsHookEx(m_hhook);
  1379. }
  1380. // ************************************************************************
  1381. #region Win32 Imports
  1382. // ************************************************************************
  1383. // Win32: SetWindowsHookEx()
  1384. [DllImport("user32.dll")]
  1385. protected static extern IntPtr SetWindowsHookEx(HookType code,
  1386. HookProc func,
  1387. IntPtr hInstance,
  1388. int threadID);
  1389. // ************************************************************************
  1390. // ************************************************************************
  1391. // Win32: UnhookWindowsHookEx()
  1392. [DllImport("user32.dll")]
  1393. protected static extern int UnhookWindowsHookEx(IntPtr hhook);
  1394. // ************************************************************************
  1395. // ************************************************************************
  1396. // Win32: CallNextHookEx()
  1397. [DllImport("user32.dll")]
  1398. protected static extern int CallNextHookEx(IntPtr hhook,
  1399. int code, IntPtr wParam, IntPtr lParam);
  1400. // ************************************************************************
  1401. #endregion
  1402. }
  1403. #endregion
  1404. #region ShellHelper
  1405. internal static class ShellHelper
  1406. {
  1407. #region Low/High Word
  1408. /// <summary>
  1409. /// Retrieves the High Word of a WParam of a WindowMessage
  1410. /// </summary>
  1411. /// <param name="ptr">The pointer to the WParam</param>
  1412. /// <returns>The unsigned integer for the High Word</returns>
  1413. public static uint HiWord(IntPtr ptr)
  1414. {
  1415. if (((uint)ptr & 0x80000000) == 0x80000000)
  1416. return ((uint)ptr >> 16);
  1417. else
  1418. return ((uint)ptr >> 16) & 0xffff;
  1419. }
  1420. /// <summary>
  1421. /// Retrieves the Low Word of a WParam of a WindowMessage
  1422. /// </summary>
  1423. /// <param name="ptr">The pointer to the WParam</param>
  1424. /// <returns>The unsigned integer for the Low Word</returns>
  1425. public static uint LoWord(IntPtr ptr)
  1426. {
  1427. return (uint)ptr & 0xffff;
  1428. }
  1429. #endregion
  1430. }
  1431. #endregion
  1432. }