Skip to content

Commit

Permalink
Merge branch 'PolarGoose-main'
Browse files Browse the repository at this point in the history
  • Loading branch information
ThomasArdal committed Dec 28, 2024
2 parents 56bdeb2 + ffd7849 commit 0324273
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 138 deletions.
16 changes: 6 additions & 10 deletions src/ExceptionVisualizer/ExceptionDebuggerVisualizerProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@ namespace ExceptionVisualizer
/// <summary>
/// Debugger visualizer provider class for <see cref="System.String"/>.
/// </summary>
/// <remarks>
/// Initializes a new instance of the <see cref="ExceptionDebuggerVisualizerProvider"/> class.
/// </remarks>
/// <param name="extension">Extension instance.</param>
/// <param name="extensibility">Extensibility object.</param>
[VisualStudioContribution]
internal class ExceptionDebuggerVisualizerProvider : DebuggerVisualizerProvider
internal class ExceptionDebuggerVisualizerProvider(ExtensionEntrypoint extension, VisualStudioExtensibility extensibility) : DebuggerVisualizerProvider(extension, extensibility)
{
/// <summary>
/// Initializes a new instance of the <see cref="ExceptionDebuggerVisualizerProvider"/> class.
/// </summary>
/// <param name="extension">Extension instance.</param>
/// <param name="extensibility">Extensibility object.</param>
public ExceptionDebuggerVisualizerProvider(ExtensionEntrypoint extension, VisualStudioExtensibility extensibility)
: base(extension, extensibility)
{
}

/// <inheritdoc/>
public override DebuggerVisualizerProviderConfiguration DebuggerVisualizerProviderConfiguration => new("Exception Visualizer", typeof(Exception))
Expand Down
43 changes: 43 additions & 0 deletions src/ExceptionVisualizer/ExceptionModelExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using ExceptionVisualizer.Models;
using ExceptionVisualizerSource;
using System.Collections.ObjectModel;
using System.Windows;

namespace ExceptionVisualizer
{
internal static class ExceptionModelExtensions
{
internal static ExceptionViewModel ToViewModel(this ExceptionModel exception)
{
var viewModel = new ExceptionViewModel
{
Data = new ObservableCollection<DataViewModel>(exception.Data.Select(d => new DataViewModel
{
Key = d.Key,
Value = d.Value,
})),
Properties = new ObservableCollection<DataViewModel>(exception.Properties.Select(d => new DataViewModel
{
Key = d.Key,
Value = d.Value,
})),
ShowData = exception.Data.Count > 0 ? Visibility.Visible : Visibility.Collapsed,
ShowProperties = exception.Properties.Count > 0 ? Visibility.Visible : Visibility.Collapsed,
HelpLink = exception.HelpLink,
HResult = exception.HResult,
Message = exception.Message,
Source = exception.Source,
StackTrace = exception.StackTrace,
TargetSite = exception.TargetSite,
Demystified = exception.Demystified,
@Type = exception.Type,
};
foreach (var inner in exception.InnerExceptions)
{
viewModel.InnerExceptions.Add(ToViewModel(inner));
}

return viewModel;
}
}
}
166 changes: 64 additions & 102 deletions src/ExceptionVisualizer/ExceptionUserControl.cs
Original file line number Diff line number Diff line change
@@ -1,102 +1,64 @@
using ExceptionVisualizer.Models;
using ExceptionVisualizerSource;
using Microsoft.VisualStudio.Extensibility.DebuggerVisualizers;
using Microsoft.VisualStudio.Extensibility.UI;
using System.Collections.ObjectModel;
using System.Windows;

namespace ExceptionVisualizer
{
internal class ExceptionUserControl : RemoteUserControl
{
private readonly VisualizerTarget visualizerTarget;

public ExceptionUserControl(VisualizerTarget visualizerTarget) : base(new ViewModel())
{
this.visualizerTarget = visualizerTarget;
}

private ViewModel ViewModel => (ViewModel)this.DataContext!;


public override Task ControlLoadedAsync(CancellationToken cancellationToken)
{
_ = Task.Run(async () =>
{
try
{
ExceptionModel? exception = await this.visualizerTarget.ObjectSource.RequestDataAsync<ExceptionModel?>(jsonSerializer: null, CancellationToken.None);
if (exception != null)
{
var viewModel = ToViewModel(exception);
ViewModel.Exceptions.Add(viewModel);
Subscribe(viewModel);
viewModel.IsSelected = true;
}
}
catch (StreamJsonRpc.RemoteInvocationException rie) when (rie.Message.Contains("Could not load file or assembly 'Newtonsoft.Json"))
{
MessageBox.Show("There's currently a bug in Visual Studio causing the Exception Visualizer extension to crash when debugging projects containing a reference to Newtonsoft.Json older than version 13. Either upgrade to version 13 of Newtonsoft.Json or follow this issue on GitHub: https://github.com/microsoft/VSExtensibility/issues/248.");
}
catch (Exception ex)
{
Telemetry.TrackException(ex);
}
});
return Task.CompletedTask;
}

private void Subscribe(ExceptionViewModel model)
{
model.PropertyChanged += Exception_PropertyChanged;
foreach (var inner in model.InnerExceptions)
{
Subscribe(inner);
}
}

private ExceptionViewModel ToViewModel(ExceptionModel exception)
{
var viewModel = new ExceptionViewModel
{
Data = new ObservableCollection<DataViewModel>(exception.Data.Select(d => new DataViewModel
{
Key = d.Key,
Value = d.Value,
})),
Properties = new ObservableCollection<DataViewModel>(exception.Properties.Select(d => new DataViewModel
{
Key = d.Key,
Value = d.Value,
})),
ShowData = exception.Data.Count > 0 ? Visibility.Visible : Visibility.Collapsed,
ShowProperties = exception.Properties.Count > 0 ? Visibility.Visible : Visibility.Collapsed,
HelpLink = exception.HelpLink,
HResult = exception.HResult,
Message = exception.Message,
Source = exception.Source,
StackTrace = exception.StackTrace,
TargetSite = exception.TargetSite,
Demystified = exception.Demystified,
@Type = exception.Type,
};
foreach (var inner in exception.InnerExceptions)
{
viewModel.InnerExceptions.Add(ToViewModel(inner));
}

return viewModel;
}

private void Exception_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
var model = sender as ExceptionViewModel;
if (model == null) return;

if (model.IsSelected)
{
ViewModel.SelectedItem = model;
}
}
}
}
using ExceptionVisualizer.Models;
using ExceptionVisualizerSource;
using Microsoft.VisualStudio.Extensibility.DebuggerVisualizers;
using Microsoft.VisualStudio.Extensibility.UI;
using System.Collections.ObjectModel;
using System.Windows;

namespace ExceptionVisualizer
{
internal class ExceptionUserControl(VisualizerTarget visualizerTarget) : RemoteUserControl(new ViewModel())
{
private readonly VisualizerTarget visualizerTarget = visualizerTarget;

private ViewModel ViewModel => (ViewModel)DataContext!;


public override Task ControlLoadedAsync(CancellationToken cancellationToken)
{
_ = Task.Run(async () =>
{
try
{
ExceptionModel? exception = await this.visualizerTarget.ObjectSource.RequestDataAsync<ExceptionModel?>(jsonSerializer: null, CancellationToken.None);
if (exception != null)
{
var viewModel = exception.ToViewModel();
ViewModel.Exceptions.Add(viewModel);
Subscribe(viewModel);
viewModel.IsSelected = true;
}
}
catch (StreamJsonRpc.RemoteInvocationException rie) when (rie.Message.Contains("Could not load file or assembly 'Newtonsoft.Json"))
{
MessageBox.Show("There's currently a bug in Visual Studio causing the Exception Visualizer extension to crash when debugging projects containing a reference to Newtonsoft.Json older than version 13. Either upgrade to version 13 of Newtonsoft.Json or follow this issue on GitHub: https://github.com/microsoft/VSExtensibility/issues/248.");
}
catch (Exception ex)
{
MessageBox.Show($"ExceptionVisualizer failed with exception:\n{ex}");
Telemetry.TrackException(ex);
}
}, cancellationToken);
return Task.CompletedTask;
}

private void Subscribe(ExceptionViewModel model)
{
model.PropertyChanged += Exception_PropertyChanged;
foreach (var inner in model.InnerExceptions)
{
Subscribe(inner);
}
}

private void Exception_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (sender is not ExceptionViewModel model) return;

if (model.IsSelected)
{
ViewModel.SelectedItem = model;
}
}
}
}
10 changes: 5 additions & 5 deletions src/ExceptionVisualizer/ExceptionVisualizer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Elmah.Io.Client" Version="5.1.76" />
<PackageReference Include="Elmah.Io.HResults" Version="1.0.23" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.11.40261" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.11.40261" />
<PackageReference Include="Elmah.Io.Client" Version="5.2.117" />
<PackageReference Include="Elmah.Io.HResults" Version="1.0.29" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.12.40390" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.12.40390" />
</ItemGroup>

<ItemGroup>
Expand All @@ -24,7 +24,7 @@
<Content Include="..\ExceptionVisualizerSource\bin\$(Configuration)\netstandard2.0\ExceptionVisualizerSource.dll" Link="netstandard2.0\ExceptionVisualizerSource.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="bin\$(Configuration)\net8.0-windows\Ben.Demystifier.dll" Link="netstandard2.0\Ben.Demystifier.dll">
<Content Include="..\ExceptionVisualizerSource\bin\$(Configuration)\netstandard2.0\Ben.Demystifier.dll" Link="netstandard2.0\Ben.Demystifier.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>

Expand Down
10 changes: 5 additions & 5 deletions src/ExceptionVisualizer/Telemetry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace ExceptionVisualizer
/// </summary>
public static class Telemetry
{
private static IElmahioAPI elmahIoClient;
private static IElmahioAPI? elmahIoClient;

public static bool Enabled { get; set; }

Expand Down Expand Up @@ -49,10 +49,10 @@ public static void TrackException(Exception ex)
User = WindowsIdentity.GetCurrent().Name,
Hostname = Hostname(),
Application = "Exception Visualizer",
ServerVariables = new List<Item>
{
ServerVariables =
[
new Item("User-Agent", $"X-ELMAHIO-APPLICATION; OS=Windows; OSVERSION={Environment.OSVersion.Version}; ENGINE=VisualStudio"),
}
]
};

elmahIoClient.Messages.CreateAndNotify(new Guid("ece7db40-a2ea-41f7-838f-9ac9c5514d18"), createMessage);
Expand All @@ -75,7 +75,7 @@ private static List<Item> PropertiesToData(Exception exception)
return items;
}

private static string Hostname()
private static string? Hostname()
{
var machineName = Environment.MachineName;
if (!string.IsNullOrWhiteSpace(machineName)) return machineName;
Expand Down
9 changes: 4 additions & 5 deletions src/ExceptionVisualizerSource/ExceptionModel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace ExceptionVisualizerSource
Expand All @@ -20,13 +19,13 @@ public class ExceptionModel
public string Demystified { get; set; }

[DataMember]
public List<KeyValuePair<string, string>> Data { get; set; } = new List<KeyValuePair<string, string>>();
public List<KeyValuePair<string, string>> Data { get; set; } = [];

[DataMember]
public List<KeyValuePair<string, string>> Properties { get; set; } = new List<KeyValuePair<string, string>>();
public List<KeyValuePair<string, string>> Properties { get; set; } = [];

[DataMember]
public List<ExceptionModel> InnerExceptions { get; set; } = new List<ExceptionModel>();
public List<ExceptionModel> InnerExceptions { get; set; } = [];

[DataMember]
public string Source { get; internal set; }
Expand Down
22 changes: 11 additions & 11 deletions src/ExceptionVisualizerSource/ExceptionModelSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,21 +39,21 @@ private static ExceptionModel Convert(Exception e)
Source = e.Source,
StackTrace = e.StackTrace,
TargetSite = e.TargetSite?.ToString(),
};
model.Data = new List<KeyValuePair<string, string>>(e
Data = new List<KeyValuePair<string, string>>(e
.Data
.Keys
.Cast<object>()
.Where(k => !string.IsNullOrWhiteSpace(k.ToString()))
.Select(i => new KeyValuePair<string, string>(i.ToString(), Value(e.Data, i))));
model.Properties = e
.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.CanRead)
.Where(p => p.PropertyType.IsSubclassOf(typeof(ValueType)) || p.PropertyType.Equals(typeof(string)))
.Where(p => p.Name != "Message" && p.Name != "Source" && p.Name != "HResult" && p.Name != "HelpLink" && p.Name != "TargetSite" && p.Name != "StackTrace" && p.GetValue(e) != null)
.Select(p => new KeyValuePair<string, string>(p.Name, p.GetValue(e)?.ToString()))
.ToList();
.Select(i => new KeyValuePair<string, string>(i.ToString(), Value(e.Data, i)))),
Properties = e
.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p => p.CanRead)
.Where(p => p.PropertyType.IsSubclassOf(typeof(ValueType)) || p.PropertyType.Equals(typeof(string)))
.Where(p => p.Name != "Message" && p.Name != "Source" && p.Name != "HResult" && p.Name != "HelpLink" && p.Name != "TargetSite" && p.Name != "StackTrace" && p.GetValue(e) != null)
.Select(p => new KeyValuePair<string, string>(p.Name, p.GetValue(e)?.ToString()))
.ToList()
};
var stackTrace = new EnhancedStackTrace(e);
if (stackTrace.FrameCount > 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>12</LangVersion>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit 0324273

Please sign in to comment.