C#: comctl32.dll version 6 in debugger

2020-06-04 01:32发布

问题:

I'm using the WindowsAPICodePack for TaskDialog. When I try to show the dialog it says that it needs to load version 6 of comctl32.dll. So I added version 6 to the app.manifest and tried running it. Still no luck. I went to the Debug folder and ran the program without Visual Studio and it works fine. I'm guessing that Visual Studio isn't using the manifest file... I was wondering if there was a way to make it do this.

回答1:

Robpol86, your code is throwing SEHExceptions, because the signatures for ActivateActCtx and DeactivateActCtx are not correct. You have to use UIntPtr instead of uint for the lpCookie.

Therefore, the correct code for EnableThemingInScope.cs would be:

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Windows.Forms;

namespace Microsoft.WindowsAPICodePack.Dialogs
{
    /// http://support.microsoft.com/kb/830033
    /// <devdoc>
    ///     This class is intended to use with the C# 'using' statement in
    ///     to activate an activation context for turning on visual theming at
    ///     the beginning of a scope, and have it automatically deactivated
    ///     when the scope is exited.
    /// </devdoc>

    [SuppressUnmanagedCodeSecurity]
    internal class EnableThemingInScope : IDisposable
    {
        // Private data
        private UIntPtr cookie;
        private static ACTCTX enableThemingActivationContext;
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")]
        private static IntPtr hActCtx;
        private static bool contextCreationSucceeded = false;

        public EnableThemingInScope(bool enable)
        {
            cookie = UIntPtr.Zero;
            if (enable && OSFeature.Feature.IsPresent(OSFeature.Themes))
            {
                if (EnsureActivateContextCreated())
                {
                    if (!ActivateActCtx(hActCtx, out cookie))
                    {
                        // Be sure cookie always zero if activation failed
                        cookie = UIntPtr.Zero;
                    }
                }
            }
        }

        ~EnableThemingInScope()
        {
            Dispose();
        }

        void IDisposable.Dispose()
        {
            Dispose();
            GC.SuppressFinalize(this);
        }

        private void Dispose()
        {
            if (cookie != UIntPtr.Zero)
            {
                try
                {
                    if (DeactivateActCtx(0, cookie))
                    {
                        // deactivation succeeded...
                        cookie = UIntPtr.Zero;
                    }
                }
                catch (SEHException)
                {
                    //Hopefully solved this exception
                }
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity")]
        private static bool EnsureActivateContextCreated()
        {
            lock (typeof(EnableThemingInScope))
            {
                if (!contextCreationSucceeded)
                {
                    // Pull manifest from the .NET Framework install
                    // directory

                    string assemblyLoc = null;

                    FileIOPermission fiop = new FileIOPermission(PermissionState.None);
                    fiop.AllFiles = FileIOPermissionAccess.PathDiscovery;
                    fiop.Assert();
                    try
                    {
                        assemblyLoc = typeof(Object).Assembly.Location;
                    }
                    finally
                    {
                        CodeAccessPermission.RevertAssert();
                    }

                    string manifestLoc = null;
                    string installDir = null;
                    if (assemblyLoc != null)
                    {
                        installDir = Path.GetDirectoryName(assemblyLoc);
                        const string manifestName = "XPThemes.manifest";
                        manifestLoc = Path.Combine(installDir, manifestName);
                    }

                    if (manifestLoc != null && installDir != null)
                    {
                        enableThemingActivationContext = new ACTCTX();
                        enableThemingActivationContext.cbSize = Marshal.SizeOf(typeof(ACTCTX));
                        enableThemingActivationContext.lpSource = manifestLoc;

                        // Set the lpAssemblyDirectory to the install
                        // directory to prevent Win32 Side by Side from
                        // looking for comctl32 in the application
                        // directory, which could cause a bogus dll to be
                        // placed there and open a security hole.
                        enableThemingActivationContext.lpAssemblyDirectory = installDir;
                        enableThemingActivationContext.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;

                        // Note this will fail gracefully if file specified
                        // by manifestLoc doesn't exist.
                        hActCtx = CreateActCtx(ref enableThemingActivationContext);
                        contextCreationSucceeded = (hActCtx != new IntPtr(-1));
                    }
                }

                // If we return false, we'll try again on the next call into
                // EnsureActivateContextCreated(), which is fine.
                return contextCreationSucceeded;
            }
        }

        // All the pinvoke goo...
        [DllImport("Kernel32.dll")]
        private extern static IntPtr CreateActCtx(ref ACTCTX actctx);
        [DllImport("Kernel32.dll")]
        private extern static bool ActivateActCtx(IntPtr hActCtx, out UIntPtr lpCookie);
        [DllImport("Kernel32.dll")]
        private extern static bool DeactivateActCtx(uint dwFlags, UIntPtr lpCookie);

        private const int ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004;

        private struct ACTCTX
        {
            public int cbSize;
            public uint dwFlags;
            public string lpSource;
            public ushort wProcessorArchitecture;
            public ushort wLangId;
            public string lpAssemblyDirectory;
            public string lpResourceName;
            public string lpApplicationName;
        }
    }
}


回答2:

I recently ran into this problem when debugging code with the TaskDialogDemo in the CodePack. This is how I kind of fixed it. The problem with using this is if I open two or three dialogs it throws an SEHException, which I haven't figured out how to fix. So buyer beware.

Add Core\Interop\TaskDialogs\EnableThemingInScope.cs:

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Windows.Forms;

namespace Microsoft.WindowsAPICodePack.Dialogs {
    /// http://support.microsoft.com/kb/830033
    /// <devdoc>
    ///     This class is intended to use with the C# 'using' statement in
    ///     to activate an activation context for turning on visual theming at
    ///     the beginning of a scope, and have it automatically deactivated
    ///     when the scope is exited.
    /// </devdoc>

    [SuppressUnmanagedCodeSecurity]
    internal class EnableThemingInScope : IDisposable {
        // Private data
        private uint cookie;
        private static ACTCTX enableThemingActivationContext;
        [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources" )]
        private static IntPtr hActCtx;
        private static bool contextCreationSucceeded = false;

        public EnableThemingInScope( bool enable ) {
            cookie = 0;
            if ( enable && OSFeature.Feature.IsPresent( OSFeature.Themes ) ) {
                if ( EnsureActivateContextCreated() ) {
                    if ( !ActivateActCtx( hActCtx, out cookie ) ) {
                        // Be sure cookie always zero if activation failed
                        cookie = 0;
                    }
                }
            }
        }

        ~EnableThemingInScope() {
            Dispose();
        }

        void IDisposable.Dispose() {
            Dispose();
            GC.SuppressFinalize( this );
        }

        private void Dispose() {
            if ( cookie != 0 ) {
                try {
                    if ( DeactivateActCtx( 0, cookie ) ) {
                        // deactivation succeeded...
                        cookie = 0;
                    }
                } catch ( SEHException ) {
                    // Robpol86: I don't know how to fix this!
                }
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity" )]
        private static bool EnsureActivateContextCreated() {
            lock ( typeof( EnableThemingInScope ) ) {
                if ( !contextCreationSucceeded ) {
                    // Pull manifest from the .NET Framework install
                    // directory

                    string assemblyLoc = null;

                    FileIOPermission fiop = new FileIOPermission( PermissionState.None );
                    fiop.AllFiles = FileIOPermissionAccess.PathDiscovery;
                    fiop.Assert();
                    try {
                        assemblyLoc = typeof( Object ).Assembly.Location;
                    } finally {
                        CodeAccessPermission.RevertAssert();
                    }

                    string manifestLoc = null;
                    string installDir = null;
                    if ( assemblyLoc != null ) {
                        installDir = Path.GetDirectoryName( assemblyLoc );
                        const string manifestName = "XPThemes.manifest";
                        manifestLoc = Path.Combine( installDir, manifestName );
                    }

                    if ( manifestLoc != null && installDir != null ) {
                        enableThemingActivationContext = new ACTCTX();
                        enableThemingActivationContext.cbSize = Marshal.SizeOf( typeof( ACTCTX ) );
                        enableThemingActivationContext.lpSource = manifestLoc;

                        // Set the lpAssemblyDirectory to the install
                        // directory to prevent Win32 Side by Side from
                        // looking for comctl32 in the application
                        // directory, which could cause a bogus dll to be
                        // placed there and open a security hole.
                        enableThemingActivationContext.lpAssemblyDirectory = installDir;
                        enableThemingActivationContext.dwFlags = ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID;

                        // Note this will fail gracefully if file specified
                        // by manifestLoc doesn't exist.
                        hActCtx = CreateActCtx( ref enableThemingActivationContext );
                        contextCreationSucceeded = (hActCtx != new IntPtr( -1 ));
                    }
                }

                // If we return false, we'll try again on the next call into
                // EnsureActivateContextCreated(), which is fine.
                return contextCreationSucceeded;
            }
        }

        // All the pinvoke goo...
        [DllImport( "Kernel32.dll" )]
        private extern static IntPtr CreateActCtx( ref ACTCTX actctx );
        [DllImport( "Kernel32.dll" )]
        private extern static bool ActivateActCtx( IntPtr hActCtx, out uint lpCookie );
        [DllImport( "Kernel32.dll" )]
        private extern static bool DeactivateActCtx( uint dwFlags, uint lpCookie );

        private const int ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004;

        private struct ACTCTX {
            public int cbSize;
            public uint dwFlags;
            public string lpSource;
            public ushort wProcessorArchitecture;
            public ushort wLangId;
            public string lpAssemblyDirectory;
            public string lpResourceName;
            public string lpApplicationName;
        }
    }
}

Then in Core\Interop\TaskDialogs\NativeTaskDialog.cs on line 93 (above HResult hresult = TaskDialogNativeMethods.TaskDialogIndirect) make the section look like this (in the end you will have three new lines):

// Here is the way we use "vanilla" P/Invoke to call TaskDialogIndirect().
HResult hresult;
using ( new EnableThemingInScope( true ) ) {
    hresult = TaskDialogNativeMethods.TaskDialogIndirect(
        nativeDialogConfig,
        out selectedButtonId,
        out selectedRadioButtonId,
        out checkBoxChecked );
}


回答3:

This page describes how to add a custom manifest to your project in order to tell Windows to load the new comctl32.dll (version 6.0):

  • Setting the correct manifest version

Does your manifest have the right dependency on comctl32.dll? Did you embed the created manifest?



回答4:

I have the same issue with Visual Studio in Debug mode. So far I haven't found a workaround, it works fine in Release mode.