views:

215

answers:

4

I am randomly getting InvalidCastException when showing FolderBrowserDialog and also many clients have reported this.

I have not been able to find anything relevant on the internet. Does anyone know what causes this/how to fix this?

My code:

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

Stack trace:

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()

EDIT: Additional information: I have been able to reproduce this only when running in VS2008 debugger.

When running out of debugger, it happens only very rarely (happened once or twice in 6 months) on my 64 bit Windows 7 and goes away after restart.

The clients are certainly not running the app in debugger so it is surely reproducible out of debugger.

+1  A: 

Here is a couple of thoughts:

As far as I can tell from using Reflector.Net this is getting thrown in the finally block just after the actual dialog returns. Here is basically where your getting the problem:

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

If your not seeing the dialog at all the exception above is probably masking the real error. Try running with 'Break on Exception' and disable Tools->Debugging->Just my code. The code in the try block looks pretty basic, the most risky thing they are doing is PInvoke on shell32.dll's SHBrowseForFolder I'd be surprised if it's generating a 'random' error.

If you are seeing the dialog and only upon closing do you get this error then you could just ignore it at the expense of leaking memory when this happens:

    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)
            ...

Of course you can always PInvoke the SHBrowseForFolder yourself and not use the dialog class.

csharptest.net
Yes, I am getting this after the dialog is closed. Thanks for the catch tip, I think I will have to go this path but I am not sure if the selectedPath will be correct. Will check tommorrow. Are you suggesting to show the dialog twice?
Marek
Sorry, yea the second show is a bug.
csharptest.net
A: 

This symptom seems to have happened to others, so at least you are not alone ;-)

A couple of possibilities:

  1. Are you running this in a single threaded apartment (i.e. with the [STAThreadAttribute] on the entry point method)?
  2. The maximum path length in Windows is 260 characters. Could the initial path used by the FolderBrowserDialog be longer than this? If you can (occasionally) reproduce this in VS debug mode, try moving your solution higher in your folder tree, thus shortening the default folder path used by the dialog.
Simon Chadwick
1. of course :) 2. no, path length is not an issue here.
Marek
A: 

You might consider using the Windows® API Code Pack for Microsoft® if you do not need to support XP or Windows 2003. Maybe its folder browser dialog is not only more beautiful but also more stable...

Dejan
I need to support all windows versions, including WinXP
Marek
A: 

I had almost the same Problem (also an InvalidCastException) in my project, which only occured sometimes.

It came from a Thread that hasn't been running as a STAThread. Although my Main method was tagged with the [STAThread] Attribute.

You said, that you'r not using a separate thread. But perhaps you'r not aware of, because of an async delegate, which does not explicit uses a Thread class, but treated as one.

If you create new threads, (doesn't matter if you create it with the ThreadPool or an async delegate), they are always MTA Threads. So you have to create your Thread by your own and starting it explicit as an STAThread.

You can do this like:

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

I think you have to dig in that direction to find the bug.

BitKFu
This folder browser dialog is shown directly from a button.Click event handler. No delegates/BeginInvokes/... that could hide a threading issue.
Marek