トップ回答者
DataGridView垂直スクロールバーの操作中にフォームクローズすると、ObjectDisposedExceptionが発生する

質問
-
環境
- .net framework2.0
- visual studio2005
- windows7 Ultimate 32bit
症状
DataGridViewの垂直スクロールバーを操作しているときにフォームがクローズされると、ObjectDisposedExceptionが発生します。 例外を補足する場合、ウィンドウメッセージの処理でエラーになるみたいで(よくわかってません)、Application.Run()を実行しているMain関数で補足する必要がありますが、 実際にはサブフォームで発生しているので親フォームで補足したいのです。どのように対応すればいいでしょうか?また、例外発生させない方法がありましたらそちらもよろしくお願いします。
エラーメッセージ
System.ObjectDisposedException はハンドルされませんでした。 Message=破棄されたオブジェクトにアクセスできません。 オブジェクト名 'VScrollBar' です。 Source=System.Windows.Forms ObjectName=VScrollBar StackTrace: 場所 System.Windows.Forms.Control.CreateHandle() 場所 System.Windows.Forms.Control.get_Handle() 場所 System.Windows.Forms.Control.set_CaptureInternal(Boolean value) 場所 System.Windows.Forms.Control.WmMouseDown(Message& m, MouseButtons button, Int32 clicks) 場所 System.Windows.Forms.Control.WndProc(Message& m) 場所 System.Windows.Forms.ScrollBar.WndProc(Message& m) 場所 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) 場所 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) 場所 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam) 場所 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg) 場所 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData) 場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context) 場所 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context) 場所 System.Windows.Forms.Application.Run(Form mainForm) 場所 DataGridViewTest.Program.Main() 場所 C:\Dropbox\My Dropbox\src\Visual Studio 2010\Projects\DataGridViewTest\DataGridViewTest\Program.cs:行 18 場所 System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) 場所 System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) 場所 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 場所 System.Threading.ThreadHelper.ThreadStart_Context(Object state) 場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) 場所 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 場所 System.Threading.ThreadHelper.ThreadStart() InnerException:
再現コード
using System; using System.ComponentModel; using System.Windows.Forms; using System.Threading; namespace DataGridViewTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { dataGridView1.Dock = DockStyle.Fill; SetupColumn(); for (int i = 0; i < 100; i++) AddRow(i); Thread thread = new Thread((ThreadStart)delegate { Thread.Sleep(3000); this.Invoke((MethodInvoker)delegate{ Close(); }); }); thread.Start(); } private void SetupColumn() { for (int i = 0; i < 5; i++) { DataGridViewColumn col = new DataGridViewTextBoxColumn(); dataGridView1.Columns.Add(col); } } private void AddRow(int rIdx) { DataGridViewRow row = new DataGridViewRow(); row.CreateCells(dataGridView1); for (var i = 0; i < row.Cells.Count; i++) row.Cells[i].Value = string.Format("行{0}:列{1}", rIdx, i); dataGridView1.Rows.Add(row); } } }
================================================== はむ日記 - http://d.hatena.ne.jp/ham007/
回答
-
問題は、「処理するべき Window メッセージが存在する状態で、メッセージポンプを回しているプライマリスレッドと非同期にウィンドウを閉じる」ことにあります。
なので、それを避ければよいわけです。
例えば
- 「外部からの通知」を受けたらフラグを立てる
- ただし↑フラグが立っていたら、「外部からの通知」をうけても何もしない
- Application.Idle で↑フラグの状態を監視して、フラグが立っていたら必要な処理を実行する
- 処理が完了したらフラグを OFF にする
のような雰囲気で実現できそうです。
あるいは
- 「外部からの通知」を受けたら BeginInvoke() で必要な処理の非同期実行を仕掛ける
- ただし↑既に非同期実行の完了待ちなら、「外部からの通知」をうけても何もしない
- 非同期実行の完了を待機
とか。
他にもいろんなパターンが考えられると思います。
- 回答としてマーク ham007 2011年1月16日 4:55
すべての返信
-
返信ありがとうございます。
>例示のコードだと、「どういう目的があって、ユーザ操作と非同期にフォームを閉じようとしているのか」が分かりません。
>どういうシナリオを実行しようとしていて、ユーザ操作と非同期にフォームを閉じるような自体が起きているのでしょうか?
”外部からの通知(別端末から送られてくる信号)をメインフォームで受け取ると、メインフォームが開いているサブフォーム全てを閉じてメインフォームの表示初期化を行なう。”
ということをやろうとしています。サブフォームのひとつに情報一覧表示するものがあるのですが、そこでDataGridViewを使用しています。
はむ日記 - http://d.hatena.ne.jp/ham007/ -
問題は、「処理するべき Window メッセージが存在する状態で、メッセージポンプを回しているプライマリスレッドと非同期にウィンドウを閉じる」ことにあります。
なので、それを避ければよいわけです。
例えば
- 「外部からの通知」を受けたらフラグを立てる
- ただし↑フラグが立っていたら、「外部からの通知」をうけても何もしない
- Application.Idle で↑フラグの状態を監視して、フラグが立っていたら必要な処理を実行する
- 処理が完了したらフラグを OFF にする
のような雰囲気で実現できそうです。
あるいは
- 「外部からの通知」を受けたら BeginInvoke() で必要な処理の非同期実行を仕掛ける
- ただし↑既に非同期実行の完了待ちなら、「外部からの通知」をうけても何もしない
- 非同期実行の完了を待機
とか。
他にもいろんなパターンが考えられると思います。
- 回答としてマーク ham007 2011年1月16日 4:55