none
Treeview dragdrop event ignores exceptions! HELP

    Question

  •  

    Hi,

    I have a windows.forms application in C# using a treeview with dragdrop capability, however I have found when an exception occurs in the DragDrop event and is unhandled, .Net ignores it (instead of showing the exception dialog)

    Attached I have some test code where I explicitly throw an exception in the DragDrop event, nothing happens!

    Any ideas?

    thanks

    Brian

     

    public partial class Form1 : Form

    {

    public Form1()

    {

    InitializeComponent();

    }

    private void treeView1_DragDrop(object sender, DragEventArgs e)

    {

    throw new ArgumentException();

    }

    private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)

    {

    DoDragDrop("!", DragDropEffects.Copy);

    }

    private void treeView1_DragOver(object sender, DragEventArgs e)

    {

    e.Effect = DragDropEffects.Copy;

    }

    }

    Wednesday, November 22, 2006 10:11 AM

Answers

  • Even with drag-and-drop within the same application, the drag-and-drop is handled through the standard OLE drag-drop mechanism.  From OLE's point of view it's dealing with two applications, the source and the target and decouples them appropriately.  Since OLE has been around far longer than .NET, OLE has no concept of a .NET exception and therefore can't communicate an exception from the target back to the source.  Even if it could, why should the source care that the target couldn't perform the drop?

    If you want to handle an exception during a DragDrop event you must handle it within your DragDrop event handler, it won't propagate beyond that event handler because there is a managed to unmanaged to managed code transition between the source and the target.
    Tuesday, December 05, 2006 3:26 PM

All replies

  •  

    FYI (For whoever is interested)

    I have discovered why this behaviour occurs, it is because you can DragDrop from other applications, if these other applications should throw an exception you don't want those exceptions bubbling up through your application.

    I have not found a work around for this yet, as I WANT unhandled exceptions to be eventually thrown in my app. (So as to find bugs during testing) My application doesn't really support drag and drop from other apps so perhaps I might just use the mouse up event or something....

    Hope this helps others....

    Monday, November 27, 2006 1:17 PM
  • Hey, I was having a similar problem (not drag-drop related), but certain events were supressing any exceptions being thrown that I didn't catch.

    I solved the problem by going into the Exception Debugging options at:
    Debug > Exceptions

    Then clicking the checkbox for "Common Language Runtime Exceptions" under the "User-unhandled" column.

    By default this is supposed to be checked, so I'm not sure why mine wasn't, but you may want to check that...

    Saturday, December 02, 2006 7:04 PM
  •  

    Thanks for your response, I am aware of this option but I was more worried about exceptions not being handled in run-time say on a clients machine or a testers machine rather than on my development machine.

    The only workaround I could find was to put a try..catch all exceptions in the DragDrop event method around my code and display a messagebox detailing the exception information. Ideally no exception should occur here but if it does I want to know about it!

     

     

    Monday, December 04, 2006 1:40 PM
  • If the other application has a problem (exception or otherwise) your application won't be affect (other than the drag-drop operation halts).  An application will *never* get the exceptions from another.
    Monday, December 04, 2006 4:37 PM
  •  

    Agreed, this is the reason I believe why unhandled exceptions don't get caught at all, however I am doing drag and drop within the same/one application! i.e. From one treeview to another and unhandled exceptions are still ignored in the DragDrop event. (Even if you drag drop within the same treeview this happens)

     

    Tuesday, December 05, 2006 2:10 PM
  • It's intriguing.  I'll have a closer look.  In the meantime, have you tried with different types of controls and this only happens with TreeView?  Or, is it a general drag-drop in .NET issue?
    Tuesday, December 05, 2006 2:41 PM
  • Even with drag-and-drop within the same application, the drag-and-drop is handled through the standard OLE drag-drop mechanism.  From OLE's point of view it's dealing with two applications, the source and the target and decouples them appropriately.  Since OLE has been around far longer than .NET, OLE has no concept of a .NET exception and therefore can't communicate an exception from the target back to the source.  Even if it could, why should the source care that the target couldn't perform the drop?

    If you want to handle an exception during a DragDrop event you must handle it within your DragDrop event handler, it won't propagate beyond that event handler because there is a managed to unmanaged to managed code transition between the source and the target.
    Tuesday, December 05, 2006 3:26 PM
  •  

    Thanks Peter, this clears it up for me, the drag and drop mechanism is performed via OLE. Which makes sense of course if you drag/dropping between applications.

     

    Tuesday, December 05, 2006 4:14 PM
  • While first suspecting the try/catch handler in Control.DoDragDrop, it is actually the COM DoDragDrop() function that swallows the exceptions.  Even a null reference exception (see sample below) is dismissed without causing the COM DoDragDrop() loop to exit.  This is questionable tactic and not documented anywhere I see.  Nothing that the framework can do, it needs to rely on the COM implementation.  Really serious runtime errors, like StackOverflowException, do however get caught by the CLR.

    Here's the test program I used:

    using System;
    using System.Security;
    using System.Windows.Forms;

    namespace WindowsApplication1 {
      public partial class Form1 : Form {
        public Form1() {
          InitializeComponent();
          this.AllowDrop = true;
        }
        private void treeView1_ItemDrag(object sender, ItemDragEventArgs e) {
          DoDragDrop(e.Item, DragDropEffects.Copy);
        }
        private void Form1_DragEnter(object sender, DragEventArgs e) {
          unsafe {
            int* p = null;
            *p = 0;
          }
        }
      }
    }
    Tuesday, December 05, 2006 4:29 PM
    Moderator
  •  

    I was quite surprised too to find that this was not documented anywhere, I wonder is there anyway of intializing .net applications to catch unhandled COM Exceptions perhaps?  similar to how you can set up applications to catch unhandled thread exceptions. If I get some time today or later this week I'll see if I can find some more info....

    In any case I have found an in between solution I have simply put a try..catch ALL excpetions in my DragDrop event and display a message box with the exception.stacktrace so at least know something has gone wrong....

     

     

     

    Wednesday, December 06, 2006 9:33 AM
  • Well, it's not "undocumented" per se.  It's an overlap in the term "exception".  With .NET certain native "exceptions" are wrapped in a framework Exception-based class.  For example, an access violation is caught by the framework and raised as an AccessViolationException.  Since the cause of the AccessViolationException is native that can be passed back over the managed-unmanaged-managed threshold.  Other  exceptions that are unique to .NET (i.e. they don't wrap a native error reporting artifact) like "throw new ArgumentException();" have no direct correlation to a native error reporting artifact and ergo can't make they're way back.
    Wednesday, December 06, 2006 3:56 PM
  •  nobugz wrote:
    While first suspecting the try/catch handler in Control.DoDragDrop, it is actually the COM DoDragDrop() function that swallows the exceptions. 
    Native code can't "swallow" managed exceptions.  The CLR just doesn't "marshal" all of them back to native code...
    Wednesday, December 06, 2006 3:58 PM
  • Hmm, it certainly can by handling the underlying SEH exception.  This blog post is relevant.  Chris specially talks about what a COM method should do when a managed exception is thrown (near the end of the section "COM error handling").  Choice #2 is exactly what the DoDragDrop() API function appears to do.  Makes sense, considering that COM methods aren't allowed to propagate exceptions.
    Wednesday, December 06, 2006 9:25 PM
    Moderator
  • I got out of Chris' blog that SEH isn't used for exception propagation from managed code to unmanaged code via COM.  Although, he's not all that clear: "Unfortunately, the most broken of the three is the last one… and that’s the one we currently follow." where the "last one" is: "Convert the exception object into an HRESULT value", but slightly contradicts with "But this time the NullReferenceException will be mapped to an SEH exception code of 0xE0434F4D"; he details that the HRESULT of a COM method is used when exceptions occur in managed code and the client can use the IErrorInfo interface to get more detail.  While a SEH may be used for certain exceptions (like NullReferenceException) originating in managed code as a result of call from unmanaged code, Chris details that one SEH code (0xE0434F4D) is used for all managed exceptions.  Which doesn't give much to the unmanaged client to give back to it's managed client (in a managed-to-unmanaged-to-managed scenario).

    But, I don't think it really matters.  This is directed more at Brian: (ignoring the whole managed-to-unmanaged-to-managed interop details and the case of managed-to-unmanaged) how can exceptions coming from an arbitrary managed drag-drop target be propagated back through the call to DoDragDrop in the general case?  What if the drag-drop target threw an Exception-derived type local only to it?  The managed drag-drop source would have to explicitly reference that assembly and explicitly handle that exception.  And what if the drag-drop source didn't handle that (or some other system exception): the drag-drop target now causes the drag-drop source to terminate from an unhandled exception, not good.  This would completely couple the two applications, coupling that OLE Drag and Drop is explicitly designed to avoid.

    Best case--and OLE Drag and Drop can handle this--is the drag-drop source is informed of success of the drop (the Win32 DoDragDrop is documented as returning E_UNSPEC if the target wasn't successful); but, I don't see what the drag-drop source can really do with that information.

    Thursday, December 07, 2006 10:32 AM
  •  Peter Ritchie wrote:
    But, I don't think it really matters.  This is directed more at Brian: (ignoring the whole managed-to-unmanaged-to-managed interop details and the case of managed-to-unmanaged) how can exceptions coming from an arbitrary managed drag-drop target be propagated back through the call to DoDragDrop in the general case?  What if the drag-drop target threw an Exception-derived type local only to it?  The managed drag-drop source would have to explicitly reference that assembly and explicitly handle that exception.  And what if the drag-drop source didn't handle that (or some other system exception): the drag-drop target now causes the drag-drop source to terminate from an unhandled exception, not good.  This would completely couple the two applications, coupling that OLE Drag and Drop is explicitly designed to avoid.

    Hi Peter,

    I agree with what you say, it makes perfect sense but what I actually was expecting was that the Drag/Drop TARGET would handle the exception occuring on dropping.  I was expecting the Target to at least notify this (with the usual unhandled exception has occured dialog box) but this may be that I misunderstood the Drag/Drop mechanism or confused myself as I am doing drop and drop just within my own application and hadn't considered drag/drop BETWEEN applications.

    thanks nobugz for the link to that blog, I've printed it out for some bedtime reading!

    Brian

     

    Thursday, December 07, 2006 1:46 PM