Editor IO exception handling (#1491)

* Close input stream
Show MessageBox with exception message
Remove unused arguments
Guard all of the Editor input/output code parts and show MessageBox
with appropriate message and issue reporting link

* Extract showing messageBox into method
This commit is contained in:
stefansjfw
2020-03-09 10:41:06 +01:00
committed by GitHub
parent 52e08a2784
commit 5581e25a21
6 changed files with 274 additions and 232 deletions

View File

@@ -16,7 +16,7 @@ namespace FancyZonesEditor
EditorOverlay mainEditor = EditorOverlay.Current; EditorOverlay mainEditor = EditorOverlay.Current;
if (mainEditor.DataContext is LayoutModel model) if (mainEditor.DataContext is LayoutModel model)
{ {
model.Persist(mainEditor.GetZoneRects()); model.Persist();
} }
_choosing = true; _choosing = true;

View File

@@ -150,11 +150,11 @@ namespace FancyZonesEditor
{ {
if (model is GridLayoutModel) if (model is GridLayoutModel)
{ {
model.Apply(mainEditor.GetZoneRects()); model.Apply();
} }
else else
{ {
model.Apply((model as CanvasLayoutModel).Zones.ToArray()); model.Apply();
} }
Close(); Close();

View File

@@ -117,46 +117,53 @@ namespace FancyZonesEditor.Models
// Implements the LayoutModel.PersistData abstract method // Implements the LayoutModel.PersistData abstract method
protected override void PersistData() protected override void PersistData()
{ {
FileStream outputStream = File.Open(Settings.AppliedZoneSetTmpFile, FileMode.Create); try
JsonWriterOptions writerOptions = new JsonWriterOptions
{ {
SkipValidation = true, FileStream outputStream = File.Open(Settings.AppliedZoneSetTmpFile, FileMode.Create);
}; JsonWriterOptions writerOptions = new JsonWriterOptions
using (var writer = new Utf8JsonWriter(outputStream, writerOptions)) {
{ SkipValidation = true,
writer.WriteStartObject(); };
writer.WriteString("uuid", "{" + Guid.ToString().ToUpper() + "}"); using (var writer = new Utf8JsonWriter(outputStream, writerOptions))
writer.WriteString("name", Name);
writer.WriteString("type", "canvas");
writer.WriteStartObject("info");
writer.WriteNumber("ref-width", _referenceWidth);
writer.WriteNumber("ref-height", _referenceHeight);
writer.WriteStartArray("zones");
foreach (Int32Rect rect in Zones)
{ {
writer.WriteStartObject(); writer.WriteStartObject();
writer.WriteNumber("X", rect.X); writer.WriteString("uuid", "{" + Guid.ToString().ToUpper() + "}");
writer.WriteNumber("Y", rect.Y); writer.WriteString("name", Name);
writer.WriteNumber("width", rect.Width);
writer.WriteNumber("height", rect.Height); writer.WriteString("type", "canvas");
writer.WriteStartObject("info");
writer.WriteNumber("ref-width", _referenceWidth);
writer.WriteNumber("ref-height", _referenceHeight);
writer.WriteStartArray("zones");
foreach (Int32Rect rect in Zones)
{
writer.WriteStartObject();
writer.WriteNumber("X", rect.X);
writer.WriteNumber("Y", rect.Y);
writer.WriteNumber("width", rect.Width);
writer.WriteNumber("height", rect.Height);
writer.WriteEndObject();
}
writer.WriteEndArray();
// end info object
writer.WriteEndObject(); writer.WriteEndObject();
// end root object
writer.WriteEndObject();
writer.Flush();
} }
writer.WriteEndArray(); outputStream.Close();
}
// end info object catch (Exception ex)
writer.WriteEndObject(); {
ShowExceptionMessageBox("Error persisting canvas layout", ex);
// end root object
writer.WriteEndObject();
writer.Flush();
} }
outputStream.Close();
} }
} }
} }

View File

@@ -2,10 +2,11 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text;
using System.Text.Json; using System.Text.Json;
using System.Windows;
namespace FancyZonesEditor.Models namespace FancyZonesEditor.Models
{ {
@@ -170,59 +171,66 @@ namespace FancyZonesEditor.Models
// Implements the LayoutModel.PersistData abstract method // Implements the LayoutModel.PersistData abstract method
protected override void PersistData() protected override void PersistData()
{ {
FileStream outputStream = File.Open(Settings.AppliedZoneSetTmpFile, FileMode.Create); try
using (var writer = new Utf8JsonWriter(outputStream, options: default))
{ {
writer.WriteStartObject(); FileStream outputStream = File.Open(Settings.AppliedZoneSetTmpFile, FileMode.Create);
writer.WriteString("uuid", "{" + Guid.ToString().ToUpper() + "}"); using (var writer = new Utf8JsonWriter(outputStream, options: default))
writer.WriteString("name", Name);
writer.WriteString("type", "grid");
writer.WriteStartObject("info");
writer.WriteNumber("rows", Rows);
writer.WriteNumber("columns", Columns);
writer.WriteStartArray("rows-percentage");
for (int row = 0; row < Rows; row++)
{ {
writer.WriteNumberValue(RowPercents[row]); writer.WriteStartObject();
} writer.WriteString("uuid", "{" + Guid.ToString().ToUpper() + "}");
writer.WriteString("name", Name);
writer.WriteEndArray(); writer.WriteString("type", "grid");
writer.WriteStartArray("columns-percentage"); writer.WriteStartObject("info");
for (int col = 0; col < Columns; col++)
{
writer.WriteNumberValue(ColumnPercents[col]);
}
writer.WriteEndArray(); writer.WriteNumber("rows", Rows);
writer.WriteNumber("columns", Columns);
writer.WriteStartArray("cell-child-map"); writer.WriteStartArray("rows-percentage");
for (int row = 0; row < Rows; row++) for (int row = 0; row < Rows; row++)
{
writer.WriteStartArray();
for (int col = 0; col < Columns; col++)
{ {
writer.WriteNumberValue(CellChildMap[row, col]); writer.WriteNumberValue(RowPercents[row]);
} }
writer.WriteEndArray(); writer.WriteEndArray();
writer.WriteStartArray("columns-percentage");
for (int col = 0; col < Columns; col++)
{
writer.WriteNumberValue(ColumnPercents[col]);
}
writer.WriteEndArray();
writer.WriteStartArray("cell-child-map");
for (int row = 0; row < Rows; row++)
{
writer.WriteStartArray();
for (int col = 0; col < Columns; col++)
{
writer.WriteNumberValue(CellChildMap[row, col]);
}
writer.WriteEndArray();
}
writer.WriteEndArray();
// end info object
writer.WriteEndObject();
// end root object
writer.WriteEndObject();
writer.Flush();
} }
writer.WriteEndArray(); outputStream.Close();
}
// end info object catch (Exception ex)
writer.WriteEndObject(); {
ShowExceptionMessageBox("Error persisting grid layout", ex);
// end root object
writer.WriteEndObject();
writer.Flush();
} }
outputStream.Close();
} }
} }
} }

View File

@@ -27,8 +27,12 @@ namespace FancyZonesEditor.Models
// Manages common properties and base persistence // Manages common properties and base persistence
public abstract class LayoutModel : INotifyPropertyChanged public abstract class LayoutModel : INotifyPropertyChanged
{ {
private static readonly string _registryPath = Settings.RegistryPath + "\\Layouts"; public static void ShowExceptionMessageBox(string message, Exception ex)
private static readonly string _fullRegistryPath = Settings.FullRegistryPath + "\\Layouts"; {
string title = "FancyZones Editor Exception Handler";
string fullMessage = "Please report the bug to https://github.com/microsoft/PowerToys/issues \n" + message + ": " + ex.Message;
MessageBox.Show(fullMessage, title);
}
protected LayoutModel() protected LayoutModel()
{ {
@@ -133,19 +137,26 @@ namespace FancyZonesEditor.Models
public static void SerializeDeletedCustomZoneSets() public static void SerializeDeletedCustomZoneSets()
{ {
FileStream outputStream = File.Open(Settings.CustomZoneSetsTmpFile, FileMode.Create); try
var writer = new Utf8JsonWriter(outputStream, options: default);
writer.WriteStartObject();
writer.WriteStartArray("deleted-custom-zone-sets");
foreach (string zoneSet in _deletedCustomModels)
{ {
writer.WriteStringValue(zoneSet); FileStream outputStream = File.Open(Settings.CustomZoneSetsTmpFile, FileMode.Create);
} var writer = new Utf8JsonWriter(outputStream, options: default);
writer.WriteStartObject();
writer.WriteStartArray("deleted-custom-zone-sets");
foreach (string zoneSet in _deletedCustomModels)
{
writer.WriteStringValue(zoneSet);
}
writer.WriteEndArray(); writer.WriteEndArray();
writer.WriteEndObject(); writer.WriteEndObject();
writer.Flush(); writer.Flush();
outputStream.Close(); outputStream.Close();
}
catch (Exception ex)
{
ShowExceptionMessageBox("Error serializing deleted layouts", ex);
}
} }
// Loads all the custom Layouts from tmp file passed by FancuZonesLib // Loads all the custom Layouts from tmp file passed by FancuZonesLib
@@ -153,79 +164,81 @@ namespace FancyZonesEditor.Models
{ {
_customModels = new ObservableCollection<LayoutModel>(); _customModels = new ObservableCollection<LayoutModel>();
FileStream inputStream = File.Open(Settings.CustomZoneSetsTmpFile, FileMode.Open);
JsonDocument jsonObject;
try try
{ {
jsonObject = JsonDocument.Parse(inputStream, options: default); FileStream inputStream = File.Open(Settings.CustomZoneSetsTmpFile, FileMode.Open);
} JsonDocument jsonObject = JsonDocument.Parse(inputStream, options: default);
catch JsonElement.ArrayEnumerator customZoneSetsEnumerator = jsonObject.RootElement.GetProperty("custom-zone-sets").EnumerateArray();
{
return _customModels;
}
JsonElement.ArrayEnumerator customZoneSetsEnumerator = jsonObject.RootElement.GetProperty("custom-zone-sets").EnumerateArray(); while (customZoneSetsEnumerator.MoveNext())
while (customZoneSetsEnumerator.MoveNext())
{
var current = customZoneSetsEnumerator.Current;
string name = current.GetProperty("name").GetString();
string type = current.GetProperty("type").GetString();
string uuid = current.GetProperty("uuid").GetString();
var info = current.GetProperty("info");
if (type.Equals("grid"))
{ {
int rows = info.GetProperty("rows").GetInt32(); var current = customZoneSetsEnumerator.Current;
int columns = info.GetProperty("columns").GetInt32(); string name = current.GetProperty("name").GetString();
int[] rowsPercentage = new int[rows]; string type = current.GetProperty("type").GetString();
JsonElement.ArrayEnumerator rowsPercentageEnumerator = info.GetProperty("rows-percentage").EnumerateArray(); string uuid = current.GetProperty("uuid").GetString();
int i = 0; var info = current.GetProperty("info");
while (rowsPercentageEnumerator.MoveNext()) if (type.Equals("grid"))
{ {
rowsPercentage[i++] = rowsPercentageEnumerator.Current.GetInt32(); int rows = info.GetProperty("rows").GetInt32();
} int columns = info.GetProperty("columns").GetInt32();
int[] rowsPercentage = new int[rows];
i = 0; JsonElement.ArrayEnumerator rowsPercentageEnumerator = info.GetProperty("rows-percentage").EnumerateArray();
int[] columnsPercentage = new int[columns]; int i = 0;
JsonElement.ArrayEnumerator columnsPercentageEnumerator = info.GetProperty("columns-percentage").EnumerateArray(); while (rowsPercentageEnumerator.MoveNext())
while (columnsPercentageEnumerator.MoveNext())
{
columnsPercentage[i++] = columnsPercentageEnumerator.Current.GetInt32();
}
i = 0;
JsonElement.ArrayEnumerator cellChildMapRows = info.GetProperty("cell-child-map").EnumerateArray();
int[,] cellChildMap = new int[rows, columns];
while (cellChildMapRows.MoveNext())
{
int j = 0;
JsonElement.ArrayEnumerator cellChildMapRowElems = cellChildMapRows.Current.EnumerateArray();
while (cellChildMapRowElems.MoveNext())
{ {
cellChildMap[i, j++] = cellChildMapRowElems.Current.GetInt32(); rowsPercentage[i++] = rowsPercentageEnumerator.Current.GetInt32();
} }
i++; i = 0;
} int[] columnsPercentage = new int[columns];
JsonElement.ArrayEnumerator columnsPercentageEnumerator = info.GetProperty("columns-percentage").EnumerateArray();
while (columnsPercentageEnumerator.MoveNext())
{
columnsPercentage[i++] = columnsPercentageEnumerator.Current.GetInt32();
}
_customModels.Add(new GridLayoutModel(uuid, name, LayoutType.Custom, rows, columns, rowsPercentage, columnsPercentage, cellChildMap)); i = 0;
} JsonElement.ArrayEnumerator cellChildMapRows = info.GetProperty("cell-child-map").EnumerateArray();
else if (type.Equals("canvas")) int[,] cellChildMap = new int[rows, columns];
{ while (cellChildMapRows.MoveNext())
int referenceWidth = info.GetProperty("ref-width").GetInt32(); {
int referenceHeight = info.GetProperty("ref-height").GetInt32(); int j = 0;
JsonElement.ArrayEnumerator zonesEnumerator = info.GetProperty("zones").EnumerateArray(); JsonElement.ArrayEnumerator cellChildMapRowElems = cellChildMapRows.Current.EnumerateArray();
IList<Int32Rect> zones = new List<Int32Rect>(); while (cellChildMapRowElems.MoveNext())
while (zonesEnumerator.MoveNext()) {
cellChildMap[i, j++] = cellChildMapRowElems.Current.GetInt32();
}
i++;
}
_customModels.Add(new GridLayoutModel(uuid, name, LayoutType.Custom, rows, columns, rowsPercentage, columnsPercentage, cellChildMap));
}
else if (type.Equals("canvas"))
{ {
int x = zonesEnumerator.Current.GetProperty("X").GetInt32(); int referenceWidth = info.GetProperty("ref-width").GetInt32();
int y = zonesEnumerator.Current.GetProperty("Y").GetInt32(); int referenceHeight = info.GetProperty("ref-height").GetInt32();
int width = zonesEnumerator.Current.GetProperty("width").GetInt32(); JsonElement.ArrayEnumerator zonesEnumerator = info.GetProperty("zones").EnumerateArray();
int height = zonesEnumerator.Current.GetProperty("height").GetInt32(); IList<Int32Rect> zones = new List<Int32Rect>();
zones.Add(new Int32Rect(x, y, width, height)); while (zonesEnumerator.MoveNext())
} {
int x = zonesEnumerator.Current.GetProperty("X").GetInt32();
int y = zonesEnumerator.Current.GetProperty("Y").GetInt32();
int width = zonesEnumerator.Current.GetProperty("width").GetInt32();
int height = zonesEnumerator.Current.GetProperty("height").GetInt32();
zones.Add(new Int32Rect(x, y, width, height));
}
_customModels.Add(new CanvasLayoutModel(uuid, name, LayoutType.Custom, referenceWidth, referenceHeight, zones)); _customModels.Add(new CanvasLayoutModel(uuid, name, LayoutType.Custom, referenceWidth, referenceHeight, zones));
}
} }
inputStream.Close();
}
catch (Exception ex)
{
ShowExceptionMessageBox("Error loading custom layouts", ex);
return new ObservableCollection<LayoutModel>();
} }
return _customModels; return _customModels;
@@ -239,56 +252,62 @@ namespace FancyZonesEditor.Models
public abstract LayoutModel Clone(); public abstract LayoutModel Clone();
public void Persist(System.Windows.Int32Rect[] zones) public void Persist()
{ {
PersistData(); PersistData();
Apply(zones); Apply();
} }
public void Apply(System.Windows.Int32Rect[] zones) public void Apply()
{ {
int zoneCount = zones.Length; try
FileStream outputStream = File.Open(Settings.ActiveZoneSetTmpFile, FileMode.Create);
var writer = new Utf8JsonWriter(outputStream, options: default);
writer.WriteStartObject();
writer.WriteString("device-id", Settings.UniqueKey);
writer.WriteStartObject("active-zoneset");
writer.WriteString("uuid", "{" + Guid.ToString().ToUpper() + "}");
switch (Type)
{ {
case LayoutType.Focus: FileStream outputStream = File.Open(Settings.ActiveZoneSetTmpFile, FileMode.Create);
writer.WriteString("type", "focus"); var writer = new Utf8JsonWriter(outputStream, options: default);
break;
case LayoutType.Rows: writer.WriteStartObject();
writer.WriteString("type", "rows"); writer.WriteString("device-id", Settings.UniqueKey);
break;
case LayoutType.Columns: writer.WriteStartObject("active-zoneset");
writer.WriteString("type", "columns"); writer.WriteString("uuid", "{" + Guid.ToString().ToUpper() + "}");
break; switch (Type)
case LayoutType.Grid: {
writer.WriteString("type", "grid"); case LayoutType.Focus:
break; writer.WriteString("type", "focus");
case LayoutType.PriorityGrid: break;
writer.WriteString("type", "priority-grid"); case LayoutType.Rows:
break; writer.WriteString("type", "rows");
case LayoutType.Custom: break;
writer.WriteString("type", "custom"); case LayoutType.Columns:
break; writer.WriteString("type", "columns");
break;
case LayoutType.Grid:
writer.WriteString("type", "grid");
break;
case LayoutType.PriorityGrid:
writer.WriteString("type", "priority-grid");
break;
case LayoutType.Custom:
writer.WriteString("type", "custom");
break;
}
writer.WriteEndObject();
Settings settings = ((App)Application.Current).ZoneSettings;
writer.WriteBoolean("editor-show-spacing", settings.ShowSpacing);
writer.WriteNumber("editor-spacing", settings.Spacing);
writer.WriteNumber("editor-zone-count", settings.ZoneCount);
writer.WriteEndObject();
writer.Flush();
outputStream.Close();
}
catch (Exception ex)
{
ShowExceptionMessageBox("Error applying layout", ex);
} }
writer.WriteEndObject();
Settings settings = ((App)Application.Current).ZoneSettings;
writer.WriteBoolean("editor-show-spacing", settings.ShowSpacing);
writer.WriteNumber("editor-spacing", settings.Spacing);
writer.WriteNumber("editor-zone-count", settings.ZoneCount);
writer.WriteEndObject();
writer.Flush();
outputStream.Close();
} }
} }
} }

View File

@@ -360,48 +360,56 @@ namespace FancyZonesEditor
private void ParseDeviceInfoData() private void ParseDeviceInfoData()
{ {
FileStream inputStream = File.Open(Settings.ActiveZoneSetTmpFile, FileMode.Open); try
var jsonObject = JsonDocument.Parse(inputStream, options: default).RootElement;
UniqueKey = jsonObject.GetProperty("device-id").GetString();
ActiveZoneSetUUid = jsonObject.GetProperty("active-zoneset").GetProperty("uuid").GetString();
string layoutType = jsonObject.GetProperty("active-zoneset").GetProperty("type").GetString();
if (ActiveZoneSetUUid == "null" || layoutType == "blank")
{ {
// Default selection is Focus FileStream inputStream = File.Open(Settings.ActiveZoneSetTmpFile, FileMode.Open);
ActiveZoneSetLayoutType = LayoutType.Focus; var jsonObject = JsonDocument.Parse(inputStream, options: default).RootElement;
_showSpacing = true;
_spacing = 16; UniqueKey = jsonObject.GetProperty("device-id").GetString();
_zoneCount = 3; ActiveZoneSetUUid = jsonObject.GetProperty("active-zoneset").GetProperty("uuid").GetString();
} string layoutType = jsonObject.GetProperty("active-zoneset").GetProperty("type").GetString();
else
{ if (ActiveZoneSetUUid == "null" || layoutType == "blank")
switch (layoutType)
{ {
case "focus": // Default selection is Focus
ActiveZoneSetLayoutType = LayoutType.Focus; ActiveZoneSetLayoutType = LayoutType.Focus;
break; _showSpacing = true;
case "columns": _spacing = 16;
ActiveZoneSetLayoutType = LayoutType.Columns; _zoneCount = 3;
break; }
case "rows": else
ActiveZoneSetLayoutType = LayoutType.Rows; {
break; switch (layoutType)
case "grid": {
ActiveZoneSetLayoutType = LayoutType.Grid; case "focus":
break; ActiveZoneSetLayoutType = LayoutType.Focus;
case "priority-grid": break;
ActiveZoneSetLayoutType = LayoutType.PriorityGrid; case "columns":
break; ActiveZoneSetLayoutType = LayoutType.Columns;
case "custom": break;
ActiveZoneSetLayoutType = LayoutType.Custom; case "rows":
break; ActiveZoneSetLayoutType = LayoutType.Rows;
break;
case "grid":
ActiveZoneSetLayoutType = LayoutType.Grid;
break;
case "priority-grid":
ActiveZoneSetLayoutType = LayoutType.PriorityGrid;
break;
case "custom":
ActiveZoneSetLayoutType = LayoutType.Custom;
break;
}
_showSpacing = jsonObject.GetProperty("editor-show-spacing").GetBoolean();
_spacing = jsonObject.GetProperty("editor-spacing").GetInt32();
_zoneCount = jsonObject.GetProperty("editor-zone-count").GetInt32();
} }
_showSpacing = jsonObject.GetProperty("editor-show-spacing").GetBoolean(); inputStream.Close();
_spacing = jsonObject.GetProperty("editor-spacing").GetInt32(); } catch (Exception ex)
_zoneCount = jsonObject.GetProperty("editor-zone-count").GetInt32(); {
LayoutModel.ShowExceptionMessageBox("Error parsing device info data", ex);
} }
} }