Browse Source

WinForms designer: Add workaround for InvalidOperationException in DesignerHost.Dispose

Also, get rid of some unexpected reentrancy.
4.0
Daniel Grunwald 14 years ago
parent
commit
e10d969891
  1. 53
      src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs
  2. 40
      src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs

53
src/AddIns/DisplayBindings/FormsDesigner/Project/Src/DesignerViewContent.cs

@ -10,6 +10,7 @@ using System.ComponentModel.Design.Serialization; @@ -10,6 +10,7 @@ using System.ComponentModel.Design.Serialization;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using ICSharpCode.Core;
@ -403,16 +404,10 @@ namespace ICSharpCode.FormsDesigner @@ -403,16 +404,10 @@ namespace ICSharpCode.FormsDesigner
bool savedIsDirty = (this.DesignerCodeFile == null) ? false : this.DesignerCodeFile.IsDirty;
this.UserContent = this.pleaseWaitLabel;
Application.DoEvents();
if (this.DesignerCodeFile != null) {
this.DesignerCodeFile.IsDirty = savedIsDirty;
}
// We cannot dispose the design surface now because of SD2-451:
// When the switch to the source view was triggered by a double-click on an event
// in the PropertyPad, "InvalidOperationException: The container cannot be disposed
// at design time" is thrown.
// This is solved by calling dispose after the double-click event has been processed.
if (designSurface != null) {
designSurface.Loading -= this.DesignerLoading;
designSurface.Loaded -= this.DesignerLoaded;
@ -435,12 +430,46 @@ namespace ICSharpCode.FormsDesigner @@ -435,12 +430,46 @@ namespace ICSharpCode.FormsDesigner
selectionService.SelectionChanged -= SelectionChangedHandler;
}
if (disposing) {
designSurface.Unloaded += delegate {
ServiceContainer serviceContainer = designSurface.GetService(typeof(ServiceContainer)) as ServiceContainer;
if (serviceContainer != null) {
// Workaround for .NET bug: .NET unregisters the designer host only if no component throws an exception,
// but then in a finally block assumes that the designer host is already unloaded.
// Thus we would get the confusing "InvalidOperationException: The container cannot be disposed at design time"
// when any component throws an exception.
// See http://community.sharpdevelop.net/forums/p/10928/35288.aspx
// Reproducible with a custom control that has a designer that crashes on unloading
// e.g. http://www.codeproject.com/KB/toolbars/WinFormsRibbon.aspx
// We work around this problem by unregistering the designer host manually.
try {
var services = (Dictionary<Type, object>)typeof(ServiceContainer).InvokeMember(
"Services",
BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.NonPublic,
null, serviceContainer, null);
foreach (var pair in services.ToArray()) {
if (pair.Value is IDesignerHost) {
serviceContainer.GetType().InvokeMember(
"RemoveFixedService",
BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic,
null, serviceContainer, new object[] { pair.Key });
}
}
} catch (Exception ex) {
LoggingService.Error(ex);
}
}
};
try {
designSurface.Dispose();
} else {
WorkbenchSingleton.SafeThreadAsyncCall(designSurface.Dispose);
} catch (ExceptionCollection exceptions) {
foreach (Exception ex in exceptions.Exceptions) {
LoggingService.Error(ex);
}
} finally {
designSurface = null;
}
designSurface = null;
}
this.typeResolutionService = null;
@ -532,7 +561,6 @@ namespace ICSharpCode.FormsDesigner @@ -532,7 +561,6 @@ namespace ICSharpCode.FormsDesigner
this.reloadPending = false;
this.unloading = false;
this.UserContent = this.pleaseWaitLabel;
Application.DoEvents();
}
void DesignerUnloading(object sender, EventArgs e)
@ -541,7 +569,6 @@ namespace ICSharpCode.FormsDesigner @@ -541,7 +569,6 @@ namespace ICSharpCode.FormsDesigner
this.unloading = true;
if (!this.disposing) {
this.UserContent = this.pleaseWaitLabel;
Application.DoEvents();
}
}
@ -975,12 +1002,10 @@ namespace ICSharpCode.FormsDesigner @@ -975,12 +1002,10 @@ namespace ICSharpCode.FormsDesigner
// the reload can interrupt the starting of the debugger.
// To prevent this, we explicitly raise the Idle event here.
LoggingService.Debug("Forms designer: DebugStarting raises the Idle event to force pending reload now");
Application.DoEvents();
Cursor oldCursor = Cursor.Current;
Cursor.Current = Cursors.WaitCursor;
try {
Application.RaiseIdle(EventArgs.Empty);
Application.DoEvents();
} finally {
Cursor.Current = oldCursor;
}

40
src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs

@ -2,10 +2,10 @@ @@ -2,10 +2,10 @@
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Forms;
using ICSharpCode.Core;
using ICSharpCode.Core.WinForms;
@ -163,7 +163,13 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -163,7 +163,13 @@ namespace ICSharpCode.SharpDevelop.Gui
/// </summary>
public static R SafeThreadFunction<R>(Func<R> method)
{
return (R)workbench.SynchronizingObject.Invoke(method, emptyObjectArray);
// InvokeRequired test is necessary so that we don't run other actions in the message queue
// when we're already running on the main thread (unexpected reentrancy)
ISynchronizeInvoke si = workbench.SynchronizingObject;
if (si.InvokeRequired)
return (R)workbench.SynchronizingObject.Invoke(method, emptyObjectArray);
else
return method();
}
/// <summary>
@ -173,7 +179,11 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -173,7 +179,11 @@ namespace ICSharpCode.SharpDevelop.Gui
/// </summary>
public static R SafeThreadFunction<A, R>(Func<A, R> method, A arg1)
{
return (R)workbench.SynchronizingObject.Invoke(method, new object[] { arg1 });
ISynchronizeInvoke si = workbench.SynchronizingObject;
if (si.InvokeRequired)
return (R)si.Invoke(method, new object[] { arg1 });
else
return method(arg1);
}
/// <summary>
@ -183,7 +193,11 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -183,7 +193,11 @@ namespace ICSharpCode.SharpDevelop.Gui
/// </summary>
public static void SafeThreadCall(Action method)
{
workbench.SynchronizingObject.Invoke(method, emptyObjectArray);
ISynchronizeInvoke si = workbench.SynchronizingObject;
if (si.InvokeRequired)
si.Invoke(method, emptyObjectArray);
else
method();
}
/// <summary>
@ -193,7 +207,11 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -193,7 +207,11 @@ namespace ICSharpCode.SharpDevelop.Gui
/// </summary>
public static void SafeThreadCall<A>(Action<A> method, A arg1)
{
workbench.SynchronizingObject.Invoke(method, new object[] { arg1 });
ISynchronizeInvoke si = workbench.SynchronizingObject;
if (si.InvokeRequired)
si.Invoke(method, new object[] { arg1 });
else
method(arg1);
}
/// <summary>
@ -203,7 +221,11 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -203,7 +221,11 @@ namespace ICSharpCode.SharpDevelop.Gui
/// </summary>
public static void SafeThreadCall<A, B>(Action<A, B> method, A arg1, B arg2)
{
workbench.SynchronizingObject.Invoke(method, new object[] { arg1, arg2 });
ISynchronizeInvoke si = workbench.SynchronizingObject;
if (si.InvokeRequired)
si.Invoke(method, new object[] { arg1, arg2 });
else
method(arg1, arg2);
}
/// <summary>
@ -213,7 +235,11 @@ namespace ICSharpCode.SharpDevelop.Gui @@ -213,7 +235,11 @@ namespace ICSharpCode.SharpDevelop.Gui
/// </summary>
public static void SafeThreadCall<A, B, C>(Action<A, B, C> method, A arg1, B arg2, C arg3)
{
workbench.SynchronizingObject.Invoke(method, new object[] { arg1, arg2, arg3 });
ISynchronizeInvoke si = workbench.SynchronizingObject;
if (si.InvokeRequired)
si.Invoke(method, new object[] { arg1, arg2, arg3 });
else
method(arg1, arg2, arg3);
}
/// <summary>

Loading…
Cancel
Save