PageRenderTime 61ms CodeModel.GetById 32ms RepoModel.GetById 1ms app.codeStats 0ms

/Code/Foundation/Windows/Shell/ShellImageList.cs

https://github.com/DavidMoore/Foundation
C# | 478 lines | 310 code | 33 blank | 135 comment | 53 complexity | 08cc55fb9a1b5122b15db5620ca2dacb MD5 | raw file
  1. using System;
  2. using System.Diagnostics;
  3. using System.Diagnostics.CodeAnalysis;
  4. using System.Drawing;
  5. using System.Runtime.InteropServices;
  6. namespace Foundation.Windows.Shell
  7. {
  8. /// <summary>
  9. /// Summary description for ShellImageList.
  10. /// </summary>
  11. public class ShellImageList : IDisposable
  12. {
  13. const int FILE_ATTRIBUTE_DIRECTORY = 0x10;
  14. const int FILE_ATTRIBUTE_NORMAL = 0x80;
  15. const int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x100;
  16. const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x2000;
  17. const int FORMAT_MESSAGE_FROM_HMODULE = 0x800;
  18. const int FORMAT_MESSAGE_FROM_STRING = 0x400;
  19. const int FORMAT_MESSAGE_FROM_SYSTEM = 0x1000;
  20. const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x200;
  21. const int FORMAT_MESSAGE_MAX_WIDTH_MASK = 0xFF;
  22. internal const int MAX_PATH = 260;
  23. bool disposed;
  24. IntPtr hIml = IntPtr.Zero;
  25. IImageList iImageList;
  26. ShellImageListSize size = ShellImageListSize.SmallIcons;
  27. /// <summary>
  28. /// Creates a Small Icons SystemImageList
  29. /// </summary>
  30. public ShellImageList()
  31. {
  32. Create();
  33. }
  34. /// <summary>
  35. /// Creates a SystemImageList with the specified size
  36. /// </summary>
  37. /// <param name="size">Size of System ImageList</param>
  38. public ShellImageList(ShellImageListSize size)
  39. {
  40. this.size = size;
  41. Create();
  42. }
  43. /// <summary>
  44. /// Gets the hImageList handle
  45. /// </summary>
  46. public IntPtr Handle
  47. {
  48. get { return hIml; }
  49. }
  50. /// <summary>
  51. /// Gets/sets the size of System Image List to retrieve.
  52. /// </summary>
  53. public ShellImageListSize ImageListSize
  54. {
  55. get { return size; }
  56. set
  57. {
  58. size = value;
  59. Create();
  60. }
  61. }
  62. /// <summary>
  63. /// Returns the size of the Image List Icons.
  64. /// </summary>
  65. /// <exception cref="ShellImageListException"> if there is an error when getting the image size</exception>
  66. [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "Potentially throws exceptions so is best served as a method")]
  67. public Size GetImageSize()
  68. {
  69. int cx = 0;
  70. int cy = 0;
  71. if (iImageList == null)
  72. {
  73. var result = NativeMethods.ImageList_GetIconSize(hIml,ref cx,ref cy);
  74. if( result != 0) throw new ShellImageListException("Couldn't get icon size. Result={0}, Last Win32 Error={1}", result, Marshal.GetLastWin32Error());
  75. }
  76. else
  77. {
  78. var result = iImageList.GetIconSize(ref cx, ref cy);
  79. if (result != 0) throw new ShellImageListException("Couldn't get icon size. Result={0}");
  80. }
  81. var sz = new Size(cx, cy);
  82. return sz;
  83. }
  84. #region IDisposable Members
  85. /// <summary>
  86. /// Clears up any resources associated with the SystemImageList
  87. /// </summary>
  88. public void Dispose()
  89. {
  90. Dispose(true);
  91. GC.SuppressFinalize(this);
  92. }
  93. #endregion
  94. /// <summary>
  95. /// Returns a GDI+ copy of the icon from the ImageList
  96. /// at the specified index.
  97. /// </summary>
  98. /// <param name="index">The index to get the icon for</param>
  99. /// <returns>The specified icon</returns>
  100. public Icon Icon(int index)
  101. {
  102. Icon icon = null;
  103. IntPtr hIcon = IntPtr.Zero;
  104. if (iImageList == null)
  105. {
  106. hIcon = NativeMethods.ImageList_GetIcon( hIml, index, (int) ImageListDrawItemOptions.Transparent);
  107. }
  108. else
  109. {
  110. var result = iImageList.GetIcon( index, (int) ImageListDrawItemOptions.Transparent, ref hIcon);
  111. if( result != 0) throw new ShellImageListException("Couldn't get icon copy. Result: {0}", result);
  112. }
  113. if (hIcon != IntPtr.Zero)
  114. {
  115. icon = System.Drawing.Icon.FromHandle(hIcon);
  116. }
  117. return icon;
  118. }
  119. /// <summary>
  120. /// Return the index of the icon for the specified file, always using
  121. /// the cached version where possible.
  122. /// </summary>
  123. /// <param name="fileName">FileName to get icon for</param>
  124. /// <returns>Index of the icon</returns>
  125. public int IconIndex(string fileName)
  126. {
  127. return IconIndex(fileName, false);
  128. }
  129. /// <summary>
  130. /// Returns the index of the icon for the specified file
  131. /// </summary>
  132. /// <param name="fileName">FileName to get icon for</param>
  133. /// <param name="forceLoadFromDisk">If True, then hit the disk to get the icon,
  134. /// otherwise only hit the disk if no cached icon is available.</param>
  135. /// <returns>Index of the icon</returns>
  136. public int IconIndex(string fileName,bool forceLoadFromDisk)
  137. {
  138. return IconIndex(
  139. fileName,
  140. forceLoadFromDisk,
  141. ShellIconStateConstants.ShellIconStateNormal);
  142. }
  143. /// <summary>
  144. /// Returns the index of the icon for the specified file
  145. /// </summary>
  146. /// <param name="fileName">FileName to get icon for</param>
  147. /// <param name="forceLoadFromDisk">If True, then hit the disk to get the icon,
  148. /// otherwise only hit the disk if no cached icon is available.</param>
  149. /// <param name="iconState">Flags specifying the state of the icon
  150. /// returned.</param>
  151. /// <returns>Index of the icon</returns>
  152. public int IconIndex(string fileName,bool forceLoadFromDisk,ShellIconStateConstants iconState)
  153. {
  154. SHGetFileInfoConstants dwFlags = SHGetFileInfoConstants.SHGFI_SYSICONINDEX;
  155. int dwAttr = 0;
  156. if (size == ShellImageListSize.SmallIcons)
  157. {
  158. dwFlags |= SHGetFileInfoConstants.SHGFI_SMALLICON;
  159. }
  160. // We can choose whether to access the disk or not. If you don't
  161. // hit the disk, you may get the wrong icon if the icon is
  162. // not cached. Also only works for files.
  163. if (!forceLoadFromDisk)
  164. {
  165. dwFlags |= SHGetFileInfoConstants.SHGFI_USEFILEATTRIBUTES;
  166. dwAttr = FILE_ATTRIBUTE_NORMAL;
  167. }
  168. else
  169. {
  170. dwAttr = 0;
  171. }
  172. // sFileSpec can be any file. You can specify a
  173. // file that does not exist and still get the
  174. // icon, for example sFileSpec = "C:\PANTS.DOC"
  175. var shfi = new SHFILEINFO();
  176. var shfiSize = (uint) Marshal.SizeOf(shfi.GetType());
  177. IntPtr retVal = NativeMethods.SHGetFileInfo(
  178. fileName, dwAttr, ref shfi, shfiSize,
  179. ((uint) (dwFlags) | (uint) iconState));
  180. if (retVal.Equals(IntPtr.Zero))
  181. {
  182. //System.Diagnostics.Debug.Assert((!retVal.Equals(IntPtr.Zero)),"Failed to get icon index");
  183. return 0;
  184. }
  185. else
  186. {
  187. return shfi.iIcon;
  188. }
  189. }
  190. /// <summary>
  191. /// Draws an image
  192. /// </summary>
  193. /// <param name="hdc">Device context to draw to</param>
  194. /// <param name="index">Index of image to draw</param>
  195. /// <param name="x">X Position to draw at</param>
  196. /// <param name="y">Y Position to draw at</param>
  197. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x")]
  198. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "y")]
  199. public void DrawImage(IntPtr hdc, int index, int x, int y)
  200. {
  201. DrawImage(hdc, index, x, y, ImageListDrawItemOptions.Transparent);
  202. }
  203. /// <summary>
  204. /// Draws an image using the specified flags
  205. /// </summary>
  206. /// <param name="hdc">Device context to draw to</param>
  207. /// <param name="index">Index of image to draw</param>
  208. /// <param name="x">X Position to draw at</param>
  209. /// <param name="y">Y Position to draw at</param>
  210. /// <param name="imageListDrawItemOptions">Drawing flags</param>
  211. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x")]
  212. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "y")]
  213. public void DrawImage(IntPtr hdc,int index,int x,int y,ImageListDrawItemOptions imageListDrawItemOptions)
  214. {
  215. if (iImageList == null)
  216. {
  217. int ret = NativeMethods.ImageList_Draw(
  218. hIml,
  219. index,
  220. hdc,
  221. x,
  222. y,
  223. (int) imageListDrawItemOptions);
  224. if( ret != 0 ) throw new ShellImageListException("Error drawing image. Return code: {0}, Last Win32 Error: {1}", ret, Marshal.GetLastWin32Error());
  225. }
  226. else
  227. {
  228. var pimldp = new IMAGELISTDRAWPARAMS();
  229. pimldp.hdcDst = hdc;
  230. pimldp.cbSize = Marshal.SizeOf(pimldp.GetType());
  231. pimldp.i = index;
  232. pimldp.x = x;
  233. pimldp.y = y;
  234. pimldp.rgbFg = -1;
  235. pimldp.fStyle = (int) imageListDrawItemOptions;
  236. var result = iImageList.Draw(ref pimldp);
  237. if (result != 0) throw new ShellImageListException("Error drawing image. Return code: {0}", result);
  238. }
  239. }
  240. /// <summary>
  241. /// Draws an image using the specified flags and specifies
  242. /// the size to clip to (or to stretch to if Scale
  243. /// is provided).
  244. /// </summary>
  245. /// <param name="hdc">Device context to draw to</param>
  246. /// <param name="index">Index of image to draw</param>
  247. /// <param name="x">X Position to draw at</param>
  248. /// <param name="y">Y Position to draw at</param>
  249. /// <param name="imageListDrawItemOptions">Drawing flags</param>
  250. /// <param name="width">Width to draw</param>
  251. /// <param name="height">Height to draw</param>
  252. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x")]
  253. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "y")]
  254. public void DrawImage(
  255. IntPtr hdc,
  256. int index,
  257. int x,
  258. int y,
  259. ImageListDrawItemOptions imageListDrawItemOptions,
  260. int width,
  261. int height
  262. )
  263. {
  264. var pimldp = new IMAGELISTDRAWPARAMS();
  265. pimldp.hdcDst = hdc;
  266. pimldp.cbSize = Marshal.SizeOf(pimldp.GetType());
  267. pimldp.i = index;
  268. pimldp.x = x;
  269. pimldp.y = y;
  270. pimldp.cx = width;
  271. pimldp.cy = height;
  272. pimldp.fStyle = (int) imageListDrawItemOptions;
  273. if (iImageList == null)
  274. {
  275. pimldp.himl = hIml;
  276. int ret = NativeMethods.ImageList_DrawIndirect(ref pimldp);
  277. if (ret != 0) throw new ShellImageListException("Error drawing image. Return code: {0}, Last Win32 Error: {1}", ret, Marshal.GetLastWin32Error());
  278. }
  279. else
  280. {
  281. var result = iImageList.Draw(ref pimldp);
  282. if (result != 0) throw new ShellImageListException("Error drawing image. Return code: {0}", result);
  283. }
  284. }
  285. /// <summary>
  286. /// Draws an image using the specified flags and state on XP systems.
  287. /// </summary>
  288. /// <param name="hdc">Device context to draw to</param>
  289. /// <param name="index">Index of image to draw</param>
  290. /// <param name="x">X Position to draw at</param>
  291. /// <param name="y">Y Position to draw at</param>
  292. /// <param name="imageListDrawItemOptions">Drawing flags</param>
  293. /// <param name="width">Width to draw</param>
  294. /// <param name="height">Height to draw</param>
  295. /// <param name="foreColor">Fore colour to blend with when using the
  296. /// Selected or Blend25 flags</param>
  297. /// <param name="stateOptions">State flags</param>
  298. /// <param name="glowOrShadowColor">If stateFlags include ILS_GLOW, then
  299. /// the colour to use for the glow effect. Otherwise if stateFlags includes
  300. /// ILS_SHADOW, then the colour to use for the shadow.</param>
  301. /// <param name="saturateColorOrAlpha">If stateFlags includes Alpha,
  302. /// then the alpha component is applied to the icon. Otherwise if
  303. /// ILS_SATURATE is included, then the (R,G,B) components are used
  304. /// to saturate the image.</param>
  305. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "x")]
  306. [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "y")]
  307. public void DrawImage(IntPtr hdc,int index,int x,int y,ImageListDrawItemOptions imageListDrawItemOptions,int width,int height,
  308. Color foreColor,ImageListDrawStateOptions stateOptions,Color saturateColorOrAlpha,Color glowOrShadowColor)
  309. {
  310. var pimldp = new IMAGELISTDRAWPARAMS();
  311. pimldp.hdcDst = hdc;
  312. pimldp.cbSize = Marshal.SizeOf(pimldp.GetType());
  313. pimldp.i = index;
  314. pimldp.x = x;
  315. pimldp.y = y;
  316. pimldp.cx = width;
  317. pimldp.cy = height;
  318. pimldp.rgbFg = Color.FromArgb(0,
  319. foreColor.R, foreColor.G, foreColor.B).ToArgb();
  320. Console.WriteLine("{0}", pimldp.rgbFg);
  321. pimldp.fStyle = (int) imageListDrawItemOptions;
  322. pimldp.fState = (int) stateOptions;
  323. if ((stateOptions & ImageListDrawStateOptions.Alpha) ==
  324. ImageListDrawStateOptions.Alpha)
  325. {
  326. // Set the alpha:
  327. pimldp.Frame = saturateColorOrAlpha.A;
  328. }
  329. else if ((stateOptions & ImageListDrawStateOptions.Saturate) ==
  330. ImageListDrawStateOptions.Saturate)
  331. {
  332. // discard alpha channel:
  333. saturateColorOrAlpha = Color.FromArgb(0,
  334. saturateColorOrAlpha.R,
  335. saturateColorOrAlpha.G,
  336. saturateColorOrAlpha.B);
  337. // set the saturate color
  338. pimldp.Frame = saturateColorOrAlpha.ToArgb();
  339. }
  340. glowOrShadowColor = Color.FromArgb(0,
  341. glowOrShadowColor.R,
  342. glowOrShadowColor.G,
  343. glowOrShadowColor.B);
  344. pimldp.crEffect = glowOrShadowColor.ToArgb();
  345. if (iImageList == null)
  346. {
  347. pimldp.himl = hIml;
  348. int ret = NativeMethods.ImageList_DrawIndirect(ref pimldp);
  349. if (ret != 0) throw new ShellImageListException("Error drawing image. Return code: {0}, Last Win32 Error: {1}", ret, Marshal.GetLastWin32Error());
  350. }
  351. else
  352. {
  353. var result = iImageList.Draw(ref pimldp);
  354. if (result != 0) throw new ShellImageListException("Error drawing image. Result={0}", result);
  355. }
  356. }
  357. /// <summary>
  358. /// Determines if the system is running Windows XP
  359. /// or above
  360. /// </summary>
  361. /// <returns>True if system is running XP or above, False otherwise</returns>
  362. static bool IsXpOrAbove()
  363. {
  364. bool ret = false;
  365. if (Environment.OSVersion.Version.Major > 5)
  366. {
  367. ret = true;
  368. }
  369. else if ((Environment.OSVersion.Version.Major == 5) &&
  370. (Environment.OSVersion.Version.Minor >= 1))
  371. {
  372. ret = true;
  373. }
  374. return ret;
  375. //return false;
  376. }
  377. /// <summary>
  378. /// Creates the SystemImageList
  379. /// </summary>
  380. void Create()
  381. {
  382. // forget last image list if any:
  383. hIml = IntPtr.Zero;
  384. if (IsXpOrAbove())
  385. {
  386. // Get the System IImageList object from the Shell:
  387. var iidImageList = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950");
  388. int ret = NativeMethods.SHGetImageList(
  389. (int) size,
  390. ref iidImageList,
  391. ref iImageList
  392. );
  393. if( ret != 0) throw new ShellImageListException("Error getting image list. Return code: {0}, LastWin32Error: {1}", ret, Marshal.GetLastWin32Error());
  394. // the image list handle is the IUnknown pointer, but
  395. // using Marshal.GetIUnknownForObject doesn't return
  396. // the right value. It really doesn't hurt to make
  397. // a second call to get the handle:
  398. var result = NativeMethods.SHGetImageListHandle((int) size, ref iidImageList, ref hIml);
  399. if( result != 0 ) throw new ShellImageListException("Error getting image list. Return code: {0}, LastWin32Error: {1}", ret, Marshal.GetLastWin32Error());
  400. }
  401. else
  402. {
  403. // Prepare flags:
  404. SHGetFileInfoConstants dwFlags = SHGetFileInfoConstants.SHGFI_USEFILEATTRIBUTES |
  405. SHGetFileInfoConstants.SHGFI_SYSICONINDEX;
  406. if (size == ShellImageListSize.SmallIcons)
  407. {
  408. dwFlags |= SHGetFileInfoConstants.SHGFI_SMALLICON;
  409. }
  410. // Get image list
  411. var shfi = new SHFILEINFO();
  412. var shfiSize = (uint) Marshal.SizeOf(shfi.GetType());
  413. // Call SHGetFileInfo to get the image list handle
  414. // using an arbitrary file:
  415. hIml = NativeMethods.SHGetFileInfo(
  416. ".txt",
  417. FILE_ATTRIBUTE_NORMAL,
  418. ref shfi,
  419. shfiSize,
  420. (uint) dwFlags);
  421. Debug.Assert((hIml != IntPtr.Zero), "Failed to create Image List");
  422. }
  423. }
  424. /// <summary>
  425. /// Clears up any resources associated with the SystemImageList
  426. /// when disposing is true.
  427. /// </summary>
  428. /// <param name="disposing">Whether the object is being disposed</param>
  429. protected virtual void Dispose(bool disposing)
  430. {
  431. if (!disposed && disposing && iImageList != null)
  432. {
  433. Marshal.ReleaseComObject(iImageList);
  434. }
  435. disposed = true;
  436. }
  437. /// <summary>
  438. /// Finalise for ShellImageList
  439. /// </summary>
  440. ~ShellImageList()
  441. {
  442. Dispose(false);
  443. }
  444. }
  445. }