【问题标题】:Exception only occuring on DragDrop仅在拖放时发生异常
【发布时间】:2012-03-30 20:09:02
【问题描述】:

我有一个使用 TaskDialog library 的 WinForms 应用程序,它利用 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 );

如果已显示对话框,则处理程序将运行正常。

表单的 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 处理程序时引发异常的任何想法/解决方案???

【问题讨论】:

  • 这取决于库的工作方式。
  • 异常发生在什么操作系统上?
  • 该错误告诉您,当您尝试调用 TaskDialogIndirect 函数时,它不可用。该功能在 Windows Vista 之前不存在,因此在旧版本的 Windows 上,它将失败。它与拖放无关。
  • @SLaks - 它发生在我的 Windows 7 机器上。但是,我对它通常工作的事实感到困惑(建议存在依赖的 Comctl32 库)......但只有在第一次通过 DragDrop 处理程序引发对话框时才会失败。
  • DragDrop 处理程序中的线程有什么不寻常的地方吗?

标签: c# winforms winforms-interop comctl32 taskdialog


【解决方案1】:
 [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 代码包摆脱不稳定的库,它为任务对话框提供了一个包装器。

【讨论】:

  • 我并不是专门试图在非 UI 线程上打开对话框...但是,我猜 DragDrop 处理程序正在从该线程外部捕获丢弃的项目(例如,另一个应用程序,如 Windows 资源管理器) 确实会导致它处理 UI 线程。
  • 至于使用 Windows API 代码包 - 这在我们的计划中绝对...这要好得多。现在我得抓紧时间了!
  • 不,拖放不会创建线程。在某些时候,您收到 InvalidOperationException 并发现您需要使用 Control.Invoke 来解决问题。但是,这不起作用,您应该想知道“为什么我需要解决这个问题!”。常见的情况是某种在线程池线程上触发的 I/O 完成回调。就像一个插座一样。您可以在堆栈跟踪中看到它。
【解决方案2】:

事实证明,在 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);
            }
        }
    }
}

我不知道为什么!??评论者能否提供理由?

【讨论】:

    猜你喜欢
    • 2010-11-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-08
    • 2020-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多