C#でTaskDialogを使用する場合のEntryPointNotFoundException
-
06-07-2019 - |
質問
次のコードを使用して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);
ただし、例外が発生します" DLL 'ComCtl32'で 'TaskDialogIndirect'という名前のエントリポイントが見つかりません。
このコードを使用しました。 Windows 7 x64(RC)を使用しています。
何が間違っているのですか?
解決
これ以外はビスタ機能ではありません
更新: この問題は、サイドバイサイドアセンブリに関係していました。これらの関数はcomctl32.dllバージョン6にのみ存在しますが、互換性の理由から、特に指定しない限り、Vistaは以前のバージョンをロードします。ほとんどの人(私を含む)が取っているアプローチは、マニフェストを使用することです。これはトリッキーであることが証明されており、とにかく正しいソリューションではないかもしれません。特にあなたが書いているのがライブラリーである場合:必ずしもアプリケーション全体で共通コントロール6を使用する必要はありません。
正しい解決策は、新しいアクティベーション Vista専用APIの1つを呼び出すときのコンテキスト。アクティベーションコンテキストは、comctl32.dllの正しいバージョンを使用し、残りのアプリケーションはそのままにして、マニフェストは不要です。
幸いなことに、これは簡単です。既に存在する完全なコードの一部は MS Knowledgebase です。記事のコード(KB 830033)は、このトリックをそのまま実行します。
代替マネージAPI: VistaのTaskDialog&の完全なラッパー。 TaskDialogIndirectはここにあります:
http://code.msdn.microsoft.com/WindowsAPICodePack
WPFの場合、次を使用します。
http://code.msdn.microsoft.com/VistaBridge <から「VistaBridgeサンプルライブラリ」をダウンロードしますダウンロードしたら、プロジェクトを開いてビルドします(すべてのコードを調べたい場合は、\ Libraryまたは\ Interopフォルダー内のファイルを調べます)。これで、VistaBridge \ bin \ debug \からDLLを取得して、プロジェクトへの参照を追加できます。また、異なるVistaBridgeモジュールごとにusingステートメントを追加する必要があります。例:
Microsoft.SDK.Samples.VistaBridge.Interopまたは.Libraryまたは.Propertiesまたは.Servicesの使用-ニーズに応じて。
VistaBridgeプロジェクトには、VistaBridgeプロジェクトを実行するために、他の多くのVista機能(TaskDialog、Vista OpenFile、SaveFileダイアログ、そしてもちろんAeroグラスエフェクトなど)のAPIが含まれています。
他のヒント
タスクダイアログを使用するには、バージョン6のWindows Common Controls DLL(ComCtl32.dll)が必要です!互換性の理由から、アプリケーションはデフォルトではこのバージョンにバインドしません。バージョン6にバインドする1つの方法は、次のコンテンツを含む実行可能ファイル(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ナレッジベースが役に立ちました( Archiv )、私が採用した完全なコード:
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);
}
GitHubのWPFタスクダイアログラッパーで提供される in TaskDialogInterop.cs
EnableThemingInScope
のファイナライザーで発生する可能性のある SEHException
の詳細については、 SOに関する質問