Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problems with WinRT #312

Open
Nuklon opened this issue Nov 22, 2023 · 15 comments
Open

Problems with WinRT #312

Nuklon opened this issue Nov 22, 2023 · 15 comments

Comments

@Nuklon
Copy link

Nuklon commented Nov 22, 2023

This keeps getting logged, it's a problem with Exceptionless client when using WinRT (in .net core apps):

Exceptionless.Json.JsonSerializationException: Error getting value from 'GetReference_1' on 'ABI.WinRT.Interop.IRestrictedErrorInfo+Vftbl'. ---> InvalidOperationException: No coercion operator is defined between types 'System.Int32(System.IntPtr, System.IntPtr*)' and 'System.Object'.
at System.Linq.Expressions.Expression.GetUserDefinedCoercionOrThrow(System.Linq.Expressions.ExpressionType coercionType, System.Linq.Expressions.Expression expression, System.Type convertToType) at offset 14
at Exceptionless.Json.Utilities.ExpressionReflectionDelegateFactory.EnsureCastExpression(System.Linq.Expressions.Expression expression, System.Type targetType, System.Boolean allowWidening) at offset 181
at Exceptionless.Json.Utilities.ExpressionReflectionDelegateFactory.CreateGet[T](System.Reflection.PropertyInfo propertyInfo) at offset 108
at Exceptionless.Json.Utilities.ReflectionDelegateFactory.CreateGet[T](System.Reflection.MemberInfo memberInfo) at offset 45
at Exceptionless.Json.Serialization.ExpressionValueProvider.GetValue(System.Object target) at offset 8
--- End of inner exception stack trace ---
at Exceptionless.Json.Serialization.ExpressionValueProvider.GetValue(System.Object target) at offset 84
at Exceptionless.Json.Serialization.JsonSerializerInternalWriter.CalculatePropertyValues(Exceptionless.Json.JsonWriter writer, System.Object value, Exceptionless.Json.Serialization.JsonContainerContract contract, Exceptionless.Json.Serialization.JsonProperty member, Exceptionless.Json.Serialization.JsonProperty property, Exceptionless.Json.Serialization.JsonContract& memberContract, System.Object& memberValue) at offset 93
at Exceptionless.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(Exceptionless.Json.JsonWriter writer, System.Object value, Exceptionless.Json.Serialization.JsonObjectContract contract, Exceptionless.Json.Serialization.JsonProperty member, Exceptionless.Json.Serialization.JsonContainerContract collectionContract, Exceptionless.Json.Serialization.JsonProperty containerProperty) at offset 60

Another one:

InvalidOperationException: No coercion operator is defined between types 'System.Int32(System.IntPtr, System.IntPtr*)' and 'System.Object'.
at System.Linq.Expressions.Expression.GetUserDefinedCoercionOrThrow(System.Linq.Expressions.ExpressionType coercionType, System.Linq.Expressions.Expression expression, System.Type convertToType) at offset 14
at Exceptionless.Json.Utilities.ExpressionReflectionDelegateFactory.EnsureCastExpression(System.Linq.Expressions.Expression expression, System.Type targetType, System.Boolean allowWidening) at offset 181
at Exceptionless.Json.Utilities.ExpressionReflectionDelegateFactory.CreateGet[T](System.Reflection.PropertyInfo propertyInfo) at offset 108
at Exceptionless.Json.Utilities.ReflectionDelegateFactory.CreateGet[T](System.Reflection.MemberInfo memberInfo) at offset 45
at Exceptionless.Json.Serialization.ExpressionValueProvider.GetValue(System.Object target) at offset 8
@niemyjski
Copy link
Member

Are you submitting any custom data? When does this happen in the logs? We are just using JSON.NET via source import.

@Nuklon
Copy link
Author

Nuklon commented Nov 23, 2023

I do have some custom data, but it's not WinRT stuff. It's thrown when an exception is logged/submitted by Exceptionless related to WinRT (i.e., thrown by WinRT, typically a COMException).

   at Exceptionless.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)
   at Exceptionless.Serializer.DefaultJsonSerializer.Serialize(Object model, String[] exclusions, Int32 maxDepth, Boolean continueOnSerializationError)
   at Exceptionless.Submission.DefaultSubmissionClient.PostEventsAsync(IEnumerable`1 events, ExceptionlessConfiguration config, IJsonSerializer serializer)
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Exceptionless.Submission.DefaultSubmissionClient.PostEventsAsync(IEnumerable`1 events, ExceptionlessConfiguration config, IJsonSerializer serializer)
   at Exceptionless.Queue.DefaultEventQueue.ProcessAsync()
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Exceptionless.Queue.DefaultEventQueue.ProcessAsync()
   at Exceptionless.Queue.DefaultEventQueue.OnProcessQueueAsync(Object state)
   at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
   at Exceptionless.Queue.DefaultEventQueue.OnProcessQueueAsync(Object state)

@niemyjski
Copy link
Member

niemyjski commented Nov 26, 2023

Any chance you could put together a small sample project in a pr (in our samples folder) that can reproduce this. I can then debug this and make sure it's not broken going forward.

@Nuklon
Copy link
Author

Nuklon commented Nov 27, 2023

Maybe, the trick is getting a WinRT method to throw.

@niemyjski
Copy link
Member

Were you able to find a method that throws?

@Nuklon
Copy link
Author

Nuklon commented Dec 10, 2023

Were you able to find a method that throws?

Maybe, need a bit more time.

@niemyjski
Copy link
Member

Were you able to come up with an easy repo sample?

@Nuklon
Copy link
Author

Nuklon commented Dec 29, 2023

Took some time, but here it is.
Create a WPF project in VS and target net8.0-windows10.0.22000.0 (necessary to get UWP types).
Some of the code in ExceptionlessService is probably not necessary.

internal class ExceptionlessService(ExceptionlessClient exceptionlessClient)
{
	private bool _isDisposed;
	private long _queuedFirstChanceExceptions;

	public void Initialize()
	{
		try
		{
			ExceptionlessConfiguration configuration = exceptionlessClient.Configuration;
			configuration.ServerUrl = "YOUR_URL";
			configuration.ApiKey = "YOUR_API_KEY";

			configuration.Enabled = true;
			configuration.IncludePrivateInformation = true;
			configuration.IncludeIpAddress = false;
			configuration.UseSessions();

			AppDomain.CurrentDomain.UnhandledException += (_, args) => OnUnhandledException(args.ExceptionObject as Exception, "AppDomainUnhandledException");
			TaskScheduler.UnobservedTaskException += (_, args) => OnUnhandledException(args.Exception, "TaskSchedulerUnobservedTaskException");
			App.Current.DispatcherUnhandledException += (_, args) => OnUnhandledException(args.Exception, "ApplicationDispatcherUnhandledException");

			AppDomain.CurrentDomain.FirstChanceException += OnFirstChanceException;

			if (configuration.SessionsEnabled)
				exceptionlessClient.SubmitSessionStart();

			exceptionlessClient.SubmittingEvent += OnSubmittingEvent;
		}
		catch
		{ }
	}

	public void Dispose()
	{
		if (exceptionlessClient == null || _isDisposed)
			return;

		_isDisposed = true;

		// Wait until all first chance exceptions have been submitted.
		while (Interlocked.Read(ref _queuedFirstChanceExceptions) > 0)
			Thread.Sleep(1);

		exceptionlessClient.ProcessQueueAsync().GetAwaiter().GetResult();

		if (exceptionlessClient.Configuration.SessionsEnabled)
			exceptionlessClient.SubmitSessionEndAsync().GetAwaiter().GetResult();
	}

	private static void OnSubmittingEvent(object sender, EventSubmittingEventArgs eventArgs)
	{
		try
		{
			if (eventArgs is { Event: not null, IsUnhandledError: true })
				eventArgs.Event.Tags.Add("Unhandled");
		}
		catch
		{ }
	}

	private static void OnUnhandledException(Exception exception, string submissionMethod)
	{
		if (exception == null)
			return;

		var contextData = new ContextData();

		contextData.MarkAsUnhandledError();
		contextData.SetSubmissionMethod(submissionMethod);

		EventBuilder eventBuilder = exception.ToExceptionless(contextData);
		eventBuilder.AddTags("Unhandled");
		eventBuilder.Submit();
	}

	private void OnFirstChanceException(object sender, FirstChanceExceptionEventArgs eventArgs)
	{
		Exception exception = eventArgs.Exception;
		string stackTrace = new StackTrace(true).ToString();

		Interlocked.Increment(ref _queuedFirstChanceExceptions);
		Task.Run(() => SubmitFirstChanceException(exception, stackTrace));
	}

	private void SubmitFirstChanceException(Exception exception, string stackTrace)
	{
		try
		{
			EventBuilder eventBuilder = exception.ToExceptionless();

			eventBuilder.AddTags("First Chance");
			eventBuilder.AddObject(stackTrace, "Stack Trace");

			eventBuilder.Submit();
		}
		finally
		{
			Interlocked.Decrement(ref _queuedFirstChanceExceptions);
		}
	}
}

public partial class MainWindow : Window
{
        public WiFiDirectAdvertisementPublisher a;
        
        public MainWindow()
        {
	        ExceptionlessService aa = new ExceptionlessService(ExceptionlessClient.Default);
	        aa.Initialize();
        
	        InitializeComponent();
        
	        try
	        {
		        a = new WiFiDirectAdvertisementPublisher();
		        a.Advertisement.LegacySettings.Ssid = "test";
		        a.Advertisement.LegacySettings.Passphrase = new PasswordCredential("a", null, null); // Throws!
		        a.Start();
	        }
	        catch (Exception e)
	        {
		        e.ToExceptionless().Submit();
		        aa.Dispose();
	        }
        }
}

Now, within Exceptionless, you'll see two exceptions logged. One is "The parameter is incorrect. Cannot create credential". This is the correct one. But you'll also get a second exception: "Error getting value from 'GetReference_1' on 'ABI.WinRT.Interop.IRestrictedErrorInfo+Vftbl'." which obviously doesn't make sense.

If the other exception doesn't show up on first start, run it a couple of times, it'll get there eventually. I've had better success by letting it run a few seconds.

On a side note, you might also see this (only rarely here, but it happens):

InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.Collections.NodeKeyValueCollection.NodeKeyValueEnumerator.MoveNext() at offset 29
at Exceptionless.ToErrorModelExtensions.ToErrorModelInternal(System.Exception exception, Exceptionless.ExceptionlessClient client, System.Boolean isInner) at offset 397

@niemyjski
Copy link
Member

Thanks for the sample! I'll try and take a look into this hopefully today or tomorrow.

@niemyjski
Copy link
Member

niemyjski commented Jan 24, 2024

Sorry for the delay. This is expected as you are wiring up FirstChanceExceptions which catch everything that's already handled. I do think that maybe we should update the default error exclusions to filter out any error data property prefixed with __Restricted or Restricted (cc @ejsmith )
image
image
image
image

Looking at your sample I see a few things that I wanted to call out.

  1. There is no reason I'm aware of that you couldn't call ExceptionlessClient.Default.Register() (register also wires up to RegisterApplicationDispatcherUnhandledExceptionHandler) to wire up to all the wpf handlers and cover WinRT scenarios. If that doesn't work which it did for me I'd still call ExceptionlessClient.Default.Startup() which handles session management for you. which will wire up to
client.RegisterAppDomainUnhandledExceptionHandler();

// make sure that queued events are sent when the app exits
client.RegisterOnProcessExitHandler();
client.RegisterTaskSchedulerUnobservedTaskExceptionHandler();

2.Instead of your dispose method call ExceptionlessClient.Default.ShutdownAsync() which does everything and more safely.
3. Be very careful of FirstChanceExceptions it can be misleading as it catches things that are already safely handled.

I'll leave this open until we get a response on __Restricted... Or if you have any follow up

@Nuklon
Copy link
Author

Nuklon commented Jan 24, 2024

  1. I'm doing manual registering of events as I want to run some code when an unhandled exception occurs (I removed that from the above example). I previously checked IsUnhandledError in OnSubmittingEvent, but found this to be too unreliable/slow sometimes.
  2. Good idea, wasn't aware of that.
  3. Right, but it provides some useful information sometimes, I've hidden useless exceptions in Exceptionless dashboard ☺️

@niemyjski
Copy link
Member

  1. If this is slow or unexpected please report with more information as we'd like to get it fixed. You can always do custom logic on unhandled in a plugin :)

@Nuklon
Copy link
Author

Nuklon commented Jan 29, 2024

It's been some time since I've written this around Exceptionless, perhaps I'll give the more default code another try someday ☺️

@niemyjski
Copy link
Member

@Nuklon would you mind submitting a pr to update the default exclusions to filter out any error data property prefixed with __Restricted or Restricted (cc @ejsmith )

@Nuklon
Copy link
Author

Nuklon commented Mar 28, 2024

@Nuklon would you mind submitting a pr to update the default exclusions to filter out any error data property prefixed with __Restricted or Restricted (cc @ejsmith )

I have no idea where this is located, so you can probably make this change yourself faster :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants