File: winforms\Managed\System\WinForms\VisualStyles\VisualStyleRenderer.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="visualStyleRenderer.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA905:SystemAndMicrosoftNamespacesRequireApproval", Scope="namespace", Target="System.Windows.Forms.VisualStyles")]
 
namespace System.Windows.Forms.VisualStyles {
    using System;
    using System.Drawing;
    using System.Windows.Forms.Internal;
    using System.Text;
    using System.Windows.Forms;
    using System.Collections;
    using System.ComponentModel;
    using System.Globalization;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using Microsoft.Win32;
    using System.Security;
    using System.Security.Permissions;
 
    /// <include file='doc\visualStyleRenderer.uex' path='docs/doc[@for="visualStyleRenderer"]/*' />
    /// <devdoc>
    ///    <para>
    ///       This class provides full feature parity with UxTheme API.
    ///    </para>
    /// </devdoc>
    public sealed class VisualStyleRenderer {
        private const TextFormatFlags AllGraphicsProperties = TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.PreserveGraphicsTranslateTransform;
 
        internal const int EdgeAdjust = 0x2000; //used with Edges in VisualStyleRenderer.DrawThemeEdge
        private string _class;
        private int part;
        private int state;
        private int lastHResult = 0;
        private static int numberOfPossibleClasses = VisualStyleElement.Count; //used as size for themeHandles
        
        [ThreadStatic]
        private static Hashtable themeHandles = null; //per-thread cache of ThemeHandle objects.
        [ThreadStatic]
        private static long threadCacheVersion = 0; 
 
        private static long globalCacheVersion = 0;
 
        static VisualStyleRenderer() {
            SystemEvents.UserPreferenceChanging += new UserPreferenceChangingEventHandler(OnUserPreferenceChanging);
        }
 
        /// <summary>
        /// Check if visual styles is supported for client area.
        /// </summary>
        private static bool AreClientAreaVisualStylesSupported {
            get {
                return (VisualStyleInformation.IsEnabledByUser &&
                   ((Application.VisualStyleState & VisualStyleState.ClientAreaEnabled) == VisualStyleState.ClientAreaEnabled));
            }
        }
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.IsSupported"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Returns true if visual styles are 1) supported by the OS 2) enabled in the client area 
        ///       and 3) currently applied to this application. Otherwise, it returns false. Note that
        ///       if it returns false, attempting to instantiate/use objects of this class 
        ///       will result in exceptions being thrown.
        ///    </para>
        /// </devdoc>
        public static bool IsSupported {
            get {
                bool supported = AreClientAreaVisualStylesSupported;
                if (supported) {
                    // VSWhidbey #171915: In some cases, this check isn't enough, since the theme handle creation
                    // could fail for some other reason. Try creating a theme handle here - if successful, return true,
                    // else return false.
                    IntPtr hTheme = GetHandle("BUTTON", false); //Button is an arbitrary choice.
                    supported = (hTheme != IntPtr.Zero);
                }
 
                return supported;
 
            }
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.IsCombinationDefined"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Returns true if the element is defined by the current visual style, else false. 
        ///       Note: 
        ///          1) Throws an exception if IsSupported is false, since it is illegal to call it in that case.
        ///          2) The underlying API does not validate states. So if you pass in invalid state values,
        ///             we might still return true. When you use an invalid state to render, you get the default
        ///             state instead.
        ///    </para>
        /// </devdoc>
        public static bool IsElementDefined(VisualStyleElement element) {
            if (element == null) {
                throw new ArgumentNullException("element");
            }
 
            return IsCombinationDefined(element.ClassName, element.Part);
        }
 
        internal static bool IsCombinationDefined(string className, int part) {
            bool returnVal = false;
 
            if (!IsSupported) {
                if (!VisualStyleInformation.IsEnabledByUser) {
                    throw new InvalidOperationException(SR.GetString(SR.VisualStyleNotActive));
                }
                else {
                    throw new InvalidOperationException(SR.GetString(SR.VisualStylesDisabledInClientArea));
                }
            }
 
            if (className == null) {
                throw new ArgumentNullException("className");
            }
 
            IntPtr hTheme = GetHandle(className, false);
 
            if (hTheme != IntPtr.Zero) {
                // IsThemePartDefined doesn't work for part = 0, although there are valid parts numbered 0. We
                // allow these explicitly here.
                if (part == 0) {
                    returnVal = true;
                }
                else {
                    returnVal = SafeNativeMethods.IsThemePartDefined(new HandleRef(null, hTheme), part, 0);
                }
            }
 
            //if the combo isn't defined, check the validity of our theme handle cache
            if (!returnVal) {
                using (ThemeHandle tHandle = ThemeHandle.Create(className, false)) {
                    if (tHandle != null) {
                        returnVal = SafeNativeMethods.IsThemePartDefined(new HandleRef(null, tHandle.NativeHandle), part, 0);
                    }
 
                    //if we did, in fact get a new correct theme handle, our cache is out of date -- update it now.
                    if (returnVal) {
                        RefreshCache();
                    }
                }
            }
 
            return returnVal;
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.VisualStyleRenderer"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Constructor takes a VisualStyleElement.
        ///    </para>
        /// </devdoc>
        public VisualStyleRenderer(VisualStyleElement element) : this(element.ClassName, element.Part, element.State) {
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.VisualStyleRenderer1"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Constructor takes weakly typed parameters - left for extensibility (using classes, parts or states
        ///       not defined in the VisualStyleElement class.)
        ///    </para>
        /// </devdoc>
        public VisualStyleRenderer(string className, int part, int state) {
            if (!IsCombinationDefined(className, part)) { //internally this call takes care of IsSupported. 
                throw new ArgumentException(SR.GetString(SR.VisualStylesInvalidCombination));
            }
 
            this._class = className;
            this.part = part;
            this.state = state;
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.Class"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Returns the current _class. Use SetParameters to set.
        ///    </para>
        /// </devdoc>
        public string Class {
            get {
                return _class;
            }
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.Part"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Returns the current part. Use SetParameters to set.
        ///    </para>
        /// </devdoc>
        public int Part {
            get {
                return part;
            }
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.State"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Returns the current state. Use SetParameters to set.
        ///    </para>
        /// </devdoc>
        public int State {
            get {
                return state;
            }
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.Handle"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Returns the underlying HTheme handle.
        ///       NOTE: The handle gets invalidated when the theme changes or the user disables theming. When that
        ///             happens, the user should requery this property to get the correct handle. To know when the
        ///             theme changed, hook on to SystemEvents.UserPreferenceChanged and look for ThemeChanged 
        ///             category.
        ///    </para>
        /// </devdoc>
        public IntPtr Handle {
            get {
                if (!IsSupported) {
                    if (!VisualStyleInformation.IsEnabledByUser) {
                        throw new InvalidOperationException(SR.GetString(SR.VisualStyleNotActive)); 
                    }
                    else {
                        throw new InvalidOperationException(SR.GetString(SR.VisualStylesDisabledInClientArea));
                    }
                }
        
                return GetHandle(_class);
            }
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.SetParameters"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Used to set a new VisualStyleElement on this VisualStyleRenderer instance.
        ///    </para>
        /// </devdoc>
        public void SetParameters(VisualStyleElement element) {
            if (element == null) {
                throw new ArgumentNullException("element");
            }
 
            SetParameters(element.ClassName, element.Part, element.State);
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.SetParameters"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Used to set the _class, part and state that the VisualStyleRenderer object references. 
        ///       These parameters cannot be set individually. 
        ///       This method is present for extensibility.
        ///    </para>
        /// </devdoc>
        public void SetParameters(string className, int part, int state) {
            if (!IsCombinationDefined(className, part)) { //internally this call takes care of IsSupported.
                throw new ArgumentException(SR.GetString(SR.VisualStylesInvalidCombination)); 
            }
 
            this._class = className;
            this.part = part;
            this.state = state;
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.DrawBackground"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public void DrawBackground(IDeviceContext dc, Rectangle bounds) {
            DrawBackground(dc, bounds, IntPtr.Zero);
        }
 
        internal void DrawBackground(IDeviceContext dc, Rectangle bounds, IntPtr hWnd) {
            if (dc == null) {
                throw new ArgumentNullException("dc");
            }
            if (bounds.Width < 0 || bounds.Height < 0) {
                return;
            }
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ){
                HandleRef hdc = new HandleRef(wgr, wgr.WindowsGraphics.DeviceContext.Hdc);
                if (IntPtr.Zero != hWnd) {
                    using (ThemeHandle hTheme = ThemeHandle.Create(_class, true, new HandleRef(null, hWnd))) {
                        lastHResult = SafeNativeMethods.DrawThemeBackground(new HandleRef(this, hTheme.NativeHandle), hdc, part, state, new NativeMethods.COMRECT(bounds), null);
                    }
                } 
                else {
                    lastHResult = SafeNativeMethods.DrawThemeBackground(new HandleRef(this, Handle), hdc, part, state, new NativeMethods.COMRECT(bounds), null);
                }
            }
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.DrawBackground1"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public void DrawBackground(IDeviceContext dc, Rectangle bounds, Rectangle clipRectangle) {
            DrawBackground(dc, bounds, clipRectangle, IntPtr.Zero);
        }
 
        internal void DrawBackground(IDeviceContext dc, Rectangle bounds, Rectangle clipRectangle, IntPtr hWnd) {
            if( dc == null ){
                throw new ArgumentNullException("dc");
            }
            if (bounds.Width < 0 || bounds.Height < 0) {
                return;
            }
            if (clipRectangle.Width < 0 || clipRectangle.Height < 0) {
                return;
            }
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                if (IntPtr.Zero != hWnd) {
                    using (ThemeHandle hTheme = ThemeHandle.Create(_class, true, new HandleRef(null, hWnd))) {
                        lastHResult = SafeNativeMethods.DrawThemeBackground(new HandleRef(this, hTheme.NativeHandle), hdc, part, state, new NativeMethods.COMRECT(bounds), new NativeMethods.COMRECT(clipRectangle));
                    }
                } 
                else {
                    lastHResult = SafeNativeMethods.DrawThemeBackground(new HandleRef(this, Handle), hdc, part, state, new NativeMethods.COMRECT(bounds), new NativeMethods.COMRECT(clipRectangle));
                }
            }
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.DrawEdge"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public Rectangle DrawEdge(IDeviceContext dc, Rectangle bounds, Edges edges, EdgeStyle style, EdgeEffects effects) {
            if (dc == null) {
                throw new ArgumentNullException("dc");
            }
 
            if (!ClientUtils.IsEnumValid_Masked(edges, (int)edges,(UInt32)(Edges.Left | Edges.Top | Edges.Right | Edges.Bottom | Edges.Diagonal))) {
                throw new InvalidEnumArgumentException("edges", (int)edges, typeof(Edges));
            }
 
            if (!ClientUtils.IsEnumValid_NotSequential(style, (int)style, (int)EdgeStyle.Raised,(int)EdgeStyle.Sunken,(int)EdgeStyle.Etched,(int)EdgeStyle.Bump )) {
                throw new InvalidEnumArgumentException("style", (int)style, typeof(EdgeStyle));
            }
 
            if (!ClientUtils.IsEnumValid_Masked(effects, (int)effects, (UInt32)(EdgeEffects.FillInterior | EdgeEffects.Flat | EdgeEffects.Soft | EdgeEffects.Mono))) {
                throw new InvalidEnumArgumentException("effects", (int)effects, typeof(EdgeEffects));
            }
 
            NativeMethods.COMRECT rect = new NativeMethods.COMRECT();
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                lastHResult = SafeNativeMethods.DrawThemeEdge( new HandleRef( this, Handle ), hdc, part, state, new NativeMethods.COMRECT( bounds ), (int) style, (int) edges | (int) effects | EdgeAdjust, rect );
            }
 
            return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom);
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.DrawImage"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///       This method uses Graphics.DrawImage as a backup if themed drawing does not work.
        ///    </para>
        /// </devdoc>
        public void DrawImage(Graphics g, Rectangle bounds, Image image) {
            if (g == null) {
                throw new ArgumentNullException("g");
            }
 
            if (image == null) {
                throw new ArgumentNullException("image");
            }
 
            if (bounds.Width < 0 || bounds.Height < 0) {
                return;
            }
 
            g.DrawImage(image, bounds);
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.DrawImage1"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.] 
        ///       This method uses Graphics.DrawImage as a backup if themed drawing does not work.
        ///    </para>
        /// </devdoc>
        public void DrawImage(Graphics g, Rectangle bounds, ImageList imageList, int imageIndex) {
            if (g == null) {
                throw new ArgumentNullException("g");
            }
 
            if (imageList == null) {
                throw new ArgumentNullException("imageList");
            }
            
            if (imageIndex < 0 || imageIndex >= imageList.Images.Count) {
                throw new ArgumentOutOfRangeException("imageIndex", SR.GetString(SR.InvalidArgument, "imageIndex", imageIndex.ToString(CultureInfo.CurrentCulture)));
            }
 
            if (bounds.Width < 0 || bounds.Height < 0) {
                return;
            }
 
            // VSWhidbey #282742: DrawThemeIcon currently seems to do nothing, but still return S_OK. As a workaround,
            // we call DrawImage on the graphics object itself for now. A bug has been opened in Windows OS Bugs on this.
 
            //int returnVal = NativeMethods.S_FALSE;
            //using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
            //    HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
            //    returnVal = SafeNativeMethods.DrawThemeIcon( new HandleRef( this, Handle ), hdc, part, state, new NativeMethods.COMRECT( bounds ), new HandleRef( this, imageList.Handle ), imageIndex );
            //}
 
            //if (returnVal != NativeMethods.S_OK) {
            g.DrawImage(imageList.Images[imageIndex], bounds);
            //}
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.DrawParentBackground"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Given a graphics object and bounds to draw in, this method effectively asks the passed in 
        ///       control's parent to draw itself in there (it sends WM_ERASEBKGND & WM_PRINTCLIENT messages
        ///       to the parent).
        ///    </para>
        /// </devdoc>
        public void DrawParentBackground(IDeviceContext dc, Rectangle bounds, Control childControl) {
            if (dc == null) {
                throw new ArgumentNullException("dc");
            }
 
            if (childControl == null) {
                throw new ArgumentNullException("childControl");
            }
 
            if (bounds.Width < 0 || bounds.Height < 0) {
                return;
            }
 
            if (childControl.Handle != IntPtr.Zero) {
                using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                    HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                    lastHResult = SafeNativeMethods.DrawThemeParentBackground( new HandleRef( this, childControl.Handle ), hdc, new NativeMethods.COMRECT( bounds ) );
                }
            }
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.DrawText"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public void DrawText(IDeviceContext dc, Rectangle bounds, string textToDraw) {
            DrawText(dc, bounds, textToDraw, false);
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.DrawText1"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public void DrawText(IDeviceContext dc, Rectangle bounds, string textToDraw, bool drawDisabled) {
            DrawText(dc, bounds, textToDraw, drawDisabled, TextFormatFlags.HorizontalCenter); 
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.DrawText2"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public void DrawText(IDeviceContext dc, Rectangle bounds, string textToDraw, bool drawDisabled, TextFormatFlags flags) {
            if( dc == null ){
                throw new ArgumentNullException("dc");
            }
 
            if (bounds.Width < 0 || bounds.Height < 0) {
                return;
            }
            
            int disableFlag = drawDisabled?0x1:0;
            
            if (!String.IsNullOrEmpty(textToDraw)) {
                using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                    HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                    lastHResult = SafeNativeMethods.DrawThemeText( new HandleRef( this, Handle ), hdc, part, state, textToDraw, textToDraw.Length, (int) flags, disableFlag, new NativeMethods.COMRECT( bounds ) );
                }
            }
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetBackgroundContentRectangle"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public Rectangle GetBackgroundContentRectangle(IDeviceContext dc, Rectangle bounds) {
            if( dc == null ){
                throw new ArgumentNullException("dc");
            }
            if (bounds.Width < 0 || bounds.Height < 0) {
                return Rectangle.Empty;
            }
            
            NativeMethods.COMRECT rect = new NativeMethods.COMRECT();
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                lastHResult = SafeNativeMethods.GetThemeBackgroundContentRect( new HandleRef( this, Handle ), hdc, part, state, new NativeMethods.COMRECT( bounds ), rect );
            }
 
            return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom);
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetBackgroundExtent"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public Rectangle GetBackgroundExtent(IDeviceContext dc, Rectangle contentBounds) {
            if( dc == null ){
                throw new ArgumentNullException("dc");
            }
            if (contentBounds.Width < 0 || contentBounds.Height < 0) {
                return Rectangle.Empty;
            }
            
            NativeMethods.COMRECT rect = new NativeMethods.COMRECT();
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                lastHResult = SafeNativeMethods.GetThemeBackgroundExtent( new HandleRef( this, Handle ), hdc, part, state, new NativeMethods.COMRECT( contentBounds ), rect );
            }
 
            return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom);
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetBackgroundRegion"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Computes the region for a regular or partially transparent background that is bounded by a specified 
        ///       rectangle. Return null if the region cannot be created.
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        [SuppressUnmanagedCodeSecurity, 
         SuppressMessage("Microsoft.Security", "CA2118:ReviewSuppressUnmanagedCodeSecurityUsage")]
        public Region GetBackgroundRegion(IDeviceContext dc, Rectangle bounds) {
            if (dc == null) {
                throw new ArgumentNullException("dc");
            }
            if (bounds.Width < 0 || bounds.Height < 0) {
                return null;
            }
 
            IntPtr hRegion = IntPtr.Zero;
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                lastHResult = SafeNativeMethods.GetThemeBackgroundRegion( new HandleRef( this, Handle ), hdc, part, state, new NativeMethods.COMRECT( bounds ), ref hRegion );
            }
 
            // GetThemeBackgroundRegion returns a null hRegion if it fails to create one, it could be because the bounding
            // box is too big. For more info see code in %xpsrc%\shell\themes\uxtheme\imagefile.cpp if you have an enlistment to it.
 
		if (hRegion == IntPtr.Zero) {
			return null;
		}
 
		// From the GDI+ sources it doesn't appear as if they take ownership of the hRegion, so this is safe to do.
		// We need to DeleteObject in order to not leak. DevDiv Bugs 169791
		Region region = Region.FromHrgn(hRegion);
		SafeNativeMethods.ExternalDeleteObject(new HandleRef(null, hRegion));
		return region;
 
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetBoolean"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public bool GetBoolean(BooleanProperty prop) {
            if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)BooleanProperty.Transparent, (int)BooleanProperty.SourceShrink)){
                throw new InvalidEnumArgumentException("prop", (int)prop, typeof(BooleanProperty));
            }
 
            bool val = false;
            lastHResult = SafeNativeMethods.GetThemeBool(new HandleRef(this, Handle), part, state, (int)prop, ref val);
            return val;
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetColor"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public Color GetColor(ColorProperty prop) {
            //valid values are 0xed9 to 0xeef
            if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)ColorProperty.BorderColor, (int)ColorProperty.AccentColorHint))
            {
                throw new InvalidEnumArgumentException("prop", (int)prop, typeof(ColorProperty));
            }
 
            int color = 0;
            lastHResult = SafeNativeMethods.GetThemeColor(new HandleRef(this, Handle), part, state, (int)prop, ref color);
            return ColorTranslator.FromWin32(color);
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetEnumValue"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public int GetEnumValue(EnumProperty prop) {
            //valid values are 0xfa1 to 0xfaf
            if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)EnumProperty.BackgroundType, (int)EnumProperty.TrueSizeScalingType))
            {
                throw new InvalidEnumArgumentException("prop", (int)prop, typeof(EnumProperty));
            }
 
            int val = 0;
            lastHResult = SafeNativeMethods.GetThemeEnumValue(new HandleRef(this, Handle), part, state, (int)prop, ref val);
            return val;
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetFilename"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public string GetFilename(FilenameProperty prop) {
            //valid values are 0xbb9 to 0xbc0
            if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)FilenameProperty.ImageFile, (int)FilenameProperty.GlyphImageFile))
            {
                throw new InvalidEnumArgumentException("prop", (int)prop, typeof(FilenameProperty));
            }
 
            StringBuilder filename = new StringBuilder(512);
            lastHResult = SafeNativeMethods.GetThemeFilename(new HandleRef(this, Handle), part, state, (int)prop, filename, filename.Capacity);
            return filename.ToString();
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetFont"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///       Returns null if the returned font was not true type, since GDI+ does not support it.
        ///    </para>
        /// </devdoc>
        [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
        public Font GetFont(IDeviceContext dc, FontProperty prop)
        {
            if( dc == null ){
                throw new ArgumentNullException("dc");
            }
            
            //valid values are 0xa29 to 0xa29
            if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)FontProperty.GlyphFont, (int)FontProperty.GlyphFont))
            {
                throw new InvalidEnumArgumentException("prop", (int)prop, typeof(FontProperty));
            }
 
            NativeMethods.LOGFONT logfont = new NativeMethods.LOGFONT();
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                lastHResult = SafeNativeMethods.GetThemeFont( new HandleRef( this, Handle ), hdc, part, state, (int) prop, logfont );
            }
 
            Font font = null;
 
            //check for a failed HR.
            if (NativeMethods.Succeeded(lastHResult)) {
 
 
                // SECREVIEW: Safe to assert here, since the logfont comes from a native api and not from the 
                //            caller of this method.  The system creates the font handle.
                //
                IntSecurity.ObjectFromWin32Handle.Assert();
                try {
                    font = Font.FromLogFont(logfont);
                }
                catch (Exception e) {
                    if (ClientUtils.IsSecurityOrCriticalException(e)) {
                        throw;
                    }
 
                    //Looks like the font was not true type
                    font = null;
                }
            }
 
            return font;
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetInteger"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public int GetInteger(IntegerProperty prop) {
            //valid values are 0x961 to 0x978
            if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)IntegerProperty.ImageCount, (int)IntegerProperty.MinDpi5))
            {
                throw new InvalidEnumArgumentException("prop", (int)prop, typeof(IntegerProperty));
            }
 
            int val = 0;
            lastHResult = SafeNativeMethods.GetThemeInt(new HandleRef(this, Handle), part, state, (int)prop, ref val);
            return val;
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetPartSize"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public Size GetPartSize(IDeviceContext dc, ThemeSizeType type) {
            return GetPartSize(dc, type, IntPtr.Zero);
        }
 
        internal Size GetPartSize(IDeviceContext dc, ThemeSizeType type, IntPtr hWnd) {
            if (dc == null) {
                throw new ArgumentNullException("dc");
            }
            
            // valid values are 0x0 to 0x2
            if (!ClientUtils.IsEnumValid(type, (int)type, (int)ThemeSizeType.Minimum, (int)ThemeSizeType.Draw)) {
                throw new InvalidEnumArgumentException("type", (int)type, typeof(ThemeSizeType));
            }
 
            NativeMethods.SIZE size = new NativeMethods.SIZE();
 
            using (WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties)) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                if (DpiHelper.EnableDpiChangedMessageHandling && (IntPtr.Zero != hWnd)) {
                    using (ThemeHandle hTheme = ThemeHandle.Create(_class, true, new HandleRef(null, hWnd))) {
                        lastHResult = SafeNativeMethods.GetThemePartSize(new HandleRef(this, hTheme.NativeHandle), hdc, part, state, null, type, size);
                    }
                } 
                else {
                    lastHResult = SafeNativeMethods.GetThemePartSize( new HandleRef(this, Handle), hdc, part, state, null, type, size);
                }
            }
 
            return new Size(size.cx, size.cy);
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetPartSize1"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public Size GetPartSize(IDeviceContext dc, Rectangle bounds, ThemeSizeType type) {
            if( dc == null ){
                throw new ArgumentNullException("dc");
            }
            
            //valid values are 0x0 to 0x2
            if (!ClientUtils.IsEnumValid(type, (int)type, (int)ThemeSizeType.Minimum, (int)ThemeSizeType.Draw))
            {
                throw new InvalidEnumArgumentException("type", (int)type, typeof(ThemeSizeType));
            }
 
            NativeMethods.SIZE size = new NativeMethods.SIZE();
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                lastHResult = SafeNativeMethods.GetThemePartSize( new HandleRef( this, Handle ), hdc, part, state, new NativeMethods.COMRECT( bounds ), type, size );
            }
 
            return new Size(size.cx, size.cy);
        } 
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetPoint"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public Point GetPoint(PointProperty prop) {
            //valid values are 0xd49 to 0xd50
            if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)PointProperty.Offset, (int)PointProperty.MinSize5))
            {
                throw new InvalidEnumArgumentException("prop", (int)prop, typeof(PointProperty));
            }
 
            NativeMethods.POINT point = new NativeMethods.POINT();
            lastHResult = SafeNativeMethods.GetThemePosition(new HandleRef(this, Handle), part, state, (int)prop, point);
            return new Point(point.x, point.y);
        }        
        
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetMargins"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public Padding GetMargins(IDeviceContext dc, MarginProperty prop) {
            if( dc == null ){
                throw new ArgumentNullException("dc");
            }
            
            //valid values are 0xe11 to 0xe13
            if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)MarginProperty.SizingMargins, (int)MarginProperty.CaptionMargins))
            {
                throw new InvalidEnumArgumentException("prop", (int)prop, typeof(MarginProperty));
            }
 
            NativeMethods.MARGINS margins = new NativeMethods.MARGINS();
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                lastHResult = SafeNativeMethods.GetThemeMargins( new HandleRef( this, Handle ), hdc, part, state, (int) prop, ref margins );
            }
 
            return new Padding(margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth, margins.cyBottomHeight);
        }
        
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetString"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public string GetString(StringProperty prop) {
            //valid values are 0xc81 to 0xc81
            if (!ClientUtils.IsEnumValid(prop, (int)prop, (int)StringProperty.Text, (int)StringProperty.Text))
            {
                throw new InvalidEnumArgumentException("prop", (int)prop, typeof(StringProperty));
            }
 
            StringBuilder aString = new StringBuilder(512);
            lastHResult = SafeNativeMethods.GetThemeString(new HandleRef(this, Handle), part, state, (int)prop, aString, aString.Capacity);
            return aString.ToString();
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetTextExtent"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public Rectangle GetTextExtent(IDeviceContext dc, string textToDraw, TextFormatFlags flags) {
            if( dc == null ){
                throw new ArgumentNullException("dc");
            }
 
            if (String.IsNullOrEmpty(textToDraw)) {
                throw new ArgumentNullException("textToDraw");
            }
 
            NativeMethods.COMRECT rect = new NativeMethods.COMRECT();
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                lastHResult = SafeNativeMethods.GetThemeTextExtent( new HandleRef( this, Handle ), hdc, part, state, textToDraw, textToDraw.Length, (int) flags, null, rect );
            }
 
            return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom);
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetTextExtent1"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public Rectangle GetTextExtent(IDeviceContext dc, Rectangle bounds, string textToDraw, TextFormatFlags flags) {
            if( dc == null ){
                throw new ArgumentNullException("dc");
            }
            
            if (String.IsNullOrEmpty(textToDraw)) {
                throw new ArgumentNullException("textToDraw");
            }
 
            NativeMethods.COMRECT rect = new NativeMethods.COMRECT();
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                lastHResult = SafeNativeMethods.GetThemeTextExtent( new HandleRef( this, Handle ), hdc, part, state, textToDraw, textToDraw.Length, (int) flags, new NativeMethods.COMRECT( bounds ), rect );
            }
 
            return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom);
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetTextMetric"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public TextMetrics GetTextMetrics(IDeviceContext dc) {
            if( dc == null ){
                throw new ArgumentNullException("dc");
            }
            
            TextMetrics tm = new TextMetrics();
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                lastHResult = SafeNativeMethods.GetThemeTextMetrics( new HandleRef( this, Handle ), hdc, part, state, ref tm );
            }
 
            return tm;
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.HitTestBackground"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        // PM team has reviewed and decided on naming changes already
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
        public HitTestCode HitTestBackground(IDeviceContext dc, Rectangle backgroundRectangle, Point pt, HitTestOptions options) {
            if( dc == null ){
                throw new ArgumentNullException("dc");
            }
            
            int htCode = 0;
            NativeMethods.POINTSTRUCT point = new NativeMethods.POINTSTRUCT(pt.X, pt.Y);
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                lastHResult = SafeNativeMethods.HitTestThemeBackground( new HandleRef( this, Handle ), hdc, part, state, (int) options, new NativeMethods.COMRECT( backgroundRectangle ), NativeMethods.NullHandleRef, point, ref htCode );
            }
 
            return (HitTestCode)htCode;
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.HitTestBackground1"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        // PM team has reviewed and decided on naming changes already
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
        public HitTestCode HitTestBackground(Graphics g, Rectangle backgroundRectangle, Region region, Point pt, HitTestOptions options) {
            if (g == null) {
                throw new ArgumentNullException("g");
            }
 
            IntPtr hRgn = region.GetHrgn(g);
 
            return HitTestBackground(g, backgroundRectangle, hRgn, pt, options);
        }
 
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.HitTestBackground1"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        // PM team has reviewed and decided on naming changes already
        [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")]
        public HitTestCode HitTestBackground(IDeviceContext dc, Rectangle backgroundRectangle, IntPtr hRgn, Point pt, HitTestOptions options) {
            if( dc == null ){
                throw new ArgumentNullException("dc");
            }
            
            int htCode = 0;
            NativeMethods.POINTSTRUCT point = new NativeMethods.POINTSTRUCT(pt.X, pt.Y);
 
            using( WindowsGraphicsWrapper wgr = new WindowsGraphicsWrapper( dc, AllGraphicsProperties ) ) {
                HandleRef hdc = new HandleRef( wgr, wgr.WindowsGraphics.DeviceContext.Hdc );
                lastHResult = SafeNativeMethods.HitTestThemeBackground( new HandleRef( this, Handle ), hdc, part, state, (int) options, new NativeMethods.COMRECT( backgroundRectangle ), new HandleRef( this, hRgn ), point, ref htCode );
            }
 
            return (HitTestCode)htCode;
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.IsBackgroundPartiallyTransparent"]/*' />
        /// <devdoc>
        ///    <para>
        ///       [See win32 equivalent.]
        ///    </para>
        /// </devdoc>
        public bool IsBackgroundPartiallyTransparent() {
            return (SafeNativeMethods.IsThemeBackgroundPartiallyTransparent(new HandleRef(this, Handle), part, state));
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetLastHResult"]/*' />
        /// <devdoc>
        ///  This is similar to GetLastError in Win32.  It returns the last HRESULT returned from a native call
        ///  into theme apis.  We eat the errors and let the user handle any errors that occurred.
        /// </devdoc>
        public int LastHResult {
            get {
                return lastHResult;
            }
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.CreateThemeHandleHashTable"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Instantiates the ThemeHandle cache hashtable.
        ///    </para>
        /// </devdoc>
        private static void CreateThemeHandleHashtable() {
            themeHandles = new Hashtable(numberOfPossibleClasses);
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.OnThemeChanged"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Handles the ThemeChanged event. Basically, we need to ensure all per-thread theme handle
        ///       caches are refreshed.
        ///    </para>
        /// </devdoc>
        private static void OnUserPreferenceChanging(object sender, UserPreferenceChangingEventArgs ea) {
            if (ea.Category == UserPreferenceCategory.VisualStyle) {
                // Let all threads know their cached handles are no longer valid; 
                // cache refresh will happen at next handle access.
                // Note that if the theme changes 2^sizeof(long) times before a thread uses 
                // its handle, this whole version hack won't work, but I don't see that happening.
                globalCacheVersion++; 
            }
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.RefreshCache"]/*' />
        /// <devdoc>
        ///    <para>
        ///     Refreshes this thread's theme handle cache.       
        ///    </para>
        /// </devdoc>
        private static void RefreshCache() {
            ThemeHandle tHandle = null;
 
            if (themeHandles != null) {
                string[] classNames = new string[themeHandles.Keys.Count];
                themeHandles.Keys.CopyTo(classNames, 0);
                                
                foreach (string className in classNames) {
                    tHandle = (ThemeHandle) themeHandles[className];
                    if (tHandle != null) {
                        tHandle.Dispose();
                    }
 
                    // We don't call IsSupported here, since that could cause RefreshCache to be called again,
                    // leading to stack overflow.
                    if (AreClientAreaVisualStylesSupported) {
                        tHandle = ThemeHandle.Create(className, false);
                        if (tHandle != null) {
                            themeHandles[className] = tHandle;
                        }
                    }
                }
            }
        }
 
        private static IntPtr GetHandle(string className) {
            return GetHandle(className, true);
        }
 
        /// <include file='doc\VisualStyleRenderer.uex' path='docs/doc[@for="VisualStyleRenderer.GetHandle"]/*' />
        /// <devdoc>
        ///    <para>
        ///     Retrieves a IntPtr theme handle for the given class from the themeHandle cache. If its not 
        ///     present in the cache, it creates a new ThemeHandle object and stores it there.
        ///    </para>
        /// </devdoc>
        private static IntPtr GetHandle(string className, bool throwExceptionOnFail) {
            ThemeHandle tHandle;
 
            if (themeHandles == null) {
                CreateThemeHandleHashtable();
            }
 
 
            if (threadCacheVersion != globalCacheVersion) {
                RefreshCache();
                threadCacheVersion = globalCacheVersion;
            }
 
            if (!themeHandles.Contains(className)) { // see if it is already in cache
                tHandle = ThemeHandle.Create(className, throwExceptionOnFail);
                if (tHandle == null) {
                    return IntPtr.Zero;
                }
                themeHandles.Add(className, tHandle);
            }
            else {
                tHandle = (ThemeHandle) themeHandles[className];
            }
 
            return tHandle.NativeHandle;
        }
 
        // This wrapper class is needed for safely cleaning up TLS cache of handles.
        private class ThemeHandle : IDisposable {
            private IntPtr _hTheme = IntPtr.Zero;
 
            private ThemeHandle(IntPtr hTheme) {
                _hTheme = hTheme;
            }            
 
            public IntPtr NativeHandle {
                get {
                    return _hTheme;
                }
            }
 
            public static ThemeHandle Create(string className, bool throwExceptionOnFail) {
                return Create(className, throwExceptionOnFail, new HandleRef(null, IntPtr.Zero));
            }
 
            internal static ThemeHandle Create(string className, bool throwExceptionOnFail, HandleRef hWndRef) {
                // HThemes require an HWND when display scaling is different between monitors.
                IntPtr hTheme = IntPtr.Zero;
 
                try
                {
                    hTheme = SafeNativeMethods.OpenThemeData(hWndRef, className);
                }
                catch (Exception e)
                {
                    //We don't want to eat critical exceptions
                    if (ClientUtils.IsSecurityOrCriticalException(e))
                    {
                        throw;
                    }
 
                    if (throwExceptionOnFail)
                    {
                        throw new InvalidOperationException(SR.GetString(SR.VisualStyleHandleCreationFailed), e);
                    }
                    else
                    {
                        return null;
                    }
                }
 
                if (hTheme == IntPtr.Zero) {
                    if (throwExceptionOnFail) {
                        throw new InvalidOperationException(SR.GetString(SR.VisualStyleHandleCreationFailed));
                    }
                    else {
                        return null;
                    }
                }
                return new ThemeHandle(hTheme);                                
            }
 
            public void Dispose() {
                if (_hTheme != IntPtr.Zero) {
                    SafeNativeMethods.CloseThemeData(new HandleRef(null, _hTheme));
                    _hTheme = IntPtr.Zero;
                }
                GC.SuppressFinalize(this);
            }
 
            ~ThemeHandle() {
                Dispose();
            }
        }
    }
}