2009-09-12 210 views
6

我正在爲TaskDialog使用WindowsAPICodePack。當我嘗試顯示對話框時,它說它需要加載comctl32.dll的版本6。所以我在app.manifest中添加了第6版,並嘗試運行它。仍然沒有運氣。我去了調試文件夾,並運行沒有Visual Studio的程序,它工作正常。我猜Visual Studio沒有使用清單文件...我想知道是否有辦法讓它做到這一點。調試器中的C#:comctl32.dll版本6

回答

9

Rob pol86,您的代碼正在拋出SEHExceptions,因爲ActivateActCtx和DeactivateActCtx的簽名不正確。您必須使用UIntPtr而不是uint作爲lpCookie。

因此,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 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; 
     } 
    } 
} 
+0

歡呼聲,這是正確的答案。沒有必要用這個來改變清單。 – 2014-05-20 15:16:48

+0

+1正確答案。爲了將來的參考,我從這篇msdn知識庫文章中得到了一個類似的破解uint cookie實現:https://support.microsoft.com/en-us/kb/830033只是爲了清楚:我可以創建範圍,但後來我得到了一個SEH DeactivateActCtx異常。進一步的調試發現它是錯誤代碼6,這是ERROR_INVALID_HANDLE,因爲由於錯誤的類型,cookie不能用於正確地停用上下文。 – Samuel 2015-11-25 07:46:53

+0

謝謝!我在發佈ClickOnce時遇到了comctl32.dll問題,並解決了這個問題 – dariusc 2016-04-25 17:20:56

0

本頁面介紹如何添加自定義清單到您的項目,以告訴Windows加載新的comctl32.dll(版本6.0):

您是否有表現comctl32.dll的正確依賴關係?你嵌入了創建的清單嗎?

1

我在調試模式下遇到與Visual Studio相同的問題。到目前爲止,我還沒有找到解決方法,它在發佈模式下工作正常。

4

我最近遇到這個問題時CodePack中的TaskDialogDemo調試代碼。這是我如何修復它。使用這個的問題是,如果我打開兩個或三個對話框,它會拋出一個SEHException,我還沒有想出如何解決。所以買家要小心。

添加核心\互操作\ 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; 
     } 
    } 
} 

然後,在上線93 核心\互操作\ TaskDialogs \ NativeTaskDialog.cs(以上的HResult HRESULT = TaskDialogNativeMethods.TaskDialogIndirect )使部分看起來像這樣(最終你將有三條新線):

// 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); 
}