Domanda

Sto usando il seguente codice per chiamare 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);

Tuttavia, ottengo l'eccezione "Impossibile trovare un punto di ingresso denominato" TaskDialogIndirect "nella DLL" ComCtl32 ". "

Ho preso questo codice . Sto usando Windows 7 x64 (RC).

Cosa sto sbagliando?

È stato utile?

Soluzione

Nulla tranne questa è una funzione di vista

UPDATE: Questo problema ha avuto a che fare con gli assembly side-by-side: queste funzioni sono presenti solo nella versione 6 di comctl32.dll, ma, per motivi di compatibilità, Vista caricherà una versione precedente a meno che non sia indicato diversamente. L'approccio adottato dalla maggior parte delle persone (incluso me) è l'utilizzo di manifest. Ciò si è rivelato difficile e potrebbe non essere la soluzione giusta, soprattutto se quello che stai scrivendo è una libreria: non vuoi necessariamente forzare l'intera applicazione a usare controlli comuni 6.

La soluzione giusta è inviare una nuova attivazione contesto quando si chiama una delle API solo Vista. Il contesto di attivazione utilizzerà la versione corretta di comctl32.dll lasciando solo il resto dell'applicazione e non è richiesto alcun manifest.

Fortunatamente, questo è facile da fare. Qualche codice completo già esistente Knowledge Base MS . Il codice dell'articolo (KB 830033) fa il trucco così com'è.

API gestite alternative: Un wrapper completo per TaskDialog & amp; TaskDialogIndirect è disponibile qui:

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

Per WPF utilizzare quanto segue:

Scarica la 'Libreria di esempi VistaBridge' da http://code.msdn.microsoft.com/VistaBridge una volta scaricato, apri il progetto e poi crealo (se vuoi esaminare tutto il codice, esamina i file nelle cartelle \ Libreria o \ Interop). Ora puoi prendere la DLL da VistaBridge \ bin \ debug \ e aggiungere un riferimento ad essa nel tuo progetto, inoltre devi aggiungere un'istruzione using per ciascuno dei diversi moduli VistaBridge. Ad esempio:

utilizzando Microsoft.SDK.Samples.VistaBridge.Interop o .Library o .Properties o .Services - A seconda delle esigenze.

Il progetto VistaBridge include API per molte altre funzionalità di Vista (come TaskDialog, Vista OpenFile e finestre di dialogo SaveFile e, naturalmente, Aero Glass Effects) per provarle, eseguire VistaBridge Project.

Altri suggerimenti

L'uso di Task Dialog richiede la versione 6 della DLL dei controlli comuni di Windows (ComCtl32.dll)! Per motivi di compatibilità, le applicazioni non si legano a questa versione per impostazione predefinita. Un modo per associare alla versione 6 consiste nel posizionare un file manifest accanto all'eseguibile (denominato YourAppName.exe.manifest), con il seguente contenuto:

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

Questo manifest può anche essere incorporato come risorsa Win32 all'interno dell'eseguibile (con il nome RT_MANIFEST e l'ID impostati su 1), se non si desidera avere il file autonomo aggiuntivo. Visual Studio può fare questo lavoro per te, se associ il tuo file manifest alle proprietà del tuo progetto.

Sulla base della risposta di almog.ori (che ha ottenuto alcuni collegamenti orfani) ho apportato una piccola modifica al codice collegato, perplesso per diversi giorni:

MS Knowledgebase ha aiutato ( Archiv ), codice completo con adozioni fatte da me:

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

L'ho usato in questo modo:

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

in TaskDialogInterop.cs fornito in Finestra di dialogo attività WPF Wrapper su GitHub

Per ulteriori informazioni sui possibili SEHException nel Finalizzatore di EnableThemingInScope vedere questo Domanda su SO

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top