Browse Source

Fix error handling for invalid solution files.

Fix crash when using cut/paste to move a project between solution folders.
pull/32/merge
Daniel Grunwald 13 years ago
parent
commit
6ebf10f939
  1. 2
      src/Main/Base/Project/Project/ISolutionFolder.cs
  2. 5
      src/Main/Base/Project/Src/Gui/Dialogs/NewProjectDialog.cs
  3. 9
      src/Main/Base/Project/Src/Gui/Pads/ProjectBrowser/TreeNodes/SolutionFolderNode.cs
  4. 2
      src/Main/Base/Project/Src/Project/ProjectLoadException.cs
  5. 10
      src/Main/Base/Project/Templates/ProjectTemplate.cs
  6. 35
      src/Main/Base/Project/Util/SharpDevelopServiceContainer.cs
  7. 2
      src/Main/Core/Project/Src/Services/FileUtility/FileUtility.cs
  8. 5
      src/Main/ICSharpCode.SharpDevelop.Widgets/Project/CustomWindowsFormsHost.cs
  9. 2
      src/Main/SharpDevelop/Project/ProjectService.cs
  10. 27
      src/Main/SharpDevelop/Project/SolutionLoader.cs
  11. 7
      src/Main/SharpDevelop/Templates/Project/ProjectDescriptor.cs

2
src/Main/Base/Project/Project/ISolutionFolder.cs

@ -47,7 +47,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -47,7 +47,7 @@ namespace ICSharpCode.SharpDevelop.Project
/// Loads an existing project from disk and adds it to this solution.
/// </summary>
/// <param name="fileName">Path to the project file</param>
/// <exception cref="InvalidProjectFileException">The specified file is not a valid project file</exception>
/// <exception cref="ProjectLoadException">The specified file is not a valid project file</exception>
/// <exception cref="IOException">Error reading from the specified project file</exception>
IProject AddExistingProject(FileName fileName);

5
src/Main/Base/Project/Src/Gui/Dialogs/NewProjectDialog.cs

@ -293,7 +293,10 @@ namespace ICSharpCode.SharpDevelop.Project.Dialogs @@ -293,7 +293,10 @@ namespace ICSharpCode.SharpDevelop.Project.Dialogs
{
try {
CreateProject();
} catch (InvalidProjectFileException ex) {
} catch (ProjectLoadException ex) {
LoggingService.Error("Unable to create new project.", ex);
MessageService.ShowError(ex.Message);
} catch (IOException ex) {
LoggingService.Error("Unable to create new project.", ex);
MessageService.ShowError(ex.Message);
}

9
src/Main/Base/Project/Src/Gui/Pads/ProjectBrowser/TreeNodes/SolutionFolderNode.cs

@ -169,7 +169,14 @@ namespace ICSharpCode.SharpDevelop.Project @@ -169,7 +169,14 @@ namespace ICSharpCode.SharpDevelop.Project
Guid guid = Guid.Parse(dataObject.GetData(typeof(ISolutionItem).ToString()).ToString());
ISolutionItem solutionItem = folderNode.Solution.GetItemByGuid(guid);
if (solutionItem != null) {
folderNode.Folder.Items.Add(solutionItem);
// Use a batch update to move the item without causing projects
// be removed from the solution (and thus disposed).
using (solutionItem.ParentFolder.Items.BatchUpdate()) {
using (folderNode.Folder.Items.BatchUpdate()) {
solutionItem.ParentFolder.Items.Remove(solutionItem);
folderNode.Folder.Items.Add(solutionItem);
}
}
ExtTreeView treeView = (ExtTreeView)folderTreeNode.TreeView;
foreach (ExtTreeNode node in treeView.CutNodes) {
ExtTreeNode oldParent = node.Parent as ExtTreeNode;

2
src/Main/Base/Project/Src/Project/ProjectLoadException.cs

@ -10,7 +10,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -10,7 +10,7 @@ namespace ICSharpCode.SharpDevelop.Project
/// Exception used when loading of a project fails.
/// </summary>
[Serializable()]
public class ProjectLoadException : Exception
public class ProjectLoadException : FormatException
{
public ProjectLoadException() : base()
{

10
src/Main/Base/Project/Templates/ProjectTemplate.cs

@ -27,6 +27,16 @@ namespace ICSharpCode.SharpDevelop.Templates @@ -27,6 +27,16 @@ namespace ICSharpCode.SharpDevelop.Templates
get { return Enumerable.Empty<TargetFramework>(); }
}
/// <summary>
/// Creates projects from the template; and adds them to the solution specified in the parameter object.
/// </summary>
/// <param name="options">Paramter object used to pass options for the template creation.</param>
/// <returns>
/// Returns a result object that describes the project that were created;
/// or null if the operation was aborted.
/// </returns>
/// <exception cref="IOException">Error writing the projects to disk</exception>
/// <exception cref="ProjectLoadException">Error creating the projects (e.g. a separate download is required for projects of this type [like the F# compiler])</exception>
public abstract ProjectTemplateResult CreateProjects(ProjectTemplateOptions options);
internal ProjectTemplateResult CreateAndOpenSolution(ProjectTemplateOptions options, string solutionDirectory, string solutionName)

35
src/Main/Base/Project/Util/SharpDevelopServiceContainer.cs

@ -16,7 +16,7 @@ namespace ICSharpCode.SharpDevelop @@ -16,7 +16,7 @@ namespace ICSharpCode.SharpDevelop
{
readonly IServiceProvider parentProvider;
readonly Dictionary<Type, object> services = new Dictionary<Type, object>();
readonly List<IDisposable> servicesToDispose = new List<IDisposable>();
readonly List<Type> servicesToDispose = new List<Type>();
readonly Dictionary<Type, object> taskCompletionSources = new Dictionary<Type, object>(); // object = TaskCompletionSource<T> for various T
public SharpDevelopServiceContainer()
@ -57,16 +57,27 @@ namespace ICSharpCode.SharpDevelop @@ -57,16 +57,27 @@ namespace ICSharpCode.SharpDevelop
public void Dispose()
{
var loggingService = SD.Log;
IDisposable[] disposables;
Type[] disposableTypes;
lock (services) {
disposables = servicesToDispose.ToArray();
services.Clear();
disposableTypes = servicesToDispose.ToArray();
//services.Clear();
servicesToDispose.Clear();
}
// dispose services in reverse order of their creation
foreach (IDisposable disposable in disposables.Reverse()) {
loggingService.Debug("Service shutdown: " + disposable.GetType());
disposable.Dispose();
for (int i = disposableTypes.Length - 1; i >= 0; i--) {
IDisposable disposable = null;
lock (services) {
object serviceInstance;
if (services.TryGetValue(disposableTypes[i], out serviceInstance)) {
disposable = serviceInstance as IDisposable;
if (disposable != null)
services.Remove(disposableTypes[i]);
}
}
if (disposable != null) {
loggingService.Debug("Service shutdown: " + disposableTypes[i]);
disposable.Dispose();
}
}
}
@ -74,7 +85,7 @@ namespace ICSharpCode.SharpDevelop @@ -74,7 +85,7 @@ namespace ICSharpCode.SharpDevelop
{
IDisposable disposableService = serviceInstance as IDisposable;
if (disposableService != null)
servicesToDispose.Add(disposableService);
servicesToDispose.Add(serviceType);
dynamic taskCompletionSource;
if (taskCompletionSources.TryGetValue(serviceType, out taskCompletionSource)) {
@ -111,7 +122,13 @@ namespace ICSharpCode.SharpDevelop @@ -111,7 +122,13 @@ namespace ICSharpCode.SharpDevelop
public void RemoveService(Type serviceType)
{
lock (services) {
services.Remove(serviceType);
object instance;
if (services.TryGetValue(serviceType, out instance)) {
services.Remove(serviceType);
IDisposable disposableInstance = instance as IDisposable;
if (disposableInstance != null)
servicesToDispose.Remove(serviceType);
}
}
}

2
src/Main/Core/Project/Src/Services/FileUtility/FileUtility.cs

@ -668,6 +668,8 @@ namespace ICSharpCode.Core @@ -668,6 +668,8 @@ namespace ICSharpCode.Core
return ObservedLoadHandleException(e, loadFile, fileName, message, policy);
} catch (UnauthorizedAccessException e) {
return ObservedLoadHandleException(e, loadFile, fileName, message, policy);
} catch (FormatException e) {
return ObservedLoadHandleException(e, loadFile, fileName, message, policy);
}
}

5
src/Main/ICSharpCode.SharpDevelop.Widgets/Project/CustomWindowsFormsHost.cs

@ -85,6 +85,11 @@ namespace ICSharpCode.SharpDevelop.Widgets @@ -85,6 +85,11 @@ namespace ICSharpCode.SharpDevelop.Widgets
}
base.WndProc(ref m);
}
protected override void OnThreadException(Exception e)
{
System.Windows.Forms.Application.OnThreadException(e);
}
}
HostNativeWindow hostNativeWindow;

2
src/Main/SharpDevelop/Project/ProjectService.cs

@ -9,6 +9,7 @@ using ICSharpCode.Core; @@ -9,6 +9,7 @@ using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Workbench;
using Microsoft.Build.Exceptions;
namespace ICSharpCode.SharpDevelop.Project
{
@ -136,6 +137,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -136,6 +137,7 @@ namespace ICSharpCode.SharpDevelop.Project
if (!CloseSolution(allowCancel: true))
return false;
FileUtility.ObservedLoad(OpenSolutionInternal, fileName);
return currentSolution != null;
}

27
src/Main/SharpDevelop/Project/SolutionLoader.cs

@ -47,18 +47,13 @@ namespace ICSharpCode.SharpDevelop.Project @@ -47,18 +47,13 @@ namespace ICSharpCode.SharpDevelop.Project
} while (currentLine != null && (currentLine.Length == 0 || currentLine[0] == '#'));
}
InvalidProjectFileException Error()
{
return Error("${res:SharpDevelop.Solution.InvalidSolutionFile}");
}
InvalidProjectFileException Error(string message, params object[] formatItems)
ProjectLoadException Error(string message, params object[] formatItems)
{
if (formatItems.Length > 0)
message = StringParser.Format(message, formatItems);
else
message = StringParser.Parse(message);
return new InvalidProjectFileException(fileName ?? string.Empty, lineNumber, 1, lineNumber, currentLine.Length + 1, message, string.Empty, string.Empty, string.Empty);
return new ProjectLoadException("Error reading from " + fileName + " at line " + lineNumber + ":" + Environment.NewLine + message);
}
#region ReadSolution
@ -93,8 +88,12 @@ namespace ICSharpCode.SharpDevelop.Project @@ -93,8 +88,12 @@ namespace ICSharpCode.SharpDevelop.Project
progress.CancellationToken.ThrowIfCancellationRequested();
// Read global sections:
if (currentLine != "Global")
throw Error();
if (currentLine != "Global") {
if (currentLine == null)
throw Error("Unexpected end of file");
else
throw Error("Unknown line: " + currentLine);
}
NextLine();
Dictionary<Guid, SolutionFolder> guidToParentFolderDict = null;
@ -121,10 +120,10 @@ namespace ICSharpCode.SharpDevelop.Project @@ -121,10 +120,10 @@ namespace ICSharpCode.SharpDevelop.Project
}
}
if (currentLine != "EndGlobal")
throw Error();
throw Error("Expected 'EndGlobal'");
NextLine();
if (currentLine != null)
throw Error();
throw Error("Expected end of file");
solution.LoadPreferences();
@ -162,9 +161,11 @@ namespace ICSharpCode.SharpDevelop.Project @@ -162,9 +161,11 @@ namespace ICSharpCode.SharpDevelop.Project
public SolutionFormatVersion ReadFormatHeader()
{
if (currentLine == null) // can happen if the .sln is an empty file
throw Error("Solution file is empty");
Match match = versionPattern.Match(currentLine);
if (!match.Success)
throw Error();
throw Error("The file is not a valid solution file");
SolutionFormatVersion version;
switch (match.Result("${Version}")) {
@ -248,7 +249,7 @@ namespace ICSharpCode.SharpDevelop.Project @@ -248,7 +249,7 @@ namespace ICSharpCode.SharpDevelop.Project
loadInformation.ProjectSections.Add(section);
}
if (currentLine != "EndProject")
throw Error();
throw Error("Expected 'EndProject'");
NextLine();
return loadInformation;
}

7
src/Main/SharpDevelop/Templates/Project/ProjectDescriptor.cs

@ -478,12 +478,13 @@ namespace ICSharpCode.SharpDevelop.Templates @@ -478,12 +478,13 @@ namespace ICSharpCode.SharpDevelop.Templates
});
} catch (InvalidProjectFileException ex) {
string message;
if (string.IsNullOrEmpty(importsFailureMessage)) {
MessageService.ShowError("Error creating project:\n" + ex.Message);
message = "Error creating project:\n" + ex.Message;
} else {
MessageService.ShowError(importsFailureMessage + "\n\n" + ex.Message);
message = importsFailureMessage + "\n\n" + ex.Message;
}
return null;
throw new ProjectLoadException(message, ex);
}
}

Loading…
Cancel
Save