Utilities.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053
  1. /*************************************************************************************
  2. Extended WPF Toolkit
  3. Copyright (C) 2007-2013 Xceed Software Inc.
  4. This program is provided to you under the terms of the Microsoft Public
  5. License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
  6. For more features, controls, and fast professional support,
  7. pick up the Plus Edition at http://xceed.com/wpf_toolkit
  8. Stay informed: follow @datagrid on Twitter or Like http://facebook.com/datagrids
  9. ***********************************************************************************/
  10. /**************************************************************************\
  11. Copyright Microsoft Corporation. All Rights Reserved.
  12. \**************************************************************************/
  13. // This file contains general utilities to aid in development.
  14. // Classes here generally shouldn't be exposed publicly since
  15. // they're not particular to any library functionality.
  16. // Because the classes here are internal, it's likely this file
  17. // might be included in multiple assemblies.
  18. namespace Standard
  19. {
  20. using System;
  21. using System.Collections.Generic;
  22. using System.ComponentModel;
  23. using System.Diagnostics.CodeAnalysis;
  24. using System.Globalization;
  25. using System.IO;
  26. using System.Reflection;
  27. using System.Runtime.InteropServices;
  28. using System.Security.Cryptography;
  29. using System.Text;
  30. using System.Windows;
  31. using System.Windows.Media;
  32. using System.Windows.Media.Imaging;
  33. internal static partial class Utility
  34. {
  35. private static readonly Version _osVersion = Environment.OSVersion.Version;
  36. private static readonly Version _presentationFrameworkVersion = Assembly.GetAssembly(typeof(Window)).GetName().Version;
  37. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  38. private static bool _MemCmp(IntPtr left, IntPtr right, long cb)
  39. {
  40. int offset = 0;
  41. for (; offset < (cb - sizeof(Int64)); offset += sizeof(Int64))
  42. {
  43. Int64 left64 = Marshal.ReadInt64(left, offset);
  44. Int64 right64 = Marshal.ReadInt64(right, offset);
  45. if (left64 != right64)
  46. {
  47. return false;
  48. }
  49. }
  50. for (; offset < cb; offset += sizeof(byte))
  51. {
  52. byte left8 = Marshal.ReadByte(left, offset);
  53. byte right8 = Marshal.ReadByte(right, offset);
  54. if (left8 != right8)
  55. {
  56. return false;
  57. }
  58. }
  59. return true;
  60. }
  61. /// <summary>The native RGB macro.</summary>
  62. /// <param name="c"></param>
  63. /// <returns></returns>
  64. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  65. public static int RGB(Color c)
  66. {
  67. return c.R | (c.G << 8) | (c.B << 16);
  68. }
  69. /// <summary>Convert a native integer that represent a color with an alpha channel into a Color struct.</summary>
  70. /// <param name="color">The integer that represents the color. Its bits are of the format 0xAARRGGBB.</param>
  71. /// <returns>A Color representation of the parameter.</returns>
  72. public static Color ColorFromArgbDword(uint color)
  73. {
  74. return Color.FromArgb(
  75. (byte)((color & 0xFF000000) >> 24),
  76. (byte)((color & 0x00FF0000) >> 16),
  77. (byte)((color & 0x0000FF00) >> 8),
  78. (byte)((color & 0x000000FF) >> 0));
  79. }
  80. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  81. public static int GET_X_LPARAM(IntPtr lParam)
  82. {
  83. return LOWORD(lParam.ToInt32());
  84. }
  85. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  86. public static int GET_Y_LPARAM(IntPtr lParam)
  87. {
  88. return HIWORD(lParam.ToInt32());
  89. }
  90. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  91. public static int HIWORD(int i)
  92. {
  93. return (short)(i >> 16);
  94. }
  95. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  96. public static int LOWORD(int i)
  97. {
  98. return (short)(i & 0xFFFF);
  99. }
  100. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  101. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
  102. public static bool AreStreamsEqual(Stream left, Stream right)
  103. {
  104. if (null == left)
  105. {
  106. return right == null;
  107. }
  108. if (null == right)
  109. {
  110. return false;
  111. }
  112. if (!left.CanRead || !right.CanRead)
  113. {
  114. throw new NotSupportedException("The streams can't be read for comparison");
  115. }
  116. if (left.Length != right.Length)
  117. {
  118. return false;
  119. }
  120. var length = (int)left.Length;
  121. // seek to beginning
  122. left.Position = 0;
  123. right.Position = 0;
  124. // total bytes read
  125. int totalReadLeft = 0;
  126. int totalReadRight = 0;
  127. // bytes read on this iteration
  128. int cbReadLeft = 0;
  129. int cbReadRight = 0;
  130. // where to store the read data
  131. var leftBuffer = new byte[512];
  132. var rightBuffer = new byte[512];
  133. // pin the left buffer
  134. GCHandle handleLeft = GCHandle.Alloc(leftBuffer, GCHandleType.Pinned);
  135. IntPtr ptrLeft = handleLeft.AddrOfPinnedObject();
  136. // pin the right buffer
  137. GCHandle handleRight = GCHandle.Alloc(rightBuffer, GCHandleType.Pinned);
  138. IntPtr ptrRight = handleRight.AddrOfPinnedObject();
  139. try
  140. {
  141. while (totalReadLeft < length)
  142. {
  143. Assert.AreEqual(totalReadLeft, totalReadRight);
  144. cbReadLeft = left.Read(leftBuffer, 0, leftBuffer.Length);
  145. cbReadRight = right.Read(rightBuffer, 0, rightBuffer.Length);
  146. // verify the contents are an exact match
  147. if (cbReadLeft != cbReadRight)
  148. {
  149. return false;
  150. }
  151. if (!_MemCmp(ptrLeft, ptrRight, cbReadLeft))
  152. {
  153. return false;
  154. }
  155. totalReadLeft += cbReadLeft;
  156. totalReadRight += cbReadRight;
  157. }
  158. Assert.AreEqual(cbReadLeft, cbReadRight);
  159. Assert.AreEqual(totalReadLeft, totalReadRight);
  160. Assert.AreEqual(length, totalReadLeft);
  161. return true;
  162. }
  163. finally
  164. {
  165. handleLeft.Free();
  166. handleRight.Free();
  167. }
  168. }
  169. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  170. public static bool GuidTryParse(string guidString, out Guid guid)
  171. {
  172. Verify.IsNeitherNullNorEmpty(guidString, "guidString");
  173. try
  174. {
  175. guid = new Guid(guidString);
  176. return true;
  177. }
  178. catch (FormatException)
  179. {
  180. }
  181. catch (OverflowException)
  182. {
  183. }
  184. // Doesn't seem to be a valid guid.
  185. guid = default(Guid);
  186. return false;
  187. }
  188. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  189. public static bool IsFlagSet(int value, int mask)
  190. {
  191. return 0 != (value & mask);
  192. }
  193. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  194. public static bool IsFlagSet(uint value, uint mask)
  195. {
  196. return 0 != (value & mask);
  197. }
  198. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  199. public static bool IsFlagSet(long value, long mask)
  200. {
  201. return 0 != (value & mask);
  202. }
  203. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  204. public static bool IsFlagSet(ulong value, ulong mask)
  205. {
  206. return 0 != (value & mask);
  207. }
  208. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  209. public static bool IsOSVistaOrNewer
  210. {
  211. get { return _osVersion >= new Version(6, 0); }
  212. }
  213. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  214. public static bool IsOSWindows7OrNewer
  215. {
  216. get { return _osVersion >= new Version(6, 1); }
  217. }
  218. /// <summary>
  219. /// Is this using WPF4?
  220. /// </summary>
  221. /// <remarks>
  222. /// There are a few specific bugs in Window in 3.5SP1 and below that require workarounds
  223. /// when handling WM_NCCALCSIZE on the HWND.
  224. /// </remarks>
  225. public static bool IsPresentationFrameworkVersionLessThan4
  226. {
  227. get { return _presentationFrameworkVersion < new Version(4, 0); }
  228. }
  229. // Caller is responsible for destroying the HICON
  230. // Caller is responsible to ensure that GDI+ has been initialized.
  231. [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
  232. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  233. public static IntPtr GenerateHICON(ImageSource image, Size dimensions)
  234. {
  235. if (image == null)
  236. {
  237. return IntPtr.Zero;
  238. }
  239. // If we're getting this from a ".ico" resource, then it comes through as a BitmapFrame.
  240. // We can use leverage this as a shortcut to get the right 16x16 representation
  241. // because DrawImage doesn't do that for us.
  242. var bf = image as BitmapFrame;
  243. if (bf != null)
  244. {
  245. bf = GetBestMatch(bf.Decoder.Frames, (int)dimensions.Width, (int)dimensions.Height);
  246. }
  247. else
  248. {
  249. // Constrain the dimensions based on the aspect ratio.
  250. var drawingDimensions = new Rect(0, 0, dimensions.Width, dimensions.Height);
  251. // There's no reason to assume that the requested image dimensions are square.
  252. double renderRatio = dimensions.Width / dimensions.Height;
  253. double aspectRatio = image.Width / image.Height;
  254. // If it's smaller than the requested size, then place it in the middle and pad the image.
  255. if (image.Width <= dimensions.Width && image.Height <= dimensions.Height)
  256. {
  257. drawingDimensions = new Rect((dimensions.Width - image.Width) / 2, (dimensions.Height - image.Height) / 2, image.Width, image.Height);
  258. }
  259. else if (renderRatio > aspectRatio)
  260. {
  261. double scaledRenderWidth = (image.Width / image.Height) * dimensions.Width;
  262. drawingDimensions = new Rect((dimensions.Width - scaledRenderWidth) / 2, 0, scaledRenderWidth, dimensions.Height);
  263. }
  264. else if (renderRatio < aspectRatio)
  265. {
  266. double scaledRenderHeight = (image.Height / image.Width) * dimensions.Height;
  267. drawingDimensions = new Rect(0, (dimensions.Height - scaledRenderHeight) / 2, dimensions.Width, scaledRenderHeight);
  268. }
  269. var dv = new DrawingVisual();
  270. DrawingContext dc = dv.RenderOpen();
  271. dc.DrawImage(image, drawingDimensions);
  272. dc.Close();
  273. var bmp = new RenderTargetBitmap((int)dimensions.Width, (int)dimensions.Height, 96, 96, PixelFormats.Pbgra32);
  274. bmp.Render(dv);
  275. bf = BitmapFrame.Create(bmp);
  276. }
  277. // Using GDI+ to convert to an HICON.
  278. // I'd rather not duplicate their code.
  279. using (MemoryStream memstm = new MemoryStream())
  280. {
  281. BitmapEncoder enc = new PngBitmapEncoder();
  282. enc.Frames.Add(bf);
  283. enc.Save(memstm);
  284. using (var istm = new ManagedIStream(memstm))
  285. {
  286. // We are not bubbling out GDI+ errors when creating the native image fails.
  287. IntPtr bitmap = IntPtr.Zero;
  288. try
  289. {
  290. Status gpStatus = NativeMethods.GdipCreateBitmapFromStream(istm, out bitmap);
  291. if (Status.Ok != gpStatus)
  292. {
  293. return IntPtr.Zero;
  294. }
  295. IntPtr hicon;
  296. gpStatus = NativeMethods.GdipCreateHICONFromBitmap(bitmap, out hicon);
  297. if (Status.Ok != gpStatus)
  298. {
  299. return IntPtr.Zero;
  300. }
  301. // Caller is responsible for freeing this.
  302. return hicon;
  303. }
  304. finally
  305. {
  306. Utility.SafeDisposeImage(ref bitmap);
  307. }
  308. }
  309. }
  310. }
  311. public static BitmapFrame GetBestMatch(IList<BitmapFrame> frames, int width, int height)
  312. {
  313. return _GetBestMatch(frames, _GetBitDepth(), width, height);
  314. }
  315. private static int _MatchImage(BitmapFrame frame, int bitDepth, int width, int height, int bpp)
  316. {
  317. int score = 2 * _WeightedAbs(bpp, bitDepth, false) +
  318. _WeightedAbs(frame.PixelWidth, width, true) +
  319. _WeightedAbs(frame.PixelHeight, height, true);
  320. return score;
  321. }
  322. private static int _WeightedAbs(int valueHave, int valueWant, bool fPunish)
  323. {
  324. int diff = (valueHave - valueWant);
  325. if (diff < 0)
  326. {
  327. diff = (fPunish ? -2 : -1) * diff;
  328. }
  329. return diff;
  330. }
  331. /// From a list of BitmapFrames find the one that best matches the requested dimensions.
  332. /// The methods used here are copied from Win32 sources. We want to be consistent with
  333. /// system behaviors.
  334. private static BitmapFrame _GetBestMatch(IList<BitmapFrame> frames, int bitDepth, int width, int height)
  335. {
  336. int bestScore = int.MaxValue;
  337. int bestBpp = 0;
  338. int bestIndex = 0;
  339. bool isBitmapIconDecoder = frames[0].Decoder is IconBitmapDecoder;
  340. for (int i = 0; i < frames.Count && bestScore != 0; ++i)
  341. {
  342. int currentIconBitDepth = isBitmapIconDecoder ? frames[i].Thumbnail.Format.BitsPerPixel : frames[i].Format.BitsPerPixel;
  343. if (currentIconBitDepth == 0)
  344. {
  345. currentIconBitDepth = 8;
  346. }
  347. int score = _MatchImage(frames[i], bitDepth, width, height, currentIconBitDepth);
  348. if (score < bestScore)
  349. {
  350. bestIndex = i;
  351. bestBpp = currentIconBitDepth;
  352. bestScore = score;
  353. }
  354. else if (score == bestScore)
  355. {
  356. // Tie breaker: choose the higher color depth. If that fails, choose first one.
  357. if (bestBpp < currentIconBitDepth)
  358. {
  359. bestIndex = i;
  360. bestBpp = currentIconBitDepth;
  361. }
  362. }
  363. }
  364. return frames[bestIndex];
  365. }
  366. // This can be cached. It's not going to change under reasonable circumstances.
  367. private static int s_bitDepth; // = 0;
  368. private static int _GetBitDepth()
  369. {
  370. if (s_bitDepth == 0)
  371. {
  372. using (SafeDC dc = SafeDC.GetDesktop())
  373. {
  374. s_bitDepth = NativeMethods.GetDeviceCaps(dc, DeviceCap.BITSPIXEL) * NativeMethods.GetDeviceCaps(dc, DeviceCap.PLANES);
  375. }
  376. }
  377. return s_bitDepth;
  378. }
  379. /// <summary>
  380. /// Simple guard against the exceptions that File.Delete throws on null and empty strings.
  381. /// </summary>
  382. /// <param name="path">The path to delete. Unlike File.Delete, this can be null or empty.</param>
  383. /// <remarks>
  384. /// Note that File.Delete, and by extension SafeDeleteFile, does not throw an exception
  385. /// if the file does not exist.
  386. /// </remarks>
  387. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  388. public static void SafeDeleteFile(string path)
  389. {
  390. if (!string.IsNullOrEmpty(path))
  391. {
  392. File.Delete(path);
  393. }
  394. }
  395. /// <summary>GDI's DeleteObject</summary>
  396. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  397. public static void SafeDeleteObject(ref IntPtr gdiObject)
  398. {
  399. IntPtr p = gdiObject;
  400. gdiObject = IntPtr.Zero;
  401. if (IntPtr.Zero != p)
  402. {
  403. NativeMethods.DeleteObject(p);
  404. }
  405. }
  406. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  407. public static void SafeDestroyIcon(ref IntPtr hicon)
  408. {
  409. IntPtr p = hicon;
  410. hicon = IntPtr.Zero;
  411. if (IntPtr.Zero != p)
  412. {
  413. NativeMethods.DestroyIcon(p);
  414. }
  415. }
  416. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  417. public static void SafeDestroyWindow(ref IntPtr hwnd)
  418. {
  419. IntPtr p = hwnd;
  420. hwnd = IntPtr.Zero;
  421. if (NativeMethods.IsWindow(p))
  422. {
  423. NativeMethods.DestroyWindow(p);
  424. }
  425. }
  426. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  427. public static void SafeDispose<T>(ref T disposable) where T : IDisposable
  428. {
  429. // Dispose can safely be called on an object multiple times.
  430. IDisposable t = disposable;
  431. disposable = default(T);
  432. if (null != t)
  433. {
  434. t.Dispose();
  435. }
  436. }
  437. /// <summary>GDI+'s DisposeImage</summary>
  438. /// <param name="gdipImage"></param>
  439. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  440. public static void SafeDisposeImage(ref IntPtr gdipImage)
  441. {
  442. IntPtr p = gdipImage;
  443. gdipImage = IntPtr.Zero;
  444. if (IntPtr.Zero != p)
  445. {
  446. NativeMethods.GdipDisposeImage(p);
  447. }
  448. }
  449. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  450. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
  451. public static void SafeCoTaskMemFree(ref IntPtr ptr)
  452. {
  453. IntPtr p = ptr;
  454. ptr = IntPtr.Zero;
  455. if (IntPtr.Zero != p)
  456. {
  457. Marshal.FreeCoTaskMem(p);
  458. }
  459. }
  460. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  461. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
  462. public static void SafeFreeHGlobal(ref IntPtr hglobal)
  463. {
  464. IntPtr p = hglobal;
  465. hglobal = IntPtr.Zero;
  466. if (IntPtr.Zero != p)
  467. {
  468. Marshal.FreeHGlobal(p);
  469. }
  470. }
  471. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  472. [SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands")]
  473. public static void SafeRelease<T>(ref T comObject) where T : class
  474. {
  475. T t = comObject;
  476. comObject = default(T);
  477. if (null != t)
  478. {
  479. Assert.IsTrue(Marshal.IsComObject(t));
  480. Marshal.ReleaseComObject(t);
  481. }
  482. }
  483. /// <summary>
  484. /// Utility to help classes catenate their properties for implementing ToString().
  485. /// </summary>
  486. /// <param name="source">The StringBuilder to catenate the results into.</param>
  487. /// <param name="propertyName">The name of the property to be catenated.</param>
  488. /// <param name="value">The value of the property to be catenated.</param>
  489. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  490. public static void GeneratePropertyString(StringBuilder source, string propertyName, string value)
  491. {
  492. Assert.IsNotNull(source);
  493. Assert.IsFalse(string.IsNullOrEmpty(propertyName));
  494. if (0 != source.Length)
  495. {
  496. source.Append(' ');
  497. }
  498. source.Append(propertyName);
  499. source.Append(": ");
  500. if (string.IsNullOrEmpty(value))
  501. {
  502. source.Append("<null>");
  503. }
  504. else
  505. {
  506. source.Append('\"');
  507. source.Append(value);
  508. source.Append('\"');
  509. }
  510. }
  511. /// <summary>
  512. /// Generates ToString functionality for a struct. This is an expensive way to do it,
  513. /// it exists for the sake of debugging while classes are in flux.
  514. /// Eventually this should just be removed and the classes should
  515. /// do this without reflection.
  516. /// </summary>
  517. /// <typeparam name="T"></typeparam>
  518. /// <param name="object"></param>
  519. /// <returns></returns>
  520. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  521. [Obsolete]
  522. public static string GenerateToString<T>(T @object) where T : struct
  523. {
  524. var sbRet = new StringBuilder();
  525. foreach (PropertyInfo property in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
  526. {
  527. if (0 != sbRet.Length)
  528. {
  529. sbRet.Append(", ");
  530. }
  531. Assert.AreEqual(0, property.GetIndexParameters().Length);
  532. object value = property.GetValue(@object, null);
  533. string format = null == value ? "{0}: <null>" : "{0}: \"{1}\"";
  534. sbRet.AppendFormat(format, property.Name, value);
  535. }
  536. return sbRet.ToString();
  537. }
  538. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  539. public static void CopyStream(Stream destination, Stream source)
  540. {
  541. Assert.IsNotNull(source);
  542. Assert.IsNotNull(destination);
  543. destination.Position = 0;
  544. // If we're copying from, say, a web stream, don't fail because of this.
  545. if (source.CanSeek)
  546. {
  547. source.Position = 0;
  548. // Consider that this could throw because
  549. // the source stream doesn't know it's size...
  550. destination.SetLength(source.Length);
  551. }
  552. var buffer = new byte[4096];
  553. int cbRead;
  554. do
  555. {
  556. cbRead = source.Read(buffer, 0, buffer.Length);
  557. if (0 != cbRead)
  558. {
  559. destination.Write(buffer, 0, cbRead);
  560. }
  561. }
  562. while (buffer.Length == cbRead);
  563. // Reset the Seek pointer before returning.
  564. destination.Position = 0;
  565. }
  566. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  567. public static string HashStreamMD5(Stream stm)
  568. {
  569. stm.Position = 0;
  570. var hashBuilder = new StringBuilder();
  571. using (MD5 md5 = MD5.Create())
  572. {
  573. foreach (byte b in md5.ComputeHash(stm))
  574. {
  575. hashBuilder.Append(b.ToString("x2", CultureInfo.InvariantCulture));
  576. }
  577. }
  578. return hashBuilder.ToString();
  579. }
  580. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  581. public static void EnsureDirectory(string path)
  582. {
  583. if (!Directory.Exists(Path.GetDirectoryName(path)))
  584. {
  585. Directory.CreateDirectory(Path.GetDirectoryName(path));
  586. }
  587. }
  588. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  589. public static bool MemCmp(byte[] left, byte[] right, int cb)
  590. {
  591. Assert.IsNotNull(left);
  592. Assert.IsNotNull(right);
  593. Assert.IsTrue(cb <= Math.Min(left.Length, right.Length));
  594. // pin this buffer
  595. GCHandle handleLeft = GCHandle.Alloc(left, GCHandleType.Pinned);
  596. IntPtr ptrLeft = handleLeft.AddrOfPinnedObject();
  597. // pin the other buffer
  598. GCHandle handleRight = GCHandle.Alloc(right, GCHandleType.Pinned);
  599. IntPtr ptrRight = handleRight.AddrOfPinnedObject();
  600. bool fRet = _MemCmp(ptrLeft, ptrRight, cb);
  601. handleLeft.Free();
  602. handleRight.Free();
  603. return fRet;
  604. }
  605. private class _UrlDecoder
  606. {
  607. private readonly Encoding _encoding;
  608. private readonly char[] _charBuffer;
  609. private readonly byte[] _byteBuffer;
  610. private int _byteCount;
  611. private int _charCount;
  612. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  613. public _UrlDecoder(int size, Encoding encoding)
  614. {
  615. _encoding = encoding;
  616. _charBuffer = new char[size];
  617. _byteBuffer = new byte[size];
  618. }
  619. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  620. public void AddByte(byte b)
  621. {
  622. _byteBuffer[_byteCount++] = b;
  623. }
  624. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  625. public void AddChar(char ch)
  626. {
  627. _FlushBytes();
  628. _charBuffer[_charCount++] = ch;
  629. }
  630. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  631. private void _FlushBytes()
  632. {
  633. if (_byteCount > 0)
  634. {
  635. _charCount += _encoding.GetChars(_byteBuffer, 0, _byteCount, _charBuffer, _charCount);
  636. _byteCount = 0;
  637. }
  638. }
  639. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  640. public string GetString()
  641. {
  642. _FlushBytes();
  643. if (_charCount > 0)
  644. {
  645. return new string(_charBuffer, 0, _charCount);
  646. }
  647. return "";
  648. }
  649. }
  650. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  651. public static string UrlDecode(string url)
  652. {
  653. if (url == null)
  654. {
  655. return null;
  656. }
  657. var decoder = new _UrlDecoder(url.Length, Encoding.UTF8);
  658. int length = url.Length;
  659. for (int i = 0; i < length; ++i)
  660. {
  661. char ch = url[i];
  662. if (ch == '+')
  663. {
  664. decoder.AddByte((byte)' ');
  665. continue;
  666. }
  667. if (ch == '%' && i < length - 2)
  668. {
  669. // decode %uXXXX into a Unicode character.
  670. if (url[i + 1] == 'u' && i < length - 5)
  671. {
  672. int a = _HexToInt(url[i + 2]);
  673. int b = _HexToInt(url[i + 3]);
  674. int c = _HexToInt(url[i + 4]);
  675. int d = _HexToInt(url[i + 5]);
  676. if (a >= 0 && b >= 0 && c >= 0 && d >= 0)
  677. {
  678. decoder.AddChar((char)((a << 12) | (b << 8) | (c << 4) | d));
  679. i += 5;
  680. continue;
  681. }
  682. }
  683. else
  684. {
  685. // decode %XX into a Unicode character.
  686. int a = _HexToInt(url[i + 1]);
  687. int b = _HexToInt(url[i + 2]);
  688. if (a >= 0 && b >= 0)
  689. {
  690. decoder.AddByte((byte)((a << 4) | b));
  691. i += 2;
  692. continue;
  693. }
  694. }
  695. }
  696. // Add any 7bit character as a byte.
  697. if ((ch & 0xFF80) == 0)
  698. {
  699. decoder.AddByte((byte)ch);
  700. }
  701. else
  702. {
  703. decoder.AddChar(ch);
  704. }
  705. }
  706. return decoder.GetString();
  707. }
  708. /// <summary>
  709. /// Encodes a URL string. Duplicated functionality from System.Web.HttpUtility.UrlEncode.
  710. /// </summary>
  711. /// <param name="url"></param>
  712. /// <returns></returns>
  713. /// <remarks>
  714. /// Duplicated from System.Web.HttpUtility because System.Web isn't part of the client profile.
  715. /// URL Encoding replaces ' ' with '+' and unsafe ASCII characters with '%XX'.
  716. /// Safe characters are defined in RFC2396 (http://www.ietf.org/rfc/rfc2396.txt).
  717. /// They are the 7-bit ASCII alphanumerics and the mark characters "-_.!~*'()".
  718. /// This implementation does not treat '~' as a safe character to be consistent with the System.Web version.
  719. /// </remarks>
  720. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  721. public static string UrlEncode(string url)
  722. {
  723. if (url == null)
  724. {
  725. return null;
  726. }
  727. byte[] bytes = Encoding.UTF8.GetBytes(url);
  728. bool needsEncoding = false;
  729. int unsafeCharCount = 0;
  730. foreach (byte b in bytes)
  731. {
  732. if (b == ' ')
  733. {
  734. needsEncoding = true;
  735. }
  736. else if (!_UrlEncodeIsSafe(b))
  737. {
  738. ++unsafeCharCount;
  739. needsEncoding = true;
  740. }
  741. }
  742. if (needsEncoding)
  743. {
  744. var buffer = new byte[bytes.Length + (unsafeCharCount * 2)];
  745. int writeIndex = 0;
  746. foreach (byte b in bytes)
  747. {
  748. if (_UrlEncodeIsSafe(b))
  749. {
  750. buffer[writeIndex++] = b;
  751. }
  752. else if (b == ' ')
  753. {
  754. buffer[writeIndex++] = (byte)'+';
  755. }
  756. else
  757. {
  758. buffer[writeIndex++] = (byte)'%';
  759. buffer[writeIndex++] = _IntToHex((b >> 4) & 0xF);
  760. buffer[writeIndex++] = _IntToHex(b & 0xF);
  761. }
  762. }
  763. bytes = buffer;
  764. Assert.AreEqual(buffer.Length, writeIndex);
  765. }
  766. return Encoding.ASCII.GetString(bytes);
  767. }
  768. // HttpUtility's UrlEncode is slightly different from the RFC.
  769. // RFC2396 describes unreserved characters as alphanumeric or
  770. // the list "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
  771. // The System.Web version unnecessarily escapes '~', which should be okay...
  772. // Keeping that same pattern here just to be consistent.
  773. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  774. private static bool _UrlEncodeIsSafe(byte b)
  775. {
  776. if (_IsAsciiAlphaNumeric(b))
  777. {
  778. return true;
  779. }
  780. switch ((char)b)
  781. {
  782. case '-':
  783. case '_':
  784. case '.':
  785. case '!':
  786. //case '~':
  787. case '*':
  788. case '\'':
  789. case '(':
  790. case ')':
  791. return true;
  792. }
  793. return false;
  794. }
  795. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  796. private static bool _IsAsciiAlphaNumeric(byte b)
  797. {
  798. return (b >= 'a' && b <= 'z')
  799. || (b >= 'A' && b <= 'Z')
  800. || (b >= '0' && b <= '9');
  801. }
  802. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  803. private static byte _IntToHex(int n)
  804. {
  805. Assert.BoundedInteger(0, n, 16);
  806. if (n <= 9)
  807. {
  808. return (byte)(n + '0');
  809. }
  810. return (byte)(n - 10 + 'A');
  811. }
  812. [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
  813. private static int _HexToInt(char h)
  814. {
  815. if (h >= '0' && h <= '9')
  816. {
  817. return h - '0';
  818. }
  819. if (h >= 'a' && h <= 'f')
  820. {
  821. return h - 'a' + 10;
  822. }
  823. if (h >= 'A' && h <= 'F')
  824. {
  825. return h - 'A' + 10;
  826. }
  827. Assert.Fail("Invalid hex character " + h);
  828. return -1;
  829. }
  830. public static void AddDependencyPropertyChangeListener(object component, DependencyProperty property, EventHandler listener)
  831. {
  832. if (component == null)
  833. {
  834. return;
  835. }
  836. Assert.IsNotNull(property);
  837. Assert.IsNotNull(listener);
  838. DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(property, component.GetType());
  839. dpd.AddValueChanged(component, listener);
  840. }
  841. public static void RemoveDependencyPropertyChangeListener(object component, DependencyProperty property, EventHandler listener)
  842. {
  843. if (component == null)
  844. {
  845. return;
  846. }
  847. Assert.IsNotNull(property);
  848. Assert.IsNotNull(listener);
  849. DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(property, component.GetType());
  850. dpd.RemoveValueChanged(component, listener);
  851. }
  852. #region Extension Methods
  853. public static bool IsThicknessNonNegative(Thickness thickness)
  854. {
  855. if (!IsDoubleFiniteAndNonNegative(thickness.Top))
  856. {
  857. return false;
  858. }
  859. if (!IsDoubleFiniteAndNonNegative(thickness.Left))
  860. {
  861. return false;
  862. }
  863. if (!IsDoubleFiniteAndNonNegative(thickness.Bottom))
  864. {
  865. return false;
  866. }
  867. if (!IsDoubleFiniteAndNonNegative(thickness.Right))
  868. {
  869. return false;
  870. }
  871. return true;
  872. }
  873. public static bool IsCornerRadiusValid(CornerRadius cornerRadius)
  874. {
  875. if (!IsDoubleFiniteAndNonNegative(cornerRadius.TopLeft))
  876. {
  877. return false;
  878. }
  879. if (!IsDoubleFiniteAndNonNegative(cornerRadius.TopRight))
  880. {
  881. return false;
  882. }
  883. if (!IsDoubleFiniteAndNonNegative(cornerRadius.BottomLeft))
  884. {
  885. return false;
  886. }
  887. if (!IsDoubleFiniteAndNonNegative(cornerRadius.BottomRight))
  888. {
  889. return false;
  890. }
  891. return true;
  892. }
  893. public static bool IsDoubleFiniteAndNonNegative(double d)
  894. {
  895. if (double.IsNaN(d) || double.IsInfinity(d) || d < 0)
  896. {
  897. return false;
  898. }
  899. return true;
  900. }
  901. #endregion
  902. }
  903. }