WinForms: почему я получаю InvalidCastException при отображении диалогового окна браузера папки? - PullRequest
6 голосов
/ 11 мая 2010

Я случайно получаю InvalidCastException при отображении FolderBrowserDialog, и многие клиенты сообщают об этом.

Мне не удалось найти что-либо релевантное в Интернете. Кто-нибудь знает, что вызывает это / как это исправить?

Мой код:

        using (FolderBrowserDialog fbd = new FolderBrowserDialog())
        {
            fbd.ShowNewFolderButton = false;
            if (fbd.ShowDialog() == DialogResult.OK)

Трассировка стека:

Error: System.InvalidCastException: 
'Unable to cast object of type 'System.__ComObject' to type 'IMalloc'.'.

    Stack trace:    
at System.Windows.Forms.UnsafeNativeMethods.Shell32.SHGetMalloc(IMalloc[] ppMalloc)
at System.Windows.Forms.FolderBrowserDialog.GetSHMalloc()
at System.Windows.Forms.FolderBrowserDialog.RunDialog(IntPtr hWndOwner)
at System.Windows.Forms.CommonDialog.ShowDialog(IWin32Window owner)
at System.Windows.Forms.CommonDialog.ShowDialog()

РЕДАКТИРОВАТЬ: Дополнительная информация: я смог воспроизвести это только при работе в отладчике VS2008.

Когда заканчивается отладчик, это происходит очень редко (случается один или два раза в 6 месяцев) на моей 64-битной Windows 7 и исчезает после перезагрузки.

Клиенты, конечно, не запускают приложение в отладчике, поэтому оно наверняка воспроизводимо из отладчика.

Ответы [ 4 ]

1 голос
/ 11 мая 2010

Вот пара мыслей:

Насколько я могу судить по использованию Reflector.Net, это происходит в блоке finally сразу после фактического возврата диалога. Вот в основном, где вы получаете проблему:

IntPtr pszPath = IntPtr.Zero;
try
{
    UnsafeNativeMethods.BROWSEINFO lpbi = new UnsafeNativeMethods.BROWSEINFO();
    hglobal = Marshal.AllocHGlobal((int) (260 * Marshal.SystemDefaultCharSize));
    pszPath = Marshal.AllocHGlobal((int) (260 * Marshal.SystemDefaultCharSize));
    ... /*init structure*/
    pidl = UnsafeNativeMethods.Shell32.SHBrowseForFolder(lpbi);
    if (pidl != IntPtr.Zero)
    {
        UnsafeNativeMethods.Shell32.SHGetPathFromIDList(pidl, pszPath);
        ...
    }
}
finally
{
    UnsafeNativeMethods.IMalloc sHMalloc = GetSHMalloc(); /* Boom! */
    sHMalloc.Free(zero);
    ...

Если вы вообще не видите диалоговое окно, исключение, приведенное выше, вероятно, маскирует реальную ошибку. Попробуйте запустить с «Break on Exception» и отключите Tools-> Debugging-> Just my code. Код в блоке try выглядит довольно простым, и самое рискованное, что они делают, это PInvoke для SHBrowseForFolder в shell32.dll. Я был бы удивлен, если он генерирует «случайную» ошибку.

Если вы видите диалог и только после закрытия вы получаете эту ошибку, то вы можете просто проигнорировать ее за счет утечки памяти, когда это произойдет:

    using (FolderBrowserDialog fbd = new FolderBrowserDialog())
    {
        fbd.ShowNewFolderButton = false;
        DialogResult r;
        try { r = fbd.ShowDialog(); }
        catch (InvalidCastException) 
        { r = DialogResult.OK; /* you might check the path first */ }
        if (fbd.ShowDialog() == DialogResult.OK)
            ...

Конечно, вы всегда можете P самостоятельно вызвать SHBrowseForFolder и не использовать класс диалога.

0 голосов
/ 12 мая 2010

У меня была почти такая же проблема (также InvalidCastException) в моем проекте, которая возникала только иногда.

Он пришел из потока, который не работал как STAThread. Хотя мой метод Main был помечен атрибутом [STAThread].

Вы сказали, что не используете отдельный поток. Но, возможно, вы не знаете о из-за асинхронного делегата, который явно не использует класс Thread, но рассматривается как один.

Если вы создаете новые потоки (не имеет значения, создаете ли вы его с помощью ThreadPool или асинхронного делегата), они всегда являются потоками MTA . Таким образом, вы должны создать свой поток самостоятельно и начать его явно как STAThread.

Вы можете сделать это следующим образом:

var thread=new Thread( () => method() );
thread.SetApartmentState(ApartmentState.STA);
thread.Start();

Я думаю, вам нужно копать в этом направлении, чтобы найти ошибку.

0 голосов
/ 12 мая 2010

Вы можете рассмотреть возможность использования Windows® API Code Pack для Microsoft® , если вам не требуется поддержка XP или Windows 2003. Возможно, диалоговое окно браузера папок не только красивее, но и стабильнее .

0 голосов
/ 11 мая 2010

Кажется, этот симптом произошел с другими , так что, по крайней мере, вы не одиноки; -)

Пара возможностей:

  1. Вы используете это в однопоточной квартире (то есть с [STAThreadAttribute] в методе точки входа)?
  2. Максимальная длина пути в Windows составляет 260 символов. Может ли начальный путь, используемый FolderBrowserDialog, быть длиннее, чем этот? Если вы можете (иногда) воспроизвести это в режиме отладки VS, попробуйте переместить ваше решение выше в дереве папок, тем самым сократив путь к папке по умолчанию, используемый в диалоговом окне.
...