EntryPointNotFoundException al usar TaskDialog en C #
-
06-07-2019 - |
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?
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