DoDragDrop加上BeginInvoke和MessageBox挂起程序

时间:2015-01-19 23:05:24

标签: c# winforms messagebox freeze begininvoke

MessageBox.Show(...)置于BeginInvoke内导致应用程序冻结。

DoDragDrop似乎是问题的一部分。如果单击选项卡而不拖动,则不会冻结。

所有内容都在UI线程上执行,因此我认为代码是按原样编写的。

代码中是否有问题,或者这是.NET中的错误?

我已经在.NET 3.5 4.0和4.5上测试过,每个都有相同的结果。

这是一个展示行为的最小剥离。

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace DynamicTests {

public class Form2 : Form {

    public Form2() {
        TabControl2 tc = new TabControl2 { Dock = DockStyle.Fill };
        for (int i = 1; i <= 5; i++) {
            TabPage tp = new TabPage("Tab Page " + i);
            tc.TabPages.Add(tp);
        }

        tc.SelectedIndexChanged += tc_SelectedIndexChanged;
        this.Controls.Add(tc);
        this.StartPosition = FormStartPosition.CenterScreen;
        this.Size = new Size(600, 600);
    }

    void tc_SelectedIndexChanged(object sender, EventArgs e) {

        // click and drag a tab + BeginInvoke results in the message
        // freezing the application 
        this.BeginInvoke((Action) delegate {
            MessageBox.Show(this, "Congrats, the program is locked.");

            // one possible solution is to show the message on a thread
            // but the form cannot be used as the parent window
            //Thread t = new Thread(() => {
            //  MessageBox.Show("Congrats, the program is locked.");
            //});
            //t.Start();
        });
    }

}

public class TabControl2 : TabControl {

    private TabPage dragTab2 = null; // tab currently being dragged

    public TabControl2() : base() {
        this.AllowDrop = true; // required for dragging tabs
    }

    private int HotTabIndex { get; set; }

    protected override void OnMouseDown(MouseEventArgs e) {
        base.OnMouseDown(e);
        dragTab2 = null;
        if (HotTabIndex >= 0) {
            dragTab2 = this.TabPages[HotTabIndex];
        }
    }

    bool cancelSelecting = false;

    // For tabs with a [x] close button, if the user clicks the close button, it doesn't make sense to first show the tab and then close it.
    // Also, some tabs are lazy loaded.  The Control the TabPage is hosting is not initialized until the tab is selected.  So it's a short
    // but noticeable time that the control is created (and it may query a database taking more time)... only to be closed.
    //
    // The solution is to intercept the left mouse button and set cancelSelecting to true.  Then in the OnSelecting(...) method, what would
    // normally happen is the tab appears right away.  However, because the user might have clicked the close button, the selection of the
    // tab was moved to the OnMouseUp(...) method.
    //
    // This behavior is similar to windows.  If you click the [X] on a window, and hold the mouse, the window does not close.
    // Thus, you can move the mouse off the button if you change your mind.
    protected override void DefWndProc(ref Message m) {
        const int WM_LBUTTONDOWN = 0x201;
        if (m.Msg == (int) WM_LBUTTONDOWN) {
            cancelSelecting = true;
        }

        base.DefWndProc(ref m);
    }

    protected override void OnSelecting(TabControlCancelEventArgs e) {
        base.OnSelecting(e);
        e.Cancel = cancelSelecting;
    }

    protected override void OnMouseLeave(EventArgs e) {
        base.OnMouseLeave(e);
        HotTabIndex = -1;
    }

    protected override void OnMouseMove(MouseEventArgs e) {
        cancelSelecting = false; // must set to false here so that drag tab works correctly
        base.OnMouseMove(e);

        //// TCM_HITTEST checks to see if the mouse is over a new tab
        TCHITTESTINFO HTI = new TCHITTESTINFO(e.X, e.Y);
        HotTabIndex = SendMessage(this.Handle, TCM_HITTEST, IntPtr.Zero, ref HTI);

        if (e.Button != MouseButtons.Left || dragTab2 == null) // not dragging
            return;

         // we have to do this because we don't select on click
         // if this isn't done then sometimes a sloppy click (click with small drag) wouldn't select the tab
        this.SelectedTab = dragTab2;
        this.DoDragDrop(dragTab2, DragDropEffects.All); // start drag
    }

    protected override void OnDragOver(DragEventArgs e) {
        base.OnDragOver(e);

        TabControl tc = this;

        // a tab is dragged?
        if (e.Data.GetData(typeof(TabPage)) == null)
            return;

        TabPage dragTab = (TabPage) e.Data.GetData(typeof(TabPage));
        TabControl tc2 = dragTab.Parent as TabControl;
        if (tc2 == null)
            return;

        int dragTab_index = tc.TabPages.IndexOf(dragTab);

        // hover over a tab?
        int hoverTab_index = getHoverTabIndex(tc);
        if (hoverTab_index < 0) {
            e.Effect = DragDropEffects.None;
            return;
        }
        TabPage hoverTab = tc.TabPages[hoverTab_index];
        e.Effect = DragDropEffects.Move;

        if (dragTab == hoverTab)
            return;

        // swap dragTab & hoverTab - avoids toggeling
        Rectangle dragTabRect = tc.GetTabRect(dragTab_index);
        Rectangle hoverTabRect = tc.GetTabRect(hoverTab_index);

        if (dragTabRect.Width < hoverTabRect.Width) {
            Point tcLocation = tc.PointToScreen(tc.Location);

            if (dragTab_index < hoverTab_index) {
                if ((e.X - tcLocation.X) > ((hoverTabRect.X + hoverTabRect.Width) - dragTabRect.Width))
                    swapTabPages(tc, dragTab, hoverTab);
            } else if (dragTab_index > hoverTab_index) {
                if ((e.X - tcLocation.X) < (hoverTabRect.X + dragTabRect.Width))
                    swapTabPages(tc, dragTab, hoverTab);
            }
        }
        else
            swapTabPages(tc, dragTab, hoverTab);

        // select new pos of dragTab
        tc.SelectedIndex = tc.TabPages.IndexOf(dragTab);
    }

    private static int getHoverTabIndex(TabControl tc) {
        Point xy = tc.PointToClient(Cursor.Position);
        for (int i = 0; i < tc.TabPages.Count; i++) {
            if (tc.GetTabRect(i).Contains(xy))
                return i;
        }

        return -1;
    }

    private static void swapTabPages(TabControl tc, TabPage src, TabPage dst) {
        int index_src = tc.TabPages.IndexOf(src);
        int index_dst = tc.TabPages.IndexOf(dst);
        tc.TabPages[index_dst] = src;
        tc.TabPages[index_src] = dst;
        tc.Refresh();
    }

    [DllImport("user32.dll")]
    private static extern int SendMessage(IntPtr hwnd, int msg, IntPtr wParam, ref TCHITTESTINFO lParam);

    [StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    private struct TCHITTESTINFO {
        public Point pt;
        public TCHITTESTFLAGS flags;
        public TCHITTESTINFO(int x, int y) {
            pt = new Point(x, y);
            flags = TCHITTESTFLAGS.TCHT_NOWHERE;
        }
    }

    [Flags]
    private enum TCHITTESTFLAGS {
        TCHT_NOWHERE = 1,
        TCHT_ONITEMICON = 2,
        TCHT_ONITEMLABEL = 4,
        TCHT_ONITEM = TCHT_ONITEMICON | TCHT_ONITEMLABEL
    }

    private const int TCM_FIRST = 0x1300;
    private const int TCM_HITTEST = TCM_FIRST + 13;

}


}

/*
    Call Stack when hang:

    [Managed to Native Transition]  
    System.Windows.Forms.dll!System.Windows.Forms.MessageBox.ShowCore(System.Windows.Forms.IWin32Window owner, string text, string caption, System.Windows.Forms.MessageBoxButtons buttons, System.Windows.Forms.MessageBoxIcon icon, System.Windows.Forms.MessageBoxDefaultButton defaultButton, System.Windows.Forms.MessageBoxOptions options, bool showHelp) + 0x44a bytes  
    System.Windows.Forms.dll!System.Windows.Forms.MessageBox.Show(System.Windows.Forms.IWin32Window owner, string text) + 0x3a bytes    
>   DynamicTests.exe!DynamicTests.Form2.tc_SelectedIndexChanged.AnonymousMethod__1() Line 28 + 0x16 bytes   C#
    [Native to Managed Transition]  
    mscorlib.dll!System.Delegate.DynamicInvokeImpl(object[] args) + 0x6a bytes  
    System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbackDo(System.Windows.Forms.Control.ThreadMethodEntry tme) + 0x97 bytes    
    System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(object obj) + 0xef bytes    
    mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x285 bytes 
    mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x9 bytes   
    mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x57 bytes    
    System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallback(System.Windows.Forms.Control.ThreadMethodEntry tme) + 0x113 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.Control.InvokeMarshaledCallbacks() + 0x10c bytes  
    System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m) + 0x2b8 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.Form.WndProc(ref System.Windows.Forms.Message m) + 0x1ee bytes    
    System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam) + 0x145 bytes    
    [Native to Managed Transition]  
    [Managed to Native Transition]  
    System.Windows.Forms.dll!System.Windows.Forms.Control.DoDragDrop(object data, System.Windows.Forms.DragDropEffects allowedEffects) + 0x187 bytes    
    DynamicTests.exe!DynamicTests.TabControl2.OnMouseMove(System.Windows.Forms.MouseEventArgs e) Line 97 + 0x20 bytes   C#
    System.Windows.Forms.dll!System.Windows.Forms.Control.WmMouseMove(ref System.Windows.Forms.Message m) + 0x89 bytes  
    System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m) + 0xe64 bytes 
    System.Windows.Forms.dll!System.Windows.Forms.TabControl.WndProc(ref System.Windows.Forms.Message m) + 0x416 bytes  
    System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.DebuggableCallback(System.IntPtr hWnd, int msg, System.IntPtr wparam, System.IntPtr lparam) + 0x145 bytes    
    [Native to Managed Transition]  
    [Managed to Native Transition]  
    System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(System.IntPtr dwComponentID, int reason, int pvLoopData) + 0x681 bytes    
    System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason, System.Windows.Forms.ApplicationContext context) + 0x56f bytes  
    System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) + 0x6f bytes    
    DynamicTests.exe!DynamicTests.Program.Main() Line 149 + 0x2a bytes  C#
*/

0 个答案:

没有答案