EntryPointNotFoundException при использовании TaskDialog в C # - PullRequest
8 голосов
/ 23 октября 2009

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

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

Ответы [ 3 ]

9 голосов
/ 23 октября 2009

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

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

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

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

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

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

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

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

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

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

3 голосов
/ 15 июля 2013

Для использования Task Dialog требуется 6-я версия DLL Common Controls DLL (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 может выполнить эту работу за вас, если вы связали свой файл манифеста в свойствах вашего проекта.

0 голосов
/ 21 февраля 2019

Основываясь на ответе 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

...