IconHelper.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / MS / Internal / AppModel / IconHelper.cs / 1 / IconHelper.cs

                            //---------------------------------------------------------------------------- 
//
// File: IconHelper.cs
//
// Description: Static internal class implements utility functions for icon 
//              implementation for the Window class.
// 
// Created: 07/27/05 
//
// Copyright (C) 2001 by Microsoft Corporation.  All rights reserved. 
//
//---------------------------------------------------------------------------

using System; 
using System.Security;
using System.Security.Permissions; 
using System.Diagnostics; 
using System.Collections.ObjectModel;
using System.Runtime.InteropServices; 
using System.ComponentModel;

using System.Windows;
using System.Windows.Interop; 
using System.Windows.Media.Imaging;
using System.Windows.Media; 
 
using MS.Internal;
using MS.Internal.PresentationFramework;                   // SecurityHelper 
using MS.Win32;

namespace MS.Internal.AppModel
{ 
    internal static class IconHelper
    { 
        ///  
        ///     Critical: This code elevates to unmanaged Code permission
        /// TreatAsSafe: There is a demand here 
        /// 
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        internal static void GetDefaultIconHandles(object callingObj, ref NativeMethods.IconHandle largeIconHandle, ref NativeMethods.IconHandle smallIconHandle) 
        {
            SecurityHelper.DemandUIWindowPermission(); 
            IntPtr mainModuleHandle = IntPtr.Zero; 
            string iconModuleFile = String.Empty;
            NativeMethods.IconHandle[] hLargeIcon = new NativeMethods.IconHandle[1]{new NativeMethods.IconHandle()}; 
            NativeMethods.IconHandle[] hSmallIcon = new NativeMethods.IconHandle[1]{new NativeMethods.IconHandle()};

            // In case we don't get any icons from the exe, we don't want to return the input values
            // as they could be mistaken to have come from the exe, thus, setting to default values here 
            largeIconHandle = hLargeIcon[0];
            smallIconHandle = hLargeIcon[0]; 
 
            // Get the default icon from the application first.
            // We use GetModuleHandle(null) to get the handle to the 
            // module that created the process.  Then we call
            // GetModuleFileName to get the file name of that module

            // GetModuleHandle(null) get's the handle of module that created 
            // the running process.  GetModuleHandle does not increase the
            // refCount of the module and hence we don't need to call 
            // FreeLibrary later.  However, this means that in multithreaded 
            // application some other thread can free the module and unload
                // from memory rendering our module handle obsolete.  But, this is 
            // not the case here and we're getting the handle to the exe.
            mainModuleHandle =  UnsafeNativeMethods.GetModuleHandle(null);

            if (mainModuleHandle != IntPtr.Zero) 
            {
                // find the path and file name of the module that created the process 
                // We start with MAX_PATH as the initial buffer length.  If it's UNC path 
                // then it could be pretty big that's why we do it in a while loop with
                // double the size until we retrieve the entire path. 
                // NOTE:  I tried to give a UNC path greater than MAX_PATH and mscorlib
                // threw FileIOLoadException even before hitting any Avalon code.  Seems
                // like greater than MAX_PATH is not currently supported by CLR.  However,
                // since GetModuleFileName(Ex) documentation mentions that it can return 
                // greater than MAX_PATH so we are doing the do/while loop below.
 
                System.Text.StringBuilder sb; 
                int fileNameCopiedLength = 0;
                int bufferLength = 0; 
                do
                {
                    bufferLength = (bufferLength == 0 ? NativeMethods.MAX_PATH : bufferLength * 2);
                    sb = new System.Text.StringBuilder( bufferLength ); 
                    // get module's file name
                    fileNameCopiedLength = UnsafeNativeMethods.GetModuleFileName( 
                        new HandleRef(callingObj, mainModuleHandle),      // handle to the module to retrieve the file name of 
                        sb,                    // buffer to copy the file name
                        bufferLength           // size of the buffer 
                        );

                    if (fileNameCopiedLength == 0) // error case
                    { 
                        throw new Win32Exception();
                    } 
 
                } while((fileNameCopiedLength == bufferLength) || (fileNameCopiedLength == (bufferLength - 1)));
                // GetModuleFileName(Ex) documentation is unclear regarding whether the returned value 
                // includes the null terminator or not thus we check both borderline cases.

                iconModuleFile = sb.ToString();
            } 

 
            // The following code is run only if we successfully retrieved the file name to retrieve the 
            // icon from.  If there was an exception in the code above the method would exit after
            // executing the finally block. 
            //
            // ExtractIconEx is not necessiated in an if statement here, but doing so
            // keeps PreSharp happy since we are doing something with the return value.
            if ((iconModuleFile != String.Empty) && 
                (UnsafeNativeMethods.ExtractIconEx(iconModuleFile, 0, ref hLargeIcon[0], ref hSmallIcon[0], 1) <= 0))
            { 
                return; 
            }
 
            largeIconHandle = hLargeIcon[0];
            smallIconHandle = hSmallIcon[0];
        }
 
        // Inputs:
        // callingObj -- to be used in HandleRef 
        // bf -- BitmapFrame to be used to create HICONs from 
        // In/Out/ref:
        // largeIconHandle -- to return the large HICON created 
        // smallIconHandle -- to return the small HICON created
        /// 
        ///     Critical: Since it calls CreateIconHandleFromBitmapFrame
        ///     TAS:      Since it creates icons with known h/w i.e. IconWidth/Height or SmallIconWidth/Height 
        /// 
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        internal static void GetIconHandlesFromBitmapFrame(object callingObj, BitmapFrame bf, ref NativeMethods.IconHandle largeIconHandle, ref NativeMethods.IconHandle smallIconHandle)
        { 
            Invariant.Assert(bf.Decoder != null, "_icon.Decoder should never be null");
            Invariant.Assert(bf.Decoder.Frames != null, "_icon.Decoder.Frames should never be null");

            // sysBitDepth is used to select the correct frame among the frames that came from 
            // the ico file
            int sysBitDepth = IconHelper.GetSystemBitDepth(callingObj); 
            int largeIconIndex = IconHelper.GetBestMatch(bf.Decoder.Frames, sysBitDepth, (int)SystemParameters.IconWidth, (int)SystemParameters.IconHeight); 
            int smallIconIndex = IconHelper.GetBestMatch(bf.Decoder.Frames, sysBitDepth, (int)SystemParameters.SmallIconWidth, (int)SystemParameters.SmallIconHeight);
 
            largeIconHandle = IconHelper.CreateIconHandleFromBitmapFrame(bf.Decoder.Frames[largeIconIndex]);
            smallIconHandle = IconHelper.CreateIconHandleFromBitmapFrame(bf.Decoder.Frames[smallIconIndex]);
        }
 
        /// 
        ///     Critical: Since it calls CreateIconCursor which is SecurityCritical.  CreateIconCursor is SecurityCritical b/c 
        ///               it creates bitmaps with the input w/h etc.  That width/height is passed by this method using 
        ///               sourceBitmapFrame.Pixel[Width/Height] and is not guarded.
        ///  
        /// 
        //
        //  Creates and HICON from a bitmap frame
        [SecurityCritical] 
        private static NativeMethods.IconHandle CreateIconHandleFromBitmapFrame(BitmapFrame sourceBitmapFrame)
        { 
            Invariant.Assert(sourceBitmapFrame != null, "sourceBitmapFrame cannot be null here"); 

            BitmapSource bitmapSource = sourceBitmapFrame; 

            PixelFormat reqPixelFormat = PixelFormats.Bgra32;
            if (bitmapSource.Format != reqPixelFormat)
            { 
                bitmapSource = new FormatConvertedBitmap(bitmapSource, reqPixelFormat, null, 0.0);
            } 
 
            // data used by CopyPixels
            int w = bitmapSource.PixelWidth; 
            int h = bitmapSource.PixelHeight;
            int bpp = bitmapSource.Format.BitsPerPixel;
            // ensuring it is in 4 byte increments since we're dealing
            // with ARGB fromat 
            int stride = (bpp * w + 31) / 32 * 4;
            int sizeCopyPixels = stride * h; 
            byte[] xor = new byte[sizeCopyPixels]; 
            bitmapSource.CopyPixels(xor, stride, 0);
 
            return CreateIconCursor(xor, w, h, 0, 0, true);
        }

        // Creates a 32 bit per pixel Icon or cursor.  This code is moved from framework\ms\internal\ink\pencursormanager.cs 
        /// 
        ///     Critical: Critical as this code create a DIB section and writes data to it 
        ///  
        [SecurityCritical]
        internal static NativeMethods.IconHandle CreateIconCursor( 
            byte[] colorArray,
            int width,
            int height,
            int xHotspot, 
            int yHotspot,
            bool isIcon) 
        { 
            NativeMethods.IconHandle returnHandle = new NativeMethods.IconHandle();
 
            //   1. We are going to generate a WIN32 color bitmap which represents the color cursor.
            //   2. Then we need to create a monochrome bitmap which is used as the cursor mask.
            //   3. At last we create a WIN32 HICON from the above two bitmaps
            NativeMethods.BitmapHandle colorBitmap = null; 
            NativeMethods.BitmapHandle maskBitmap = null;
 
            try 
            {
                // 1) Create the color bitmap using colorArray 
                // Fill in the header information
                NativeMethods.BITMAPINFO bi = new NativeMethods.BITMAPINFO(
                                                    width,      // width
                                                    -height,    // A negative value indicates the bitmap is top-down DIB 
                                                    32          // biBitCount
                                                    ); 
                bi.bmiHeader_biCompression = NativeMethods.BI_RGB; 

                IntPtr bits = IntPtr.Zero; 
                colorBitmap = MS.Win32.UnsafeNativeMethods.CreateDIBSection(
                                        new HandleRef(null, IntPtr.Zero),   // A device context. Pass null in if no DIB_PAL_COLORS is used.
                                        ref bi,                             // A BITMAPINFO structure which specifies the dimensions and colors.
                                        NativeMethods.DIB_RGB_COLORS,       // Specifies the type of data contained in the bmiColors array member of the BITMAPINFO structure 
                                        ref bits,                           // An out Pointer to a variable that receives a pointer to the location of the DIB bit values
                                        IntPtr.Zero,                        // Handle to a file-mapping object that the function will use to create the DIB. This parameter can be null. 
                                        0                                   // dwOffset. This value is ignored if hSection is NULL 
                                        );
 
                if ( colorBitmap.IsInvalid || bits == IntPtr.Zero)
                {
                    // Note we will release the GDI resources in the finally block.
                    return new NativeMethods.IconHandle(IntPtr.Zero); 
                }
 
                // Copy the color bits to the win32 bitmap 
                Marshal.Copy(colorArray, 0, bits, colorArray.Length);
 

                // 2) Now create the mask bitmap which is monochrome
                byte[] maskArray = GenerateMaskArray(width, height, colorArray);
                Invariant.Assert(maskArray != null); 

                maskBitmap = UnsafeNativeMethods.CreateBitmap(width, height, 1, 1, maskArray); 
                if ( maskBitmap.IsInvalid ) 
                {
                    // Note we will release the GDI resources in the finally block. 
                    return new NativeMethods.IconHandle(IntPtr.Zero);
                }

                // Now create HICON from two bitmaps. 
                NativeMethods.ICONINFO iconInfo = new NativeMethods.ICONINFO();
                iconInfo.fIcon = isIcon;            // fIcon == ture means creating an Icon, otherwise Cursor 
                iconInfo.xHotspot = xHotspot; 
                iconInfo.yHotspot = yHotspot;
                iconInfo.hbmMask = maskBitmap; 
                iconInfo.hbmColor = colorBitmap;

                returnHandle = UnsafeNativeMethods.CreateIconIndirect(iconInfo);
                if ( returnHandle.IsInvalid ) 
                {
                    // Note we will release the GDI resources in the finally block. 
                    return new NativeMethods.IconHandle(IntPtr.Zero); 
                }
            } 
            finally
            {
                if (colorBitmap != null)
                { 
                    colorBitmap.Dispose();
                    colorBitmap = null; 
                } 

                if (maskBitmap != null) 
                {
                    maskBitmap.Dispose();
                    maskBitmap = null;
                } 
            }
 
            return returnHandle; 
        }
 
        // generates the mask array for the input colorArray.
        // The mask array is 1 bpp
        private static byte[] GenerateMaskArray(int width, int height, byte[] colorArray)
        { 
            int nCount = width * height;
 
            // NOTICE-2005/04/26-[....], 
            // Check out the notes in CreateBitmap in MSDN. The scan line has to be aliged to WORD.
            int bytesPerScanLine = AlignToBytes(width, 2) / 8; 

            byte[] bitsMask = new byte[bytesPerScanLine * height];

            // We are scaning all pixels in color bitmap. 
            // If the alpha value is 0, we should set the corresponding mask bit to 1. So the pixel will show
            // the screen pixel. Otherwise, we should set the mask bit to 0 which causes the cursor to display 
            // the color bitmap pixel. 
            for ( int i = 0; i < nCount; i++ )
            { 
                // Get the i-th pixel position (hPos, vPos)
                int hPos = i % width;
                int vPos = i / width;
 
                // For each byte in 2-bit color bitmap, the lowest the bit represents the right-most display pixel.
                // For example the bollow mask - 
                //    1 1 1 0 0 0 0 1 
                //    ^             ^
                //  offsetBit = 0x80   offsetBit = 0x01 
                int byteIndex = hPos / 8;
                byte offsetBit = (byte)( 0x80 >> ( hPos % 8 ) );

                // Now we turn the mask on or off accordingly. 
                if ( colorArray[i * 4 + 3] /* Alpha value since it's in Args32 Format */ == 0x00 )
                { 
                    // Set the mask bit to 1. 
                    bitsMask[byteIndex + bytesPerScanLine * vPos] |= (byte)offsetBit;
                } 
                else
                {
                    // Reset the mask bit to 0
                    bitsMask[byteIndex + bytesPerScanLine * vPos] &= (byte)( ~offsetBit ); 
                }
 
                // Since the scan line of the mask bitmap has to be aligned to word. We have set all padding bits to 1. 
                // So the extra pixel can be seen through.
                if ( hPos == width - 1 && width == 8 ) 
                {
                    bitsMask[1 + bytesPerScanLine * vPos] = 0xff;
                }
            } 

            return bitsMask; 
        } 

        ///  
        /// Calculate the bits count aligned to N-Byte based on the input count
        /// 
        /// The original value
        /// N-Byte 
        /// the nearest bit count which is aligned to N-Byte
        internal static int AlignToBytes(double original, int nBytesCount) 
        { 
            Debug.Assert(nBytesCount > 0, "The N-Byte has to be greater than 0!");
 
            int nBitsCount = 8 << (nBytesCount - 1);
            return (((int)Math.Ceiling(original) + (nBitsCount - 1)) / nBitsCount) * nBitsCount;
        }
 

        // returns the current system bit depth -- incdicates the # of 
        // colors that can be shown. 
        //
        // Code taken from WinForms implementation of selecting 
        // which icon frame to use
        /// 
        ///     Critical:       Calls GetDC, ReleaseDC and GetDeviceCaps that are marked SecurityCritical
        ///     TreatAsSafe:    It is okay to return the current system bit depth 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private static int GetSystemBitDepth(object callerObj) 
        {
            int sysBitDepth = 0; 
            HandleRef handleRefDC;
            HandleRef handleRefZero = new HandleRef(callerObj, IntPtr.Zero);

            IntPtr dc = UnsafeNativeMethods.GetDC(handleRefZero); 

            handleRefDC = new HandleRef(callerObj, dc); 
            sysBitDepth = UnsafeNativeMethods.GetDeviceCaps(handleRefDC, NativeMethods.BITSPIXEL); 
            sysBitDepth *= UnsafeNativeMethods.GetDeviceCaps(handleRefDC, NativeMethods.PLANES);
 
            UnsafeNativeMethods.ReleaseDC(handleRefZero, handleRefDC);

            return sysBitDepth;
        } 

        // returns the index of the frame that best matches the given width, height and the current 
        // system bit depth. 
        //
        // This code is taken from Winforms implementation of choosing the correct frame based on the 
        // requirements.
        //
        //  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.
        //
        /// 
        ///     Critical:       Calls LinkDemand property BitsPerPixel 
        ///     TreatAsSafe:    The bits per pixel info is not revealed/returned
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        private static int GetBestMatch(ReadOnlyCollection frames, int sysBitDepth, int width, int height)
        { 
            Invariant.Assert(width != 0, "input param width should not be zero");
            Invariant.Assert(height != 0, "input param height should not be zero");

            // If the _sysBitDepth 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 (sysBitDepth == 8)
            { 
                sysBitDepth = 4;
            }

 
            int bestWidth = 0;
            int bestHeight = 0; 
            int bestBitDepth = 0; 
            int bestIndex = 0;
            int bestDelta = 0; 

            bool isBitmapIconDecoder = frames.Count > 0 && frames[0].Decoder is IconBitmapDecoder;

            // iterate each frame to see if this one is a better match than the 
            // previous best match
            for (int i = 0; i < frames.Count; i++) 
            { 
                bool updateBestFit = false;
                int currentWidth = frames[i].PixelWidth; 
                int currentHeight = frames[i].PixelHeight;
                int currentIconBitDepth = 0;

 
                // determine the bit-depth (# of colors) in the
                // current frame 
                // 
                // if the icon is palettized, Format.BitsPerPixel gives
                // the # of bits required to index into the palette (thus, 
                // the # of colors in the palette).  If it is a true
                // color icon, it gives the # of bits required to support
                // true colors.
                // For icons, get the Format from the Thumbnail rather than from the 
                // BitmapFrame directly because the unmanaged icon decoder
                // converts every icon to 32-bit. Thumbnail.Format.BitsPerPixel 
                // will give us the original bit depth. 

                currentIconBitDepth = isBitmapIconDecoder ? frames[i].Thumbnail.Format.BitsPerPixel : frames[i].Format.BitsPerPixel; 

                // it looks like if nothing is specified at this point, bpp is 8...
                // straight from WinForm and Win32
                if (currentIconBitDepth == 0) 
                {
                    currentIconBitDepth = 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. 
                //
 
                // for the first index, don't have anything to compare against so pick it
                if (i == 0)
                {
                    updateBestFit = true; 
                }
                else 
                { 
                    int thisDelta = Math.Abs(currentWidth - width) + Math.Abs(currentHeight - height);
 
                    if ((thisDelta < bestDelta) ||
                        (thisDelta == bestDelta && (currentIconBitDepth <= sysBitDepth && currentIconBitDepth > bestBitDepth || bestBitDepth > sysBitDepth && currentIconBitDepth < bestBitDepth)))
                    {
                        updateBestFit = true; 
                    }
                } 
 
                if (updateBestFit)
                { 
                    bestWidth = currentWidth;
                    bestHeight = currentHeight;
                    bestBitDepth = currentIconBitDepth;
                    bestIndex = i; 
                    bestDelta = Math.Abs(currentWidth - width) + Math.Abs(currentHeight - height);
                } 
            } 

            return bestIndex; 
        }
    }
}
 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK