Pregunta

Estoy usando el siguiente código para llamar a un TaskDialog.

    [DllImport("ComCtl32", CharSet = CharSet.Unicode, PreserveSig = false)]
    internal static extern void TaskDialogIndirect(
        [In] ref TASKDIALOGCONFIG pTaskConfig,
        [Out] out int pnButton,
        [Out] out int pnRadioButton,
        [Out] out bool pfVerificationFlagChecked);

Sin embargo, obtengo la excepción " No se puede encontrar un punto de entrada denominado 'TaskDialogIndirect' en la DLL 'ComCtl32'. "

Tomé este código . Estoy usando Windows 7 x64 (RC).

¿Qué estoy haciendo mal?

¿Fue útil?

Solución

Nada, excepto que esta es una característica de vista

ACTUALIZACIÓN: Este problema tuvo que ver con los conjuntos de lado a lado: estas funciones están presentes solo en la versión 6 de comctl32.dll, pero, por razones de compatibilidad, Vista cargará una versión anterior a menos que le indique lo contrario. El enfoque que la mayoría de las personas (incluyéndome a mí) ha estado usando es usar un manifiesto. Esto ha demostrado ser complicado, y puede que no sea la solución correcta de todos modos, especialmente si lo que estás escribiendo es una biblioteca: no necesariamente quieres forzar a toda la aplicación a usar los controles comunes 6.

La solución correcta es enviar una nueva activación context cuando se llama a una de las API solo para Vista. El contexto de activación utilizará la versión correcta de comctl32.dll mientras deja el resto de la aplicación solo, y no se requiere ningún manifiesto.

Afortunadamente, esto es fácil de hacer. Algún código completo que ya existe MS Knowledgebase . El código del artículo (KB 830033) hace el truco como está.

API administrada alternativa: Una envoltura completa para Vista's TaskDialog & amp; TaskDialogIndirect se puede encontrar aquí:

http://code.msdn.microsoft.com/WindowsAPICodePack

Para WPF use lo siguiente:

Descargue la 'Biblioteca de ejemplo de VistaBridge' de http://code.msdn.microsoft.com/VistaBridge una vez descargado, abra el proyecto y luego genérelo (si desea revisar todo el código, examine los archivos en las carpetas \ Biblioteca o \ Interoperabilidad). Ahora puede tomar la DLL de VistaBridge \ bin \ debug \ y agregar una referencia a ella en su proyecto, así como también debe agregar una declaración de uso para cada uno de los diferentes módulos de VistaBridge. Por ejemplo:

utilizando Microsoft.SDK.Samples.VistaBridge.Interop o .Library o .Properties o .Services - Dependiendo de sus necesidades.

El proyecto VistaBridge incluye API para muchas otras características de Vista (como TaskDialog, Vista OpenFile y SaveFile Dialogs y, por supuesto, Aero Glass Effects) para probar esto, ejecute el proyecto VistaBridge.

Otros consejos

¡El uso del Cuadro de diálogo de tareas requiere la versión 6 de la DLL de controles comunes de Windows (ComCtl32.dll)! Por razones de compatibilidad, las aplicaciones no se unen a esta versión de forma predeterminada. Una forma de enlazar a la versión 6 es colocar un archivo de manifiesto junto a su ejecutable (denominado YourAppName.exe.manifest), con el siguiente contenido:

 <dependency>
    <dependentAssembly>
      <assemblyIdentity
          type="win32"
          name="Microsoft.Windows.Common-Controls"
          version="6.0.0.0"
          processorArchitecture="*"
          publicKeyToken="6595b64144ccf1df"
          language="*"
        />
    </dependentAssembly>
  </dependency>

Este manifiesto también se puede incrustar como un recurso de Win32 dentro de su ejecutable (con el nombre RT_MANIFEST y el ID establecido en 1), si no desea tener el archivo independiente adicional. Visual Studio puede hacer este trabajo por usted, si asocia su archivo de manifiesto a las propiedades de su proyecto.

Basándome en la respuesta de almog.ori (que tiene algunos enlaces huérfanos) hice un pequeño cambio en el código vinculado, me desconcerté por varios días:

MS Knowledgebase ayudó ( Archiv ), Código completo con las adopciones hechas por mí:

using System.Runtime.InteropServices;
using System;
using System.Security;
using System.Security.Permissions;
using System.Collections;
using System.IO;
using System.Text;

namespace MyOfficeNetAddin
{
    /// <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 IntPtr cookie; // changed cookie from uint to IntPtr
   private static ACTCTX enableThemingActivationContext;
   private static IntPtr hActCtx;
   private static bool contextCreationSucceeded = false;

   public EnableThemingInScope(bool enable)
   {
     if (enable)
     {
       if (EnsureActivateContextCreated())
       {
         if (!ActivateActCtx(hActCtx, out cookie))
         {
           // Be sure cookie always zero if activation failed
           cookie = IntPtr.Zero;
         }
       }
     }
  }

  // Finalizer removed, that could cause Exceptions
  // ~EnableThemingInScope()
  // {
  //    Dispose(false);
  // }

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

  private void Dispose(bool disposing)
  {
     if (cookie != IntPtr.Zero)
     {
        if (DeactivateActCtx(0, cookie))
        {
           // deactivation succeeded...
           cookie = IntPtr.Zero;
        }
     }
  }

  private 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);

  // changed from uint to IntPtr according to 
  // https://www.pinvoke.net/default.aspx/kernel32.ActiveActCtx
  [DllImport("Kernel32.dll", SetLastError = true)]
  [return: MarshalAs(UnmanagedType.Bool)]
  private static extern bool ActivateActCtx(IntPtr hActCtx, out IntPtr lpCookie);

  // changed from uint to IntPtr according to 
  // https://www.pinvoke.net/default.aspx/kernel32.DeactivateActCtx
  [DllImport("Kernel32.dll", SetLastError = true)]
  [return: MarshalAs(UnmanagedType.Bool)]
  private static extern bool DeactivateActCtx(int dwFlags, IntPtr 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;
  }
 }
}

Entonces lo usé de esa manera:

using (new EnableThemingInScope(true))
{
    // The call all this mucking about is here for.
    VistaUnsafeNativeMethods.TaskDialogIndirect(ref config, out result, out radioButtonResult, out verificationFlagChecked);
 }

en TaskDialogInterop.cs proporcionado en WPF Task Dialog Wrapper en GitHub

Para obtener más información sobre posibles SEHException en el Finalizador de EnableThemingInScope , vea esto Question on SO

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top