I got no takers and eventually figured this out.
The answer is to monitor the QueryContinueDrag event. This event fires continually during drag drop operation. The QueryContinueDragEventArgs contain an Action property of type enum DragAction, which is either DragAction.Cancel, DragAction.Drop or DragAction.Continue. It is a read/write property in order to allow you to change the standard behaviour (we don't need this).
This example code assumes DragDropInProgress flag is set at the start of a drag drop and reset when a drag drop is completed successfully. It catches a DragDrop ending because the user has let go of the mouse without being over a drag drop target (the drag drop targets are MyControl1 and MyControl2) or cancels the drag drop. If you don't care if the DragDropInProgressFlag is reset before your DragDrop events fires you can dispense with the hit test and just reset the flag.
Private Sub MyControl_QueryContinueDrag(ByVal sender As Object, ByVal e As System.Windows.Forms.QueryContinueDragEventArgs) Handles MyControl.QueryContinueDrag
Dim MousePointerLocation As Point = MousePosition
If e.Action = DragAction.Cancel Then '' User pressed the Escape button
DragDropInProgressFlag = False
End If
If e.Action = DragAction.Drop Then
If Not HitTest(new {MyControl1, MyControl2}, MousePointerLocation) Then
DragDropInProgressFlag = False
End If
End If
End Sub
Private Function HitTest(ByVal ctls() As Control, ByVal p As Point) As Boolean
HitTest = False
For Each ctl In ctls
Dim ClientPoint As Point = ctl.PointToClient(p)
HitTest = HitTest Or (ClientPoint.X >= 0 AndAlso ClientPoint.Y >= 0 AndAlso ClientPoint.X <= ctl.Width AndAlso ClientPoint.Y <= ctl.Height)
If HitTest Then Exit For
Next
End Function
In this example HitTest is a rountine that takes a Mouse position (screen co-ordinate) and an array of controls and sifts through the array passing True if the mouse position is in any of the controls rectangles.