Pergunta

Eu estou usando o seguinte código para chamar um 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);

No entanto, eu recebo a exceção "Não é possível encontrar um ponto de entrada chamado 'TaskDialogIndirect' em DLL 'ComCtl32'."

este código . Estou usando o Windows 7 x64 (RC).

O que estou fazendo de errado?

Foi útil?

Solução

Nada, exceto esta é uma característica vista

UPDATE: Este probem tinha a ver com o lado por assembleias colaterais: estas funções estão presentes apenas em comctl32.dll versão 6, mas, por razões de compatibilidade, Vista irá carregar uma versão anterior, a menos que você diga a ele o contrário. A abordagem mais pessoas (inclusive eu) têm vindo a tomar é usar um manifesto. Isto tem provado ser complicado, e pode não ser a solução certa de qualquer maneira, especialmente se o que você está escrevendo é uma biblioteca:. Você não necessariamente quer forçar todo o aplicativo para usar controles comuns 6

A solução certa é empurrar um nova ativação contexto ao chamar um dos Vista somente APIs. O contexto de ativação irá utilizar a versão correta do comctl32.dll, deixando o resto da aplicação sozinho, e nenhum manifesto é necessária.

Felizmente, isso é fácil de código completo do.Some que já existe MS Conhecimento . O código do artigo (KB 830033) faz o truque como é.

API geridos Alternativa: Um invólucro completo para o Vista TaskDialog & TaskDialogIndirect pode ser encontrada aqui:

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

Para WPF usar o seguinte:

Faça o download do 'VistaBridge Library Sample' de http://code.msdn.microsoft.com/VistaBridge , uma vez baixado, abra o projeto e, em seguida, construí-lo (se você quiser olhar através de todo o código, examine os arquivos nas pastas \ biblioteca ou \ Interop). Agora você pode tomar a DLL a partir VistaBridge \ bin \ debug \ e adicione uma referência a ele em seu projeto, assim você deve adicionar um usando instrução para cada um dos diferentes módulos VistaBridge. Por Exemplo:

usando Microsoft.SDK.Samples.VistaBridge.Interop ou .library ou .Properties ou .Os serviços -. Dependendo de suas necessidades

O projeto VistaBridge inclui API para muitos outros Vista Características (como o TaskDialog, Vista OpenFile e SaveFile Diálogos, e, claro, os efeitos Aero Glass) para tentar estes para fora, executar o Projeto VistaBridge.

Outras dicas

O uso de diálogo Tarefa requer a versão 6 do Controls DLL do Windows Comum (ComCtl32.dll)! Por razões de compatibilidade, os aplicativos não se ligam a esta versão por padrão. Uma maneira de se ligam a versão 6 é colocar um arquivo de manifesto ao lado de seu executável (chamado YourAppName.exe.manifest), com o seguinte conteúdo:

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

Este manifesto também pode ser incorporado como um recurso Win32 dentro do seu executável (com o nome RT_MANIFEST e conjunto de ID a 1), se você não quer ter o arquivo autônomo extra. Visual Studio pode fazer este trabalho para você, se você associar o seu arquivo de manifesto nas propriedades do seu projeto.

Com base na resposta das almog.ori (que tem alguns links órfãos) fiz uma pequena alteração ao código ligada, eu intrigado por vários dias:

MS Conhecimento ajudou ( Archiv ), Código completa com adoções feitas por mim:

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

Então, usei-lo dessa forma:

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

em TaskDialogInterop.cs fornecidas em Wrapper diálogo WPF Task no GitHub

Para obter mais informações sobre possíveis SEHExceptions no finalizador de EnableThemingInScope ver este pergunta sobre SO

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top