Repository navigation
Expand file tree
/
Copy pathApp.xaml.cs
More file actions
220 lines (195 loc) · 8.14 KB
/
Copy pathApp.xaml.cs
File metadata and controls
220 lines (195 loc) · 8.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
using System;
using System.Text.Json;
using Windows.ApplicationModel;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Windows.Storage;
namespace NativeScriptWindowsDemo
{
sealed partial class App : Application
{
private const string LastLaunchArgsKey = "LastLaunchArgs";
private readonly RuntimeHost _runtimeHost = new RuntimeHost();
public static Window CurrentWindow { get; private set; }
public Window MainWindow => CurrentWindow;
public Window Window => CurrentWindow;
enum AppEventKind
{
Activated = 1,
Deactivated = 2,
Shown = 3,
Hidden = 4,
UncaughtError = 5,
Exit = 6
}
public App()
{
this.InitializeComponent();
CurrentWindow ??= new Window();
CrashDiagnostics.InstallGlobalHandlers();
CurrentWindow.Closed += OnWindowClosed;
CurrentWindow.Activated += OnWindowActivated;
CurrentWindow.VisibilityChanged += OnWindowVisibilityChanged;
this.UnhandledException += OnUnhandledException;
}
protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
_runtimeHost.Initialize();
// Capture before any await — continuations may resume on a thread pool thread.
var dispatcherQueue = DispatcherQueue.GetForCurrentThread();
// Show crash report from the previous run if one exists.
try
{
var panicLogPath = System.IO.Path.Combine(
ApplicationData.Current.LocalFolder.Path, "nativescript-panic.log");
if (System.IO.File.Exists(panicLogPath))
{
var content = System.IO.File.ReadAllText(panicLogPath);
System.IO.File.Delete(panicLogPath);
if (!string.IsNullOrWhiteSpace(content))
await CrashDiagnostics.ShowCrashDialogAsync("Crash from previous run", content);
}
}
catch { }
string jsError = null;
try
{
_runtimeHost.RunMainScript();
jsError = _runtimeHost.GetLastJsError();
if (!string.IsNullOrEmpty(jsError))
{
CrashDiagnostics.WriteMessage("JS Error", jsError);
var report = CrashDiagnostics.BuildErrorReport(null, jsError);
CrashDiagnostics.WriteToTraceLog(report);
await CrashDiagnostics.ShowCrashDialogAsync("JavaScript Error", report);
}
}
catch (Exception scriptEx)
{
jsError = _runtimeHost.GetLastJsError();
System.Diagnostics.Debug.WriteLine($"[NativeScript] Script exception: {scriptEx.Message}");
CrashDiagnostics.WriteExceptionReport("RunMainScript", scriptEx, null);
var report = CrashDiagnostics.BuildErrorReport(scriptEx, jsError);
CrashDiagnostics.WriteToTraceLog(report);
await CrashDiagnostics.ShowCrashDialogAsync("Script Execution Error", report);
}
void ShowMainWindow()
{
StartPump();
try
{
if (CurrentWindow?.Content != null)
{
CurrentWindow.Activate();
}
}
catch { /* JS owns its own Window */ }
}
if (dispatcherQueue?.HasThreadAccess == true)
{
ShowMainWindow();
}
else if (!(dispatcherQueue?.TryEnqueue(ShowMainWindow) ?? false))
{
ShowMainWindow();
}
}
private void OnWindowClosed(object sender, WindowEventArgs e)
{
StopPump();
_runtimeHost.NotifyAppEvent((int)AppEventKind.Exit, null);
ApplicationData.Current.LocalSettings.Values[LastLaunchArgsKey] = string.Empty;
_runtimeHost.Dispose();
}
// WindowActivationState.Deactivated → lost focus (background); otherwise foreground/resume.
private void OnWindowActivated(object sender, WindowActivatedEventArgs e)
{
var kind = e.WindowActivationState == WindowActivationState.Deactivated ? AppEventKind.Deactivated : AppEventKind.Activated;
_runtimeHost.NotifyAppEvent((int)kind, null);
}
private void OnWindowVisibilityChanged(object sender, WindowVisibilityChangedEventArgs e)
{
var kind = e.Visible ? AppEventKind.Shown : AppEventKind.Hidden;
_runtimeHost.NotifyAppEvent((int)kind, null);
}
private void OnUnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
{
e.Handled = true;
var jsError = _runtimeHost.GetLastJsError();
// Surface to the JS `uncaughtError` application event before reporting.
try
{
var message = e.Message ?? jsError ?? "Unhandled exception";
_runtimeHost.NotifyAppEvent((int)AppEventKind.UncaughtError, message);
}
catch { }
CrashDiagnostics.WriteExceptionReport(
"Xaml.UnhandledException",
e.Exception,
"JsError=" + (jsError ?? "<none>"));
var report = CrashDiagnostics.BuildErrorReport(e.Exception, jsError);
CrashDiagnostics.WriteToTraceLog(report);
var _ = CrashDiagnostics.ShowCrashDialogAsync(
e.Message ?? "Unhandled exception", report);
}
// Pump cadence from two sources, both via the guarded SchedulePump(): CompositionTarget.Rendering
// (display refresh rate; smooth JS-driven animations) + a low-frequency DispatcherQueueTimer
// heartbeat (keeps timers/promises running when Rendering stops, e.g. minimised/occluded). The pump
// runs OUTSIDE the render walk (TryEnqueue) so the JS it pumps can mutate the XAML tree without
// tripping XAML's re-entrancy guard (which RoFailFasts with E_UNEXPECTED / 0xC000027B). _pumpQueued
// keeps at most one pump in flight.
private DispatcherQueue _dispatcherQueue;
private DispatcherQueueTimer _pumpHeartbeat;
private bool _pumpQueued;
private void StartPump()
{
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
CompositionTarget.Rendering -= OnRenderFrame;
CompositionTarget.Rendering += OnRenderFrame;
if (_pumpHeartbeat == null && _dispatcherQueue != null)
{
_pumpHeartbeat = _dispatcherQueue.CreateTimer();
_pumpHeartbeat.Interval = TimeSpan.FromMilliseconds(100);
_pumpHeartbeat.IsRepeating = true;
_pumpHeartbeat.Tick += OnPumpHeartbeat;
_pumpHeartbeat.Start();
}
}
private void StopPump()
{
CompositionTarget.Rendering -= OnRenderFrame;
if (_pumpHeartbeat != null)
{
_pumpHeartbeat.Stop();
_pumpHeartbeat.Tick -= OnPumpHeartbeat;
_pumpHeartbeat = null;
}
}
// Inside XAML's render walk — only schedule; never pump or touch the tree here.
private void OnRenderFrame(object sender, object e) => SchedulePump();
private void OnPumpHeartbeat(DispatcherQueueTimer sender, object args) => SchedulePump();
private void SchedulePump()
{
if (_pumpQueued)
{
return;
}
var dispatcherQueue = _dispatcherQueue;
if (dispatcherQueue == null)
{
return;
}
_pumpQueued = true;
dispatcherQueue.TryEnqueue(() =>
{
_pumpQueued = false;
_runtimeHost.PumpTimers();
#if DEBUG
_runtimeHost.PumpDevtools();
#endif
});
}
}
}