Code:
/ DotNET / DotNET / 8.0 / untmp / whidbey / REDBITS / ndp / fx / src / CommonUI / System / Drawing / Icon.cs / 2 / Icon.cs
// #define CUSTOM_MARSHALING_ISTREAM //------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* */ namespace System.Drawing { using System.Runtime.Serialization.Formatters; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Security; using System.Security.Permissions; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System; using System.Collections; using System.ComponentModel; using Microsoft.Win32; using System.Drawing.Design; using System.Drawing.Imaging; using System.IO; using System.Reflection; using System.Text; using System.Drawing.Internal; ////// /// This class represents a Windows icon, which is a small bitmap image used to /// represent an object. Icons can be thought of as transparent bitmaps, although /// their size is determined by the system. /// [ TypeConverterAttribute(typeof(IconConverter)), Editor("System.Drawing.Design.IconEditor, " + AssemblyRef.SystemDrawingDesign, typeof(UITypeEditor)) ] [Serializable] public sealed class Icon : MarshalByRefObject, ISerializable, ICloneable, IDisposable { #if FINALIZATION_WATCH private string allocationSite = Graphics.GetAllocationStack(); #endif private static int bitDepth; // Icon data // private byte[] iconData; private int bestImageOffset; private int bestBitDepth; private Size iconSize = System.Drawing.Size.Empty; private IntPtr handle = IntPtr.Zero; private bool ownHandle = true; private Icon() { } internal Icon(IntPtr handle) : this(handle, false) { } internal Icon(IntPtr handle, bool takeOwnership) { if (handle == IntPtr.Zero) { throw new ArgumentException(SR.GetString(SR.InvalidGDIHandle, (typeof(Icon)).Name)); } this.handle = handle; this.ownHandle = takeOwnership; } ////// /// Loads an icon object from the given filename. /// public Icon(string fileName) : this(fileName, 0,0) { } public Icon(string fileName, Size size) : this(fileName, size.Width, size.Height) { } public Icon(string fileName, int width, int height) : this() { // SECREVIEW : This FileStream constructor demands FileIOPermission (FileIOPermissionAccess) for reading, writing, // and appending to files so we don't need to set a demand here. // using (FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) { Debug.Assert(f != null, "File.OpenRead returned null instead of throwing an exception"); iconData = new byte[(int)f.Length]; f.Read(iconData, 0, iconData.Length); } Initialize(width, height); } ////// /// Duplicates the given icon, attempting to find a version of the icon /// that matches the requested size. If a version cannot be found that /// exactally matches the size, the closest match will be used. Note /// that if original is an icon with a single size, this will /// merely create a dupicate icon. You can use the stretching modes /// of drawImage to force the icon to the size you want. /// public Icon(Icon original, Size size) : this(original, size.Width, size.Height) { } ////// /// Duplicates the given icon, attempting to find a version of the icon /// that matches the requested size. If a version cannot be found that /// exactally matches the size, the closest match will be used. Note /// that if original is an icon with a single size, this will /// merely create a dupicate icon. You can use the stretching modes /// of drawImage to force the icon to the size you want. /// public Icon(Icon original, int width, int height) : this() { if (original == null) { throw new ArgumentException(SR.GetString(SR.InvalidArgument, "original", "null")); } iconData = original.iconData; if (iconData == null) { iconSize = original.Size; handle = SafeNativeMethods.CopyImage(new HandleRef(original, original.Handle), SafeNativeMethods.IMAGE_ICON, iconSize.Width, iconSize.Height, 0); } else { Initialize(width, height); } } ////// /// Loads an icon object from the given resource. /// public Icon(Type type, string resource) : this() { Stream stream = type.Module.Assembly.GetManifestResourceStream(type, resource); if (stream == null) { throw new ArgumentException(SR.GetString(SR.ResourceNotFound, type, resource)); } iconData = new byte[(int)stream.Length]; stream.Read(iconData, 0, iconData.Length); Initialize(0, 0); } ////// /// Loads an icon object from the given data stream. /// public Icon(Stream stream) : this(stream, 0,0) { } public Icon(Stream stream, Size size) : this(stream, size.Width, size.Height) { } ////// /// public Icon(Stream stream, int width, int height) : this() { if (stream == null) { throw new ArgumentException(SR.GetString(SR.InvalidArgument, "stream", "null")); } iconData = new byte[(int)stream.Length]; stream.Read(iconData, 0, iconData.Length); Initialize(width, height); } ///[To be supplied.] ////// /// Constructor used in deserialization /// ///[SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] private Icon(SerializationInfo info, StreamingContext context) { iconData = (byte[])info.GetValue("IconData", typeof(byte[])); iconSize = (Size)info.GetValue("IconSize", typeof(Size)); if (iconSize.IsEmpty) { Initialize(0, 0); } else { Initialize(iconSize.Width, iconSize.Height); } } /// /// /// Extracts an icon object from the given filename. /// public static Icon ExtractAssociatedIcon(string filePath) { return ExtractAssociatedIcon(filePath, 0); } ////// /// Extracts an icon object from the given filename. /// private static Icon ExtractAssociatedIcon(string filePath, int index) { if (filePath == null) { throw new ArgumentException(SR.GetString(SR.InvalidArgument, "filePath", "null")); } Uri uri; try { uri = new Uri(filePath); } catch (UriFormatException) { // It's a relative pathname, get its full path as a file. // SECREVIEW : If path does exist, the caller must have FileIOPermissionAccess.PathDiscovery permission. // Note that unlike most members of the Path class, this method accesses the file system. // filePath = Path.GetFullPath(filePath); uri = new Uri(filePath); } if (uri.IsUnc) { throw new ArgumentException (SR.GetString (SR.InvalidArgument, "filePath", filePath)); } if (uri.IsFile) { // SECREVIEW : The File.Exists() below will do the demand for the FileIOPermission // for us. So, we do not need an additional demand anymore. // if (!File.Exists(filePath)) { // I have to do this so I can give a meaningful // error back to the user. File.Exists() cal fail because of either // a failure to demand security or because the file does not exist. // Always telling the user that the file does not exist is not a good // choice. So, we demand the permission again. This means that we are // going to demand the permission twice for the failure case, but that's // better than always demanding the permission twice. // IntSecurity.DemandReadFileIO(filePath); throw new FileNotFoundException(filePath); } Icon icon = new Icon(); StringBuilder sb = new StringBuilder(NativeMethods.MAX_PATH); sb.Append(filePath); IntPtr hIcon = SafeNativeMethods.ExtractAssociatedIcon(NativeMethods.NullHandleRef, sb, ref index); if (hIcon != IntPtr.Zero) { IntSecurity.ObjectFromWin32Handle.Demand(); icon = new Icon(hIcon, true); return icon; } } return null; } ////// /// The Win32 handle for this object. This is not a copy of the handle; do /// not free it. /// [Browsable(false)] public IntPtr Handle { // SECREVIEW : Getting the handle is ok, methods receiving a handle should demand security permissions though. // get { if (handle == IntPtr.Zero) { throw new ObjectDisposedException(GetType().Name); } return handle; } } ////// /// [Browsable(false)] public int Height { get { return Size.Height;} } ///[To be supplied.] ////// /// The size of this icon object. /// public Size Size { get { if (iconSize.IsEmpty) { SafeNativeMethods.ICONINFO info = new SafeNativeMethods.ICONINFO(); SafeNativeMethods.GetIconInfo(new HandleRef(this, Handle), info); SafeNativeMethods.BITMAP bmp = new SafeNativeMethods.BITMAP(); if (info.hbmColor != IntPtr.Zero) { SafeNativeMethods.GetObject(new HandleRef(null, info.hbmColor), Marshal.SizeOf(typeof(SafeNativeMethods.BITMAP)), bmp); SafeNativeMethods.IntDeleteObject(new HandleRef(null, info.hbmColor)); iconSize = new Size(bmp.bmWidth, bmp.bmHeight); } else if (info.hbmMask != IntPtr.Zero) { SafeNativeMethods.GetObject(new HandleRef(null, info.hbmMask), Marshal.SizeOf(typeof(SafeNativeMethods.BITMAP)), bmp); iconSize = new Size(bmp.bmWidth, bmp.bmHeight / 2); } if (info.hbmMask != IntPtr.Zero) { SafeNativeMethods.IntDeleteObject(new HandleRef(null, info.hbmMask)); } } return iconSize; } } ////// /// [Browsable(false)] public int Width { get { return Size.Width;} } ///[To be supplied.] ////// /// Clones the icon object, creating a duplicate image. /// public object Clone() { return new Icon(this, Size.Width, Size.Height); } ////// /// Called when this object is going to destroy it's Win32 handle. You /// may override this if there is something special you need to do to /// destroy the handle. This will be called even if the handle is not /// owned by this object, which is handy if you want to create a /// derived class that has it's own create/destroy semantics. /// /// The default implementation will call the appropriate Win32 /// call to destroy the handle if this object currently owns the /// handle. It will do nothing if the object does not currently /// own the handle. /// internal void DestroyHandle() { if (ownHandle) { SafeNativeMethods.DestroyIcon(new HandleRef(this, handle)); handle = IntPtr.Zero; } } ////// /// Cleans up the resources allocated by this object. Once called, the cursor /// object is no longer useful. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } void Dispose(bool disposing) { if (handle != IntPtr.Zero) { #if FINALIZATION_WATCH if (!disposing) { Debug.WriteLine("**********************\nDisposed through finalization:\n" + allocationSite); } #endif DestroyHandle(); } } ////// /// Draws this image to a graphics object. The drawing command originates on the graphics /// object, but a graphics object generally has no idea how to render a given image. So, /// it passes the call to the actual image. This version crops the image to the given /// dimensions and allows the user to specify a rectangle within the image to draw. /// // This method is way more powerful than what we expose, but I'll leave it in place. private void DrawIcon(IntPtr dc, Rectangle imageRect, Rectangle targetRect, bool stretch) { int imageX = 0; int imageY = 0; int imageWidth; int imageHeight; int targetX = 0; int targetY = 0; int targetWidth = 0; int targetHeight = 0; Size cursorSize = Size; // compute the dimensions of the icon, if needed // if (!imageRect.IsEmpty) { imageX = imageRect.X; imageY = imageRect.Y; imageWidth = imageRect.Width; imageHeight = imageRect.Height; } else { imageWidth = cursorSize.Width; imageHeight = cursorSize.Height; } if (!targetRect.IsEmpty) { targetX = targetRect.X; targetY = targetRect.Y; targetWidth = targetRect.Width; targetHeight = targetRect.Height; } else { targetWidth = cursorSize.Width; targetHeight = cursorSize.Height; } int drawWidth, drawHeight; int clipWidth, clipHeight; if (stretch) { drawWidth = cursorSize.Width * targetWidth / imageWidth; drawHeight = cursorSize.Height * targetHeight / imageHeight; clipWidth = targetWidth; clipHeight = targetHeight; } else { drawWidth = cursorSize.Width; drawHeight = cursorSize.Height; clipWidth = targetWidth < imageWidth ? targetWidth : imageWidth; clipHeight = targetHeight < imageHeight ? targetHeight : imageHeight; } // The ROP is SRCCOPY, so we can be simple here and take // advantage of clipping regions. Drawing the cursor // is merely a matter of offsetting and clipping. // IntPtr hSaveRgn = SafeNativeMethods.SaveClipRgn(dc); try { SafeNativeMethods.IntersectClipRect(new HandleRef(this, dc), targetX, targetY, targetX+clipWidth, targetY+clipHeight); SafeNativeMethods.DrawIconEx(new HandleRef(null, dc), targetX - imageX, targetY - imageY, new HandleRef(this, handle), drawWidth, drawHeight, 0, NativeMethods.NullHandleRef, SafeNativeMethods.DI_NORMAL); } finally { SafeNativeMethods.RestoreClipRgn(dc, hSaveRgn); } } internal void Draw(Graphics graphics, int x, int y) { Size size = Size; Draw(graphics, new Rectangle(x, y, size.Width, size.Height)); } ////// /// Draws this image to a graphics object. The drawing command originates on the graphics /// object, but a graphics object generally has no idea how to render a given image. So, /// it passes the call to the actual image. This version stretches the image to the given /// dimensions and allows the user to specify a rectangle within the image to draw. /// internal void Draw(Graphics graphics, Rectangle targetRect) { Rectangle copy = targetRect; copy.X += (int) graphics.Transform.OffsetX; copy.Y += (int) graphics.Transform.OffsetY; WindowsGraphics wg = WindowsGraphics.FromGraphics(graphics, ApplyGraphicsProperties.Clipping); IntPtr dc = wg.GetHdc(); try { DrawIcon(dc, Rectangle.Empty, copy, true); } finally { wg.Dispose(); } } ////// /// Draws this image to a graphics object. The drawing command originates on the graphics /// object, but a graphics object generally has no idea how to render a given image. So, /// it passes the call to the actual image. This version crops the image to the given /// dimensions and allows the user to specify a rectangle within the image to draw. /// internal void DrawUnstretched(Graphics graphics, Rectangle targetRect) { Rectangle copy = targetRect; copy.X += (int) graphics.Transform.OffsetX; copy.Y += (int) graphics.Transform.OffsetY; WindowsGraphics wg = WindowsGraphics.FromGraphics(graphics, ApplyGraphicsProperties.Clipping); IntPtr dc = wg.GetHdc(); try { DrawIcon(dc, Rectangle.Empty, copy, false); } finally { wg.Dispose(); } } ////// /// Cleans up Windows resources for this object. /// ~Icon() { Dispose(false); } ////// /// Creates an icon object from a given Win32 icon handle. The Icon object /// does not claim ownership of the icon handle; you must free it when you are /// done. /// public static Icon FromHandle(IntPtr handle) { IntSecurity.ObjectFromWin32Handle.Demand(); return new Icon(handle); } private unsafe short GetShort(byte* pb) { int retval = 0; if (0 != (unchecked((byte)pb) & 1)) { retval = *pb; pb++; retval = retval | (*pb << 8); } else { retval = (int)(*(short*)pb); } return (short)retval; } private unsafe int GetInt(byte* pb) { int retval = 0; if (0 != (unchecked((byte)pb) & 3)) { retval = *pb; pb++; retval = retval | (*pb << 8); pb++; retval = retval | (*pb << 16); pb++; retval = retval | (*pb << 24); } else { retval = *(int*)pb; } return retval; } ////// /// Initializes this Image object. This is identical to calling the image's /// constructor with picture, but this allows non-constructor initialization, /// which may be necessary in some instances. /// private unsafe void Initialize(int width, int height) { if (iconData == null || handle != IntPtr.Zero) { throw new InvalidOperationException(SR.GetString(SR.IllegalState, GetType().Name)); } if (iconData.Length < Marshal.SizeOf(typeof(SafeNativeMethods.ICONDIR))) { throw new ArgumentException(SR.GetString(SR.InvalidPictureType, "picture", "Icon")); } // Get the correct width / height // if (width == 0) { width = UnsafeNativeMethods.GetSystemMetrics(SafeNativeMethods.SM_CXICON); } if (height == 0) { height = UnsafeNativeMethods.GetSystemMetrics(SafeNativeMethods.SM_CYICON); } if (bitDepth == 0) { IntPtr dc = UnsafeNativeMethods.GetDC(NativeMethods.NullHandleRef); bitDepth = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), SafeNativeMethods.BITSPIXEL); bitDepth *= UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), SafeNativeMethods.PLANES); UnsafeNativeMethods.ReleaseDC(NativeMethods.NullHandleRef, new HandleRef(null, dc)); // If the bitdepth is 8, make it 4. Why? Because windows does not // choose a 256 color icon if the display is running in 256 color mode // because of palette flicker. // if (bitDepth == 8) bitDepth = 4; } fixed(byte *pbIconData = iconData) { short idReserved = GetShort(pbIconData); short idType = GetShort(pbIconData + 2); short idCount = GetShort(pbIconData + 4); if (idReserved != 0 || idType != 1 || idCount == 0) { throw new ArgumentException(SR.GetString(SR.InvalidPictureType, "picture", "Icon")); } SafeNativeMethods.ICONDIRENTRY EntryTemp; byte bestWidth = 0; byte bestHeight = 0; int bestBytesInRes = 0; //int bestBitDepth = 0; byte* pbIconDirEntry = pbIconData + 6; int icondirEntrySize = Marshal.SizeOf(typeof(SafeNativeMethods.ICONDIRENTRY)); Debug.Assert((icondirEntrySize * idCount) < iconData.Length, "Illegal number of ICONDIRENTRIES"); if ((icondirEntrySize * idCount) >= iconData.Length) { throw new ArgumentException(SR.GetString(SR.InvalidPictureType, "picture", "Icon")); } for (int i = 0; i < idCount; i++) { // // Fill in EntryTemp // EntryTemp.bWidth = pbIconDirEntry[0]; EntryTemp.bHeight = pbIconDirEntry[1]; EntryTemp.bColorCount = pbIconDirEntry[2]; EntryTemp.bReserved = pbIconDirEntry[3]; EntryTemp.wPlanes = GetShort(pbIconDirEntry + 4); EntryTemp.wBitCount = GetShort(pbIconDirEntry + 6); EntryTemp.dwBytesInRes = GetInt( pbIconDirEntry + 8); EntryTemp.dwImageOffset = GetInt( pbIconDirEntry +12); // // // bool fUpdateBestFit = false; int iconBitDepth = 0; if(EntryTemp.bColorCount != 0) { iconBitDepth = 4; if(EntryTemp.bColorCount < 0x10) iconBitDepth = 1; } else { iconBitDepth = EntryTemp.wBitCount; } // it looks like if nothing is specified at this point, bpp is 8... if(iconBitDepth == 0) iconBitDepth = 8; // Windows rules for specifing an icon: // // 1. The icon with the closest size match. // 2. For matching sizes, the image with the closest bit depth. // 3. If there is no color depth match, the icon with the closest color depth that does not exceed the display. // 4. If all icon color depth > display, lowest color depth is chosen. // 5. color depth of > 8bpp are all equal. // 6. Never choose an 8bpp icon on an 8bpp system. // if (0 == bestBytesInRes) { fUpdateBestFit = true; } else { int bestDelta = Math.Abs(bestWidth - width) + Math.Abs(bestHeight - height); int thisDelta = Math.Abs(EntryTemp.bWidth - width) + Math.Abs(EntryTemp.bHeight - height); if ((thisDelta < bestDelta) || (thisDelta == bestDelta && (iconBitDepth <= bitDepth && iconBitDepth > bestBitDepth || bestBitDepth > bitDepth && iconBitDepth < bestBitDepth))) { fUpdateBestFit = true; } } if (fUpdateBestFit) { bestWidth = EntryTemp.bWidth; bestHeight = EntryTemp.bHeight; bestImageOffset = EntryTemp.dwImageOffset; bestBytesInRes = EntryTemp.dwBytesInRes; bestBitDepth = iconBitDepth; } pbIconDirEntry += icondirEntrySize; } Debug.Assert(bestImageOffset >= 0 && (bestImageOffset + bestBytesInRes) <= iconData.Length, "Illegal offset/length for the Icon data"); if (bestImageOffset < 0 || (bestImageOffset + bestBytesInRes) > iconData.Length) { throw new ArgumentException(SR.GetString(SR.InvalidPictureType, "picture", "Icon")); } // See DevDivBugs 17509. Copying bytes into an aligned buffer if needed if ((bestImageOffset % IntPtr.Size) != 0) { // Beginning of icon's content is misaligned byte[] alignedBuffer = new byte[bestBytesInRes]; Array.Copy(this.iconData, bestImageOffset, alignedBuffer, 0, bestBytesInRes); fixed (byte *pbAlignedBuffer = alignedBuffer) { handle = SafeNativeMethods.CreateIconFromResourceEx(pbAlignedBuffer, bestBytesInRes, true, 0x00030000, 0, 0, 0); } } else { handle = SafeNativeMethods.CreateIconFromResourceEx(pbIconData + bestImageOffset, bestBytesInRes, true, 0x00030000, 0, 0, 0); } if (handle == IntPtr.Zero) { throw new Win32Exception(); } #if NEVER // old code SafeNativeMethods.ICONDIR *pIconDir = (SafeNativeMethods.ICONDIR *)pbIconData; if (pIconDir->idReserved != 0 || pIconDir->idType != 1 || pIconDir->idCount == 0) { throw new ArgumentException(SR.GetString(SR.InvalidPictureType, "picture", "Icon")); } SafeNativeMethods.ICONDIRENTRY *pIconDirEntry = &pIconDir->idEntries; SafeNativeMethods.ICONDIRENTRY *pBestFit = null; int bestBitDepth = 0; int icondirEntrySize = Marshal.SizeOf(typeof(SafeNativeMethods.ICONDIRENTRY)); Debug.Assert((icondirEntrySize * pIconDir->idCount) < iconData.Length, "Illegal number of ICONDIRENTRIES"); if ((icondirEntrySize * pIconDir->idCount) >= iconData.Length) { throw new ArgumentException(SR.GetString(SR.InvalidPictureType, "picture", "Icon")); } for (int i = 0; i < pIconDir->idCount; i++) { int iconBitDepth = pIconDirEntry->wPlanes * pIconDirEntry->wBitCount; if (iconBitDepth == 0) { if (pIconDirEntry->bColorCount == 0) { iconBitDepth = 16; } else { iconBitDepth = 8; if (pIconDirEntry->bColorCount < 0xFF) iconBitDepth = 4; if (pIconDirEntry->bColorCount < 0x10) iconBitDepth = 2; } } // Windows rules for specifing an icon: // // 1. The icon with the closest size match. // 2. For matching sizes, the image with the closest bit depth. // 3. If there is no color depth match, the icon with the closest color depth that does not exceed the display. // 4. If all icon color depth > display, lowest color depth is chosen. // 5. color depth of > 8bpp are all equal. // 6. Never choose an 8bpp icon on an 8bpp system. // if (pBestFit == null) { pBestFit = pIconDirEntry; bestBitDepth = iconBitDepth; } else { int bestDelta = Math.Abs(pBestFit->bWidth - width) + Math.Abs(pBestFit->bHeight - height); int thisDelta = Math.Abs(pIconDirEntry->bWidth - width) + Math.Abs(pIconDirEntry->bHeight - height); if (thisDelta < bestDelta) { pBestFit = pIconDirEntry; bestBitDepth = iconBitDepth; } else if (thisDelta == bestDelta && (iconBitDepth <= bitDepth && iconBitDepth > bestBitDepth || bestBitDepth > bitDepth && iconBitDepth < bestBitDepth)) { pBestFit = pIconDirEntry; bestBitDepth = iconBitDepth; } } pIconDirEntry++; } Debug.Assert(pBestFit->dwImageOffset >= 0 && (pBestFit->dwImageOffset + pBestFit->dwBytesInRes) <= iconData.Length, "Illegal offset/length for the Icon data"); if (pBestFit->dwImageOffset < 0 || (pBestFit->dwImageOffset + pBestFit->dwBytesInRes) > iconData.Length) { throw new ArgumentException(SR.GetString(SR.InvalidPictureType, "picture", "Icon")); } handle = SafeNativeMethods.CreateIconFromResourceEx(pbIconData + pBestFit->dwImageOffset, pBestFit->dwBytesInRes, true, 0x00030000, 0, 0, 0); if (handle == IntPtr.Zero) { throw new Win32Exception(); } #endif // WIN64 } } ////// /// Saves this image to the given output stream. /// public void Save(Stream outputStream) { if (iconData != null) { outputStream.Write(iconData, 0, iconData.Length); } else { // Ideally, we would pick apart the icon using // GetIconInfo, and then pull the individual bitmaps out, // converting them to DIBS and saving them into the file. // But, in the interest of simplicity, we just call to // OLE to do it for us. // SafeNativeMethods.IPicture picture; SafeNativeMethods.PICTDESC pictdesc = SafeNativeMethods.PICTDESC.CreateIconPICTDESC(Handle); Guid g = typeof(SafeNativeMethods.IPicture).GUID; picture = SafeNativeMethods.OleCreatePictureIndirect(pictdesc, ref g, false); if (picture != null) { int temp; try{ picture.SaveAsFile(new UnsafeNativeMethods.ComStreamFromDataStream(outputStream), -1, out temp); } finally{ Marshal.ReleaseComObject(picture); } } } } // SECREVIEW : Make sure the code calling this methods validates the source and target data. // // SAME CODE OR SIMILAR IN ImageList.cs private void CopyBitmapData(BitmapData sourceData, BitmapData targetData) { // do the actual copy int offsetSrc = 0; int offsetDest = 0; Debug.Assert(sourceData.Height == targetData.Height, "Unexpected height. How did this happen?"); for (int i = 0; i < Math.Min(sourceData.Height, targetData.Height); i++) { IntPtr srcPtr, destPtr; if (IntPtr.Size == 4) { srcPtr = new IntPtr(sourceData.Scan0.ToInt32() + offsetSrc); destPtr = new IntPtr(targetData.Scan0.ToInt32() + offsetDest); } else { srcPtr = new IntPtr(sourceData.Scan0.ToInt64() + offsetSrc); destPtr = new IntPtr(targetData.Scan0.ToInt64() + offsetDest); } // SECREVIEW : This is an usafe call, buffer overflow can occur. It is mitigated by copying as much data as the smaller // buffer can hold. The src and dst of the data needs to be validated by callers of this function. // UnsafeNativeMethods.CopyMemory(new HandleRef(this, destPtr), new HandleRef(this, srcPtr), Math.Abs(targetData.Stride)); offsetSrc += sourceData.Stride; offsetDest += targetData.Stride; } } private static bool BitmapHasAlpha(BitmapData bmpData) { bool hasAlpha = false; for (int i = 0; i < bmpData.Height; i++) { for (int j = 3; j < Math.Abs(bmpData.Stride); j += 4) { // stride here is fine since we know we're doing this on the whole image unsafe { byte* candidate = ((byte*)bmpData.Scan0.ToPointer()) + (i * bmpData.Stride) + j; if (*candidate != 0) { hasAlpha = true; goto Found; } } } } Found: return hasAlpha; } // [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts")] // supressing here since the call within the assert is safe public Bitmap ToBitmap() { Bitmap bitmap = null; if(iconData != null && bestBitDepth == 32) { // GDI+ doesnt handle 32 bpp icons with alpha properly // we load the icon ourself from the byte table bitmap = new Bitmap(Size.Width, Size.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); Debug.Assert(bestImageOffset >= 0 && (bestImageOffset + 40 + (Size.Width * Size.Height - 1)*4) <= iconData.Length, "Illegal offset/length for the Icon data"); unsafe { System.Drawing.Imaging.BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, Size.Width, Size.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); try { uint * pixelPtr = (uint*)bmpdata.Scan0.ToPointer(); // jumping the image header int newOffset = bestImageOffset + Marshal.SizeOf(typeof(SafeNativeMethods.BITMAPINFOHEADER)); // there is no color table that we need to skip since we're 32bpp int lineLength = Size.Width * 4; int width = Size.Width; for(int j=(Size.Height-1)*4;j>=0;j-=4) { Marshal.Copy(iconData, newOffset + j * width, (IntPtr)pixelPtr, lineLength); pixelPtr+=width; } // note: we ignore the mask that's available after the pixel table } finally { bitmap.UnlockBits(bmpdata); } } } else if(bestBitDepth == 0 || bestBitDepth == 32){ // we don't know or we are 32bpp for sure //we don't have any icon data, let's fish out the data from the handle that we got... // we have to fish out the data for this icon if the icon is a 32bpp icon SafeNativeMethods.ICONINFO info = new SafeNativeMethods.ICONINFO(); SafeNativeMethods.GetIconInfo(new HandleRef(this, handle), info); SafeNativeMethods.BITMAP bmp = new SafeNativeMethods.BITMAP(); try { if (info.hbmColor != IntPtr.Zero) { SafeNativeMethods.GetObject(new HandleRef(null, info.hbmColor), Marshal.SizeOf(typeof(SafeNativeMethods.BITMAP)), bmp); if(bmp.bmBitsPixel ==32) { Bitmap tmpBitmap = null; BitmapData bmpData = null; BitmapData targetData = null; // SECREVIEW : This assert is safe here, all data passed to unmanaged code is created by us. The scope is ok too, // most operations in it demand this permission. // IntSecurity.ObjectFromWin32Handle.Assert(); try { tmpBitmap = Bitmap.FromHbitmap(info.hbmColor); // bmpData = tmpBitmap.LockBits(new Rectangle(0,0, tmpBitmap.Width, tmpBitmap.Height), ImageLockMode.ReadOnly, tmpBitmap.PixelFormat); // we need do the following if the image has alpha because otherwise the image is fully transparent even though it has data if(BitmapHasAlpha(bmpData)) { bitmap = new Bitmap(bmpData.Width, bmpData.Height, PixelFormat.Format32bppArgb); targetData = bitmap.LockBits(new Rectangle(0, 0, bmpData.Width, bmpData.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); // SECREVIEW : both bmpData and targetData point to memory owned by this Icon object so the following call // is safe. // CopyBitmapData(bmpData, targetData); } } finally { CodeAccessPermission.RevertAssert(); if(tmpBitmap != null && bmpData != null) { tmpBitmap.UnlockBits(bmpData); } if(bitmap != null && targetData != null) { bitmap.UnlockBits(targetData); } } tmpBitmap.Dispose(); } } } finally { if(info.hbmColor != IntPtr.Zero) SafeNativeMethods.IntDeleteObject(new HandleRef(null, info.hbmColor)); if(info.hbmMask != IntPtr.Zero) SafeNativeMethods.IntDeleteObject(new HandleRef(null, info.hbmMask)); } } if(bitmap == null) { // last chance... all the other cases (ie non 32 bpp icons coming from a handle or from the bitmapData) // we have to do this rather than just return Bitmap.FromHIcon because // the bitmap returned from that, even though it's 32bpp, just paints where the mask allows it // seems like another GDI+ weirdness. might be interesting to investigate further. In the meantime // this looks like the right thing to do and is not more expansive that what was present before. Size size = Size; bitmap = new Bitmap(size.Width, size.Height); // initialized to transparent Graphics graphics = null; try { graphics = Graphics.FromImage(bitmap); // SECREVIEW : This assert is safe here, no user data is involved here. // IntSecurity.ObjectFromWin32Handle.Assert(); try{ using(Bitmap tmpBitmap = Bitmap.FromHicon(this.Handle)) { graphics.DrawImage(tmpBitmap, new Rectangle(0, 0, size.Width, size.Height)); } } catch(ArgumentException) { // GDI+ weirdness episode MMMCLXXXXIVI, sometime FromHicon crash with no real reason, // see VSWhidbey 518812 // backup plan is to just draw the image like we used to. // NOTE: FromHIcon is also where we have the buffer overrun // if width and height are mismatched Draw(graphics, new Rectangle(0, 0, size.Width, size.Height)); } finally{ CodeAccessPermission.RevertAssert(); } } finally { if (graphics != null) { graphics.Dispose(); } } // gpr: GDI+ is filling the surface with a sentinel color for GetDC, // but is not correctly cleaning it up again, so we have to for it. Color fakeTransparencyColor = Color.FromArgb(0x0d, 0x0b, 0x0c); bitmap.MakeTransparent(fakeTransparencyColor); } Debug.Assert(bitmap != null, "Bitmap cannot be null"); return bitmap; } ////// /// Retrieves a human readable string representing the cursor. /// public override string ToString() { return SR.GetString(SR.toStringIcon); } ////// /// ISerializable private implementation /// ///[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.SerializationFormatter)] void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) { if (iconData != null) { si.AddValue("IconData", iconData, typeof(byte[])); } else { MemoryStream stream = new MemoryStream(); Save(stream); si.AddValue("IconData", stream.ToArray(), typeof(byte[])); } si.AddValue("IconSize", iconSize, typeof(Size)); } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- TransportContext.cs
- ExpressionConverter.cs
- BindingExpressionBase.cs
- RowTypePropertyElement.cs
- HtmlTableCellCollection.cs
- _TransmitFileOverlappedAsyncResult.cs
- RelationshipNavigation.cs
- CallContext.cs
- ParallelTimeline.cs
- ClientSponsor.cs
- MatrixAnimationUsingPath.cs
- XmlSchemaProviderAttribute.cs
- UrlAuthFailureHandler.cs
- CryptographicAttribute.cs
- DataControlFieldCell.cs
- FigureParagraph.cs
- EdmEntityTypeAttribute.cs
- SqlDataRecord.cs
- Debug.cs
- DesignerDataView.cs
- TypeSystem.cs
- ZipIOZip64EndOfCentralDirectoryBlock.cs
- ColorAnimation.cs
- SqlLiftWhereClauses.cs
- SectionRecord.cs
- ThreadLocal.cs
- DigitShape.cs
- SupportedAddressingMode.cs
- SafeCoTaskMem.cs
- MimePart.cs
- AssociativeAggregationOperator.cs
- XPathDescendantIterator.cs
- RemoteCryptoDecryptRequest.cs
- SeparatorAutomationPeer.cs
- ReaderWriterLock.cs
- IndexingContentUnit.cs
- PbrsForward.cs
- ObjectPropertyMapping.cs
- SelectionWordBreaker.cs
- CqlLexerHelpers.cs
- RelatedCurrencyManager.cs
- translator.cs
- BooleanConverter.cs
- MarkupCompilePass2.cs
- XmlSortKeyAccumulator.cs
- SerializableAttribute.cs
- ViewEventArgs.cs
- LambdaCompiler.Statements.cs
- TypeUtils.cs
- ModuleConfigurationInfo.cs
- ToolConsole.cs
- SectionInformation.cs
- AsymmetricSecurityProtocol.cs
- EventLogPermissionEntryCollection.cs
- EncodingNLS.cs
- XmlSchemaCollection.cs
- ConfigurationStrings.cs
- PropertyValueChangedEvent.cs
- ProcessingInstructionAction.cs
- TemplatePropertyEntry.cs
- PrtTicket_Public_Simple.cs
- CompositeControl.cs
- XPathDescendantIterator.cs
- xml.cs
- MergeFilterQuery.cs
- Slider.cs
- NamedPermissionSet.cs
- AxisAngleRotation3D.cs
- DataGridViewCellToolTipTextNeededEventArgs.cs
- DefaultSerializationProviderAttribute.cs
- IRCollection.cs
- WebGetAttribute.cs
- Activity.cs
- HeaderedContentControl.cs
- TemplateControlBuildProvider.cs
- XmlCustomFormatter.cs
- GroupAggregateExpr.cs
- RemoteAsymmetricSignatureFormatter.cs
- RenderData.cs
- EpmContentSerializer.cs
- DependencySource.cs
- Stackframe.cs
- SystemParameters.cs
- SQLMoney.cs
- Journal.cs
- ReadOnlyDataSourceView.cs
- WindowsTokenRoleProvider.cs
- DeploymentSectionCache.cs
- MemberMaps.cs
- ApplicationFileCodeDomTreeGenerator.cs
- Tuple.cs
- IisTraceListener.cs
- Canvas.cs
- Input.cs
- LifetimeMonitor.cs
- XmlNullResolver.cs
- DPCustomTypeDescriptor.cs
- ClientScriptManagerWrapper.cs
- FixedTextSelectionProcessor.cs
- Int32.cs