我有一个WinForms应用程序,它使用了一个TaskDialog library,它利用了ComCtl32.dll的Vista风格对话框,而对于较小的操作系统,它使用了模拟的win形式...
但这不是问题......这个图书馆工作得很好,我们从来没有遇到过这个问题。直到现在......如果我们在正常情况下启动对话,那么它看起来很好。
但是,我在主窗体上添加了一个拖放处理程序来捕获从其他源(例如Windows资源管理器)中删除的文件路径。如果该拖放处理程序是第一次显示对话框,那么我们会得到以下异常:
无法在DLL“ComCtl32”中找到名为“TaskDialogIndirect”的入口点。
这发生在第三方图书馆致电:
/// <summary>
/// TaskDialogIndirect taken from commctl.h
/// </summary>
/// <param name="pTaskConfig">All the parameters about the Task Dialog to Show.</param>
/// <param name="pnButton">The push button pressed.</param>
/// <param name="pnRadioButton">The radio button that was selected.</param>
/// <param name="pfVerificationFlagChecked">The state of the verification checkbox on dismiss of the Task Dialog.</param>
[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 );
如果已显示对话框,则处理程序将运行OK。
表单的DragDrop处理程序没有显示InvokeRequired
,但我还是小心翼翼地通过Form.Invoke
提升对话框。
private void MainForm_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
Array fileNames = (Array)e.Data.GetData(DataFormats.FileDrop);
if (fileNames != null && fileNames.OfType<string>().Any())
{
foreach (var fileName in fileNames.OfType<string>())
{
this.Invoke(new Action<string>(this.AttemptOpenFromPath), fileName);
}
}
}
}
作为一方:我正在64位Windows 7计算机上编译(和运行),但使用“AnyCPU”架构标记。
关于为什么仅在第一次调用TaskDialogIndirect
时通过DragDrop处理程序引发异常的想法/解决方案???
答案 0 :(得分:3)
[DllImport ( "ComCtl32", ...)]
该库正在使用comctl32.dll Windows dll非常繁重的快捷方式。这往往会意外地结束,但它会在您的代码中失败。完整的解释是相当冗长的,我会尽量保持简短。
核心问题是Windows有两个版本的comctl32.dll。 c:\ windows \ system32中的一个版本是 legacy 版本,它们在Windows 2000及更早版本中的外观和工作方式实现了通用控件。 Windows XP获得了视觉样式,使这些控件看起来非常不同。有另一个实现这些视觉样式的 DLL,它存储在Windows并排缓存中(c:\ windows \ winsxs)。
应用程序必须明确告诉Windows它支持新版本的DLL。有两种方法可以实现,你可以在清单中(WPF的方式)或者你可以进行操作系统调用,CreateActCtx()函数(Winforms的方式)。
图书馆的工作方式是希望有人做了这两件事之一。并加载了正确版本的comctl32.dll,以便对[DllImport]函数进行直接加载并不会实际加载c:\ windows \ system32版本。旧的没有实现TaskDialogIndirect()。这是偶然的,因为一些代码通常会这样做。事实上,Windows只关心DLL名称,而不关心它来自何处以确定是否需要加载DLL。
我可以猜到你的运气如何。您正在使用Control.Invoke(),这是您在使用线程时唯一需要做的事情。显然,您在另一个线程上显示此表单,而不是主UI线程。这通常是一个非常糟糕的想法,UI线程已经设计为能够处理多个窗口。通常在UI线程上发生的一件事是Application.EnableVisualStyles()调用。告诉Windows你想要新版本的comctl32。
您可以尝试在工作线程上调用它。可能会工作,不知道。到目前为止,最好的解决方案是不在工作线程上创建窗口。您可以使用Windows API代码包摆脱不稳定的库,它为任务对话框提供了包装。
答案 1 :(得分:0)
事实证明,在DragDrop处理程序中,我应该使用BeginInvoke
将调用异步排队到Form的UI线程,而不是同步等待它在处理程序中完成...
因此,它解决了:
private void MainForm_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
Array fileNames = (Array)e.Data.GetData(DataFormats.FileDrop);
if (fileNames != null && fileNames.OfType<string>().Any())
{
foreach (var fileName in fileNames.OfType<string>())
{
this.BeginInvoke(new Action<string>(this.AttemptOpenFromPath), fileName);
}
}
}
}
我不确定为什么!??评论者可能提供一个理由吗?