diff --git a/src/net/Qml.Net.Tests/BaseTests.cs b/src/net/Qml.Net.Tests/BaseTests.cs index 1e060f36..e863d1dd 100644 --- a/src/net/Qml.Net.Tests/BaseTests.cs +++ b/src/net/Qml.Net.Tests/BaseTests.cs @@ -9,6 +9,7 @@ namespace Qml.Net.Tests protected BaseTests() { + QmlNetConfig.ShouldEnsureUIThread = false; // Not need for unit tests. Monitor.Enter(LockObject); } diff --git a/src/net/Qml.Net/Internal/DefaultCallbacks.cs b/src/net/Qml.Net/Internal/DefaultCallbacks.cs index 33d00e2b..8f6e86ab 100644 --- a/src/net/Qml.Net/Internal/DefaultCallbacks.cs +++ b/src/net/Qml.Net/Internal/DefaultCallbacks.cs @@ -245,12 +245,12 @@ namespace Qml.Net.Internal } var result = componentCompelted.ComponentCompleted(); - if (Tasks.ListenForExceptionsWhenInvokingTasks) + if (QmlNetConfig.ListenForExceptionsWhenInvokingTasks) { result?.ContinueWith( task => { - Tasks.RaiseUnhandledTaskException(task.Exception); + QmlNetConfig.RaiseUnhandledTaskException(task.Exception); }, TaskContinuationOptions.OnlyOnFaulted); } @@ -344,12 +344,12 @@ namespace Qml.Net.Internal Task resultTask = null; del(target, parameters, result, ref resultTask); - if (Tasks.ListenForExceptionsWhenInvokingTasks) + if (QmlNetConfig.ListenForExceptionsWhenInvokingTasks) { resultTask?.ContinueWith( task => { - Tasks.RaiseUnhandledTaskException(task.Exception); + QmlNetConfig.RaiseUnhandledTaskException(task.Exception); }, TaskContinuationOptions.OnlyOnFaulted); } diff --git a/src/net/Qml.Net/Internal/Types/NetReference.cs b/src/net/Qml.Net/Internal/Types/NetReference.cs index 5e0137a7..b39dcd51 100644 --- a/src/net/Qml.Net/Internal/Types/NetReference.cs +++ b/src/net/Qml.Net/Internal/Types/NetReference.cs @@ -25,6 +25,7 @@ namespace Qml.Net.Internal.Types { return obj; } + throw new InvalidOperationException($"No object found for object id {ObjectId}"); } } @@ -33,6 +34,8 @@ namespace Qml.Net.Internal.Types public bool ActivateSignal(string signalName, params object[] parameters) { + QmlNetConfig.EnsureUIThread(); + if (parameters != null && parameters.Length > 0) { using (var list = new NetVariantList()) @@ -45,6 +48,7 @@ namespace Qml.Net.Internal.Types list.Add(variant); } } + return Interop.NetReference.ActivateSignal(Handle, signalName, list.Handle) == 1; } } diff --git a/src/net/Qml.Net/QCoreApplication.cs b/src/net/Qml.Net/QCoreApplication.cs index e1f9ddfa..1f684578 100644 --- a/src/net/Qml.Net/QCoreApplication.cs +++ b/src/net/Qml.Net/QCoreApplication.cs @@ -16,7 +16,7 @@ namespace Qml.Net private GCHandle _triggerHandle; private GCHandle _aboutToQuitHandle; private readonly List _aboutToQuitEventHandlers = new List(); - private static int _threadId; + private static int? _threadId; protected QCoreApplication(IntPtr handle, bool ownsHandle) : base(handle, ownsHandle) @@ -66,7 +66,18 @@ namespace Qml.Net SynchronizationContext.SetSynchronizationContext(new QtSynchronizationContext(this)); } - public static bool IsMainThread => Environment.CurrentManagedThreadId == _threadId; + public static bool IsMainThread + { + get + { + if (!_threadId.HasValue) + { + throw new Exception("QCoreApplication hasn't been created yet, can't determine what thread is the main thread."); + } + + return Environment.CurrentManagedThreadId == _threadId; + } + } public int Exec() { diff --git a/src/net/Qml.Net/QmlNetConfig.cs b/src/net/Qml.Net/QmlNetConfig.cs new file mode 100644 index 00000000..732df046 --- /dev/null +++ b/src/net/Qml.Net/QmlNetConfig.cs @@ -0,0 +1,38 @@ +using System; + +namespace Qml.Net +{ + public class QmlNetConfig + { + public static bool ListenForExceptionsWhenInvokingTasks { get; set; } + + public static event Action UnhandledTaskException; + + internal static void RaiseUnhandledTaskException(AggregateException ex) + { + var handler = UnhandledTaskException; + handler?.Invoke(ex); + } + + public static bool ShouldEnsureUIThread { get; set; } = true; + + public static Action EnsureUIThreadDelegate = () => + { + if (QCoreApplication.IsMainThread) + { + throw new Exception( + "You must be on the UI thread to perform this task. See https://github.com/qmlnet/qmlnet/issues/112"); + } + }; + + internal static void EnsureUIThread() + { + if (!ShouldEnsureUIThread) + { + return; + } + + EnsureUIThreadDelegate?.Invoke(); + } + } +} \ No newline at end of file diff --git a/src/net/Qml.Net/Signals.cs b/src/net/Qml.Net/Signals.cs index e52bf4b4..63af321b 100644 --- a/src/net/Qml.Net/Signals.cs +++ b/src/net/Qml.Net/Signals.cs @@ -13,11 +13,6 @@ namespace Qml.Net { public static bool ActivateSignal(this object instance, string signalName, params object[] args) { - if (!QCoreApplication.IsMainThread) - { - throw new Exception("An attempt was made to activate a signal from a non-UI thread."); - } - var existing = NetReference.CreateForObject(instance, false /*Ignore if not tagged*/); if (existing != null && existing.ActivateSignal(signalName, args)) { diff --git a/src/net/Qml.Net/Tasks.cs b/src/net/Qml.Net/Tasks.cs deleted file mode 100644 index c92e814a..00000000 --- a/src/net/Qml.Net/Tasks.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace Qml.Net -{ - public static class Tasks - { - public static bool ListenForExceptionsWhenInvokingTasks { get; set; } - - public static event Action UnhandledTaskException; - - internal static void RaiseUnhandledTaskException(AggregateException ex) - { - var handler = UnhandledTaskException; - handler?.Invoke(ex); - } - } -} \ No newline at end of file