一個關於.Net的SaveFileDialog控制項(Winform)的有趣問題

來源:互聯網
上載者:User

標籤:

情境:winform的程式中,有一個畫面上放了一個Button,點擊這個Button會調用.Net控制項SaveFileDialog的ShowDialog方法。

 

情境很簡單,但是碰到了這樣一個有趣的問題:

在機器很慢的情況下,連續快速兩次點擊上述Button,會導致棧溢出異常(StackOverflowException)。

 

由於機器很慢的情況難以類比且不能穩定重現,嘗試在Button的點擊事件中先用非同步委託調一次SaveFileDialog.ShowDialog,然後再正常調用一次SaveFileDialog.ShowDialog,然後。。。問題重現了!

 

SaveFileDialog的基類CommonDialog代碼如下:

 1 public DialogResult ShowDialog(IWin32Window owner) 2 { 3     IntSecurity.SafeSubWindows.Demand(); 4     if (!SystemInformation.UserInteractive) 5     { 6         throw new InvalidOperationException(SR.GetString("CantShowModalOnNonInteractive")); 7     } 8     NativeWindow window = null; 9     IntPtr zero = IntPtr.Zero;10     DialogResult cancel = DialogResult.Cancel;11     try12     {13         if (owner != null)14         {15             zero = Control.GetSafeHandle(owner);16         }17         if (zero == IntPtr.Zero)18         {19             zero = UnsafeNativeMethods.GetActiveWindow();20         }21         if (zero == IntPtr.Zero)22         {23             window = new NativeWindow();24             window.CreateHandle(new CreateParams());25             zero = window.Handle;26         }27         if (helpMsg == 0)28         {29             helpMsg = SafeNativeMethods.RegisterWindowMessage("commdlg_help");30         }31         NativeMethods.WndProc d = new NativeMethods.WndProc(this.OwnerWndProc);32         this.hookedWndProc = Marshal.GetFunctionPointerForDelegate(d);33         IntPtr userCookie = IntPtr.Zero;34         try35         {36             this.defOwnerWndProc = UnsafeNativeMethods.SetWindowLong(new HandleRef(this, zero), -4, d);37             if (Application.UseVisualStyles)38             {39                 userCookie = UnsafeNativeMethods.ThemingScope.Activate();40             }41             Application.BeginModalMessageLoop();42             try43             {44                 cancel = this.RunDialog(zero) ? DialogResult.OK : DialogResult.Cancel;45             }46             finally47             {48                 Application.EndModalMessageLoop();49             }50             return cancel;51         }52         finally53         {54             IntPtr windowLong = UnsafeNativeMethods.GetWindowLong(new HandleRef(this, zero), -4);55             if ((IntPtr.Zero != this.defOwnerWndProc) || (windowLong != this.hookedWndProc))56             {57                 UnsafeNativeMethods.SetWindowLong(new HandleRef(this, zero), -4, new HandleRef(this, this.defOwnerWndProc));58             }59             UnsafeNativeMethods.ThemingScope.Deactivate(userCookie);60             this.defOwnerWndProc = IntPtr.Zero;61             this.hookedWndProc = IntPtr.Zero;62             GC.KeepAlive(d);63         }64     }65     finally66     {67         if (window != null)68         {69             window.DestroyHandle();70         }71     }72     return cancel;73 }

注意第36行代碼:

this.defOwnerWndProc = UnsafeNativeMethods.SetWindowLong(new HandleRef(this, zero), -4, d);

上面的d指向這裡:

 1 protected virtual IntPtr OwnerWndProc(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) 2 { 3     if (msg != helpMsg) 4     { 5         return UnsafeNativeMethods.CallWindowProc(this.defOwnerWndProc, hWnd, msg, wparam, lparam); 6     } 7     if (NativeWindow.WndProcShouldBeDebuggable) 8     { 9         this.OnHelpRequest(EventArgs.Empty);10     }11     else12     {13         try14         {15             this.OnHelpRequest(EventArgs.Empty);16         }17         catch (Exception exception)18         {19             Application.OnThreadException(exception);20         }21     }22     return IntPtr.Zero;23 }

它使用了自己的訊息處理器替代了SaveFileDialog所屬畫面的訊息處理器,然後把畫面的訊息處理器暫存在this.defOwnerWndProc中,然後在自己的訊息處理中再轉調畫面的訊息處理器(見上面第5行)。

簡單來說,Dialog攔截了Form的訊息處理,在自己的訊息處理器處理完後,再將訊息分發會Form。

 

基於以上處理,如果連續調兩次ShowDialog會如何??

第一次是OK的,但是當第二次執行第36行的代碼時,SetWindowLong將會返回之前的處理器,即Dialog自己的訊息處理器,然後儲存在this.defOwnerWndProc中,等到Dialog自己的訊息處理器處理完後,想要將訊息再分發給Form時,this.defOwnerWndProc已經被修改為自己的訊息處理器,然後就沒有然後了。。。

一個關於.Net的SaveFileDialog控制項(Winform)的有趣問題

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.