Исключение происходит только в DragDrop - PullRequest
1 голос
/ 31 марта 2012

У меня есть приложение WinForms, которое использует библиотеку TaskDialog , которая использует диалоги в стиле Vista из ComCtl32.dll, а для небольших ОС она использует эмулированную форму win ...

Но это не проблема ... Эта библиотека работает нормально, и у нас никогда не было проблем с ней. До сих пор ... На самом деле, если мы запускаем диалог при нормальных обстоятельствах, то он выглядит нормально.

Однако я добавил обработчик перетаскивания в основную форму для захвата путей к файлам, пропущенным из других источников (например, Windows Explorer). Если этот обработчик перетаскивания отображается в первый раз, тогда мы получаем следующее исключение:

Невозможно найти точку входа с именем «TaskDialogIndirect» в DLL «ComCtl32».

Это происходит при обращении сторонней библиотеки к:

    /// <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 ???

Ответы [ 2 ]

3 голосов
/ 31 марта 2012
 [DllImport ( "ComCtl32", ...)]

Библиотека использует довольно тяжелый ярлык для использования Windows DLL библиотеки comctl32.dll.Это имеет тенденцию заканчиваться случайно, но оно падает в вашем коде.Полное объяснение довольно многословно, я постараюсь сделать его кратким.

Основная проблема заключается в том, что Windows имеет две версии comctl32.dll.Версия в c: \ windows \ system32 является устаревшей версией, реализующей общие элементы управления, как они выглядели и работали в 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 (), то, что вам когда-либо нужно делать, когда вы используете потоки.Очевидно, вы отображаете эту форму в другом потоке, а не в основном потоке пользовательского интерфейса.В целом это действительно плохая идея, поток пользовательского интерфейса уже был разработан, чтобы иметь возможность обрабатывать несколько окон.Единственное, чего не произошло, что обычно происходит в потоке пользовательского интерфейса, - это вызов Application.EnableVisualStyles ().Тот, который сообщает Windows, что вы хотите новую версию comctl32.

Вы можете попробовать вызвать его в своем рабочем потоке.Может работать, понятия не имею.Безусловно, лучшее решение - не создавать окна в рабочих потоках.Вы можете избавиться от библиотеки Wonky, используя Windows API Code Pack, он предоставляет оболочку для диалогов задач.

0 голосов
/ 31 марта 2012

Оказалось, что в обработчике DragDrop я должен использовать BeginInvoke для асинхронной очереди вызова в потоке пользовательского интерфейса формы, а не для синхронного ожидания его завершения в обработчике ...

Таким образом, это было решено с помощью:

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);
            }
        }
    }
}

Я не уверен, почему, хотя! ?? Может ли комментатор указать причину?

...