开发者

WinForms: Why do I get InvalidCastException when showing folder browser dialog?

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.


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.


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜