Исключение EntryPointNotFoundException при использовании TaskDialog в C#

StackOverflow https://stackoverflow.com/questions/1612351

Вопрос

Я использую следующий код для вызова 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);

Однако я получаю исключение "Не удается найти точку входа с именем 'TaskDialogIndirect' в DLL 'ComCtl32'".

Я взял этот код.Я использую Windows 7 x64 (RC).

Что я делаю не так?

Это было полезно?

Решение

Ничего, кроме того, что это функция Vista

Обновить:Эта проблема была связана с параллельными сборками:эти функции присутствуют только в comctl32.dll версии 6, но по соображениям совместимости Vista загрузит более раннюю версию, если вы не укажете иное.Подход, которого придерживается большинство людей (включая меня), заключается в использовании манифеста.Это оказалось сложным и, возможно, в любом случае не является правильным решением, особенно если то, что вы пишете, является библиотекой:вы не обязательно хотите заставлять все приложение использовать общие элементы управления 6.

Правильное решение состоит в том, чтобы подтолкнуть новая активация контекст при вызове одного из API-интерфейсов только для Vista.Контекст активации будет использовать правильную версию comctl32.dll, оставляя остальную часть приложения в покое, и манифест не требуется.

К счастью, это легко сделать.Некоторый полный код, который уже существует База знаний MS Knowledge Base.Код из статьи (KB 830033) выполняет трюк как есть.

Альтернативный управляемый API:Полную оболочку для TaskDialog Vista и TaskDialogIndirect можно найти здесь:

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

Для WPF используйте следующее:

Загрузите "Библиотеку примеров VistaBridge" с сайта http://code.msdn.microsoft.com/VistaBridge после загрузки откройте проект, а затем соберите его (если вы хотите просмотреть весь код, изучите файлы в папках \Library или \Interop).Теперь вы можете взять библиотеку DLL из VistaBridge\bin\debug\ и добавить ссылку на нее в свой проект, также вы должны добавить инструкцию using для каждого из различных модулей VistaBridge.Например:

использование Microsoft.SDK.Samples.VistaBridge.Interop, или .Library, или .Properties, или .Services - в зависимости от ваших потребностей.

Проект VistaBridge включает API для многих других функций Vista (таких как TaskDialog, диалоговые окна Vista OpenFile и SaveFile и, конечно же, эффекты Aero Glass). чтобы опробовать их, запустите проект VistaBridge.

Другие советы

Для использования диалога задач требуется 6-я версия библиотеки общих элементов управления Windows (ComCtl32.dll)! Из соображений совместимости приложения не привязываются к этой версии по умолчанию. Один из способов привязки к версии 6 - поместить файл манифеста рядом с исполняемым файлом (с именем YourAppName.exe.manifest) со следующим содержимым:

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

Этот манифест также может быть встроен как ресурс Win32 в ваш исполняемый файл (с именем RT_MANIFEST и ID, установленным в 1), если вы не хотите иметь дополнительный автономный файл. Visual Studio может выполнить эту работу за вас, если вы свяжете свой файл манифеста в свойствах вашего проекта.

Основываясь на ответе almog.ori (который получил несколько осиротевших ссылок), я внес небольшое изменение в связанный код, я ломал голову над несколькими днями:

База знаний MS помогла ( Архив ), полный код с усыновлениями, сделанными мной:

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

Затем я использовал это так:

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

в TaskDialogInterop.cs , предоставленной в Оболочке диалогового окна задач WPF на GitHub

Для получения дополнительной информации о возможных SEHException в финализаторе EnableThemingInScope см. этот Вопрос по SO

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top