feat: restore legacy modal and drag behavior
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using MBN_STOCK_WEBVIEW.Infrastructure;
|
||||
using MBN_STOCK_WEBVIEW.LegacyApplication;
|
||||
using MBN_STOCK_WEBVIEW.LegacyBridge;
|
||||
@@ -11,17 +12,31 @@ namespace MBN_STOCK_WEBVIEW.LegacyParityApp;
|
||||
|
||||
public sealed partial class MainWindow : Window
|
||||
{
|
||||
private const int SmCxDrag = 68;
|
||||
private const int SmCyDrag = 69;
|
||||
private const uint WmSettingChange = 0x001A;
|
||||
private const uint WmDisplayChange = 0x007E;
|
||||
private const uint WmDpiChanged = 0x02E0;
|
||||
private const nuint InteractionMetricsSubclassId = 0x4D424E49;
|
||||
|
||||
private readonly CancellationTokenSource _lifetimeCancellation = new();
|
||||
private readonly SemaphoreSlim _intentGate = new(1, 1);
|
||||
private readonly WindowSubclassProcedure _windowSubclassProcedure;
|
||||
private readonly LegacyOperatorController _controller;
|
||||
private readonly LegacyIndustrySelectionWorkflow _industryWorkflow;
|
||||
private DatabaseRuntime? _databaseRuntime;
|
||||
private bool _closing;
|
||||
private bool _interactionMetricsRefreshQueued;
|
||||
private bool _intentBusy;
|
||||
private bool _windowSubclassAttached;
|
||||
private bool _webViewReady;
|
||||
private nint _windowHandle;
|
||||
private LegacyInteractionMetrics _interactionMetrics =
|
||||
LegacyInteractionMetrics.Default;
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
_windowSubclassProcedure = OnWindowSubclassMessage;
|
||||
InitializeComponent();
|
||||
EnsureCloseConfirmationAttached();
|
||||
var localApplicationData = Environment.GetFolderPath(
|
||||
@@ -202,6 +217,9 @@ public sealed partial class MainWindow : Window
|
||||
{
|
||||
EnsureCloseConfirmationAttached();
|
||||
var windowHandle = WindowNative.GetWindowHandle(this);
|
||||
_windowHandle = windowHandle;
|
||||
AttachInteractionMetricsRefresh();
|
||||
RefreshInteractionMetrics();
|
||||
var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(windowHandle);
|
||||
var appWindow = AppWindow.GetFromWindowId(windowId);
|
||||
appWindow.Title = "V-Stock 증권정보송출시스템 for 매일경제TV (26.03.26)";
|
||||
@@ -244,8 +262,11 @@ public sealed partial class MainWindow : Window
|
||||
CoreWebView2HostResourceAccessKind.DenyCors);
|
||||
coreWebView.Settings.AreDevToolsEnabled =
|
||||
System.Diagnostics.Debugger.IsAttached;
|
||||
coreWebView.Settings.AreBrowserAcceleratorKeysEnabled = false;
|
||||
coreWebView.Settings.AreDefaultContextMenusEnabled = false;
|
||||
coreWebView.Settings.IsStatusBarEnabled = false;
|
||||
coreWebView.Settings.IsZoomControlEnabled = false;
|
||||
coreWebView.Settings.IsPinchZoomEnabled = false;
|
||||
coreWebView.NavigationStarting += OnNavigationStarting;
|
||||
coreWebView.NavigationCompleted += OnNavigationCompleted;
|
||||
coreWebView.NewWindowRequested += OnNewWindowRequested;
|
||||
@@ -327,6 +348,7 @@ public sealed partial class MainWindow : Window
|
||||
private async Task ProcessIntentAsync(LegacyUiIntent intent)
|
||||
{
|
||||
var gateEntered = false;
|
||||
var dispatchStarted = false;
|
||||
try
|
||||
{
|
||||
// MainForm performed database search synchronously. The busy snapshot
|
||||
@@ -337,7 +359,11 @@ public sealed partial class MainWindow : Window
|
||||
_lifetimeCancellation.Token);
|
||||
if (!gateEntered)
|
||||
{
|
||||
PostState(_controller.Current);
|
||||
PostState(IsNamedPlaylistModalIntent(intent)
|
||||
? ReportIntentFailure(
|
||||
intent,
|
||||
"다른 요청을 처리 중이어서 DB 재생목록 요청을 시작하지 않았습니다.")
|
||||
: _controller.Current);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -348,7 +374,8 @@ public sealed partial class MainWindow : Window
|
||||
|
||||
if (IsOperatorMutationIntent(intent) && IsOperatorMutationQuarantined())
|
||||
{
|
||||
PostState(_controller.ReportError(
|
||||
PostState(ReportIntentFailure(
|
||||
intent,
|
||||
"이전 저장 결과가 불명확하여 이번 실행의 추가 변경이 차단되었습니다. DB 또는 로컬 파일을 읽기 전용으로 대조한 뒤 앱을 다시 시작하세요."));
|
||||
return;
|
||||
}
|
||||
@@ -357,14 +384,16 @@ public sealed partial class MainWindow : Window
|
||||
IsOperatorMutationIntent(intent) &&
|
||||
!IsFixedSectionBatchMutationIntent(intent))
|
||||
{
|
||||
PostState(_controller.ReportError(
|
||||
PostState(ReportIntentFailure(
|
||||
intent,
|
||||
"고정 컷 섹션의 수동 입력 중에는 현재 입력 단계만 변경할 수 있습니다. 완료하거나 취소한 뒤 다른 편성 작업을 실행하세요."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsOperatorMutationIntent(intent) && !CanAcceptOperatorMutation())
|
||||
{
|
||||
PostState(_controller.ReportError(
|
||||
PostState(ReportIntentFailure(
|
||||
intent,
|
||||
"PREPARE 또는 송출 중에는 플레이리스트와 운영 데이터를 변경할 수 없습니다. TAKE OUT 후 다시 시도하세요."));
|
||||
return;
|
||||
}
|
||||
@@ -433,6 +462,7 @@ public sealed partial class MainWindow : Window
|
||||
PostState(_controller.Current);
|
||||
}
|
||||
|
||||
dispatchStarted = true;
|
||||
var state = await HandleIntentAsync(intent, _lifetimeCancellation.Token);
|
||||
ObserveOperatorMutationQuarantine(state);
|
||||
if (!_closing)
|
||||
@@ -450,8 +480,12 @@ public sealed partial class MainWindow : Window
|
||||
catch
|
||||
{
|
||||
_intentBusy = false;
|
||||
PostState(_controller.ReportError(
|
||||
"요청을 처리하지 못했습니다. 데이터베이스 연결 상태를 확인하세요."));
|
||||
var failureState = ReportIntentFailure(
|
||||
intent,
|
||||
"요청을 처리하지 못했습니다. 데이터베이스 연결 상태를 확인하세요.",
|
||||
writeOutcomeMayBeUnknown: dispatchStarted);
|
||||
ObserveOperatorMutationQuarantine(failureState);
|
||||
PostState(failureState);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -467,6 +501,82 @@ public sealed partial class MainWindow : Window
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsNamedPlaylistModalIntent(LegacyUiIntent intent) =>
|
||||
intent is LegacyRefreshNamedPlaylistsIntent or
|
||||
LegacySelectNamedPlaylistIntent or
|
||||
LegacyLoadSelectedNamedPlaylistIntent or
|
||||
LegacyLoadNamedPlaylistByIdIntent or
|
||||
LegacyCreateNamedPlaylistIntent or
|
||||
LegacySaveCurrentNamedPlaylistIntent or
|
||||
LegacySaveCurrentNamedPlaylistToIntent or
|
||||
LegacyDeleteSelectedNamedPlaylistIntent;
|
||||
|
||||
private LegacyOperatorSnapshot ReportIntentFailure(
|
||||
LegacyUiIntent intent,
|
||||
string message,
|
||||
bool writeOutcomeMayBeUnknown = false) =>
|
||||
intent switch
|
||||
{
|
||||
LegacyRefreshNamedPlaylistsIntent refresh
|
||||
when !string.IsNullOrWhiteSpace(refresh.RequestId) =>
|
||||
_controller.ReportNamedPlaylistCommandFailure(
|
||||
"refresh-named-playlists",
|
||||
refresh.RequestId,
|
||||
message),
|
||||
LegacySelectNamedPlaylistIntent selection
|
||||
when !string.IsNullOrWhiteSpace(selection.RequestId) =>
|
||||
_controller.ReportNamedPlaylistCommandFailure(
|
||||
"select-named-playlist",
|
||||
selection.RequestId,
|
||||
message),
|
||||
LegacyCreateNamedPlaylistIntent create
|
||||
when writeOutcomeMayBeUnknown &&
|
||||
!string.IsNullOrWhiteSpace(create.RequestId) =>
|
||||
_controller.ReportNamedPlaylistWriteOutcomeUnknown(
|
||||
"create-named-playlist",
|
||||
create.RequestId,
|
||||
message),
|
||||
LegacyCreateNamedPlaylistIntent create
|
||||
when !string.IsNullOrWhiteSpace(create.RequestId) =>
|
||||
_controller.ReportNamedPlaylistCommandFailure(
|
||||
"create-named-playlist",
|
||||
create.RequestId,
|
||||
message),
|
||||
LegacyDeleteSelectedNamedPlaylistIntent delete
|
||||
when writeOutcomeMayBeUnknown &&
|
||||
!string.IsNullOrWhiteSpace(delete.RequestId) =>
|
||||
_controller.ReportNamedPlaylistWriteOutcomeUnknown(
|
||||
"delete-selected-named-playlist",
|
||||
delete.RequestId,
|
||||
message),
|
||||
LegacyDeleteSelectedNamedPlaylistIntent delete
|
||||
when !string.IsNullOrWhiteSpace(delete.RequestId) =>
|
||||
_controller.ReportNamedPlaylistCommandFailure(
|
||||
"delete-selected-named-playlist",
|
||||
delete.RequestId,
|
||||
message),
|
||||
LegacyLoadNamedPlaylistByIdIntent load
|
||||
when !string.IsNullOrWhiteSpace(load.DefinitionId) =>
|
||||
_controller.ReportNamedPlaylistCommandFailure(
|
||||
"load-named-playlist-by-id",
|
||||
load.DefinitionId,
|
||||
message),
|
||||
LegacySaveCurrentNamedPlaylistToIntent save
|
||||
when writeOutcomeMayBeUnknown &&
|
||||
!string.IsNullOrWhiteSpace(save.DefinitionId) =>
|
||||
_controller.ReportNamedPlaylistWriteOutcomeUnknown(
|
||||
"save-current-named-playlist-to",
|
||||
save.DefinitionId,
|
||||
message),
|
||||
LegacySaveCurrentNamedPlaylistToIntent save
|
||||
when !string.IsNullOrWhiteSpace(save.DefinitionId) =>
|
||||
_controller.ReportNamedPlaylistCommandFailure(
|
||||
"save-current-named-playlist-to",
|
||||
save.DefinitionId,
|
||||
message),
|
||||
_ => _controller.ReportError(message)
|
||||
};
|
||||
|
||||
private async Task<LegacyOperatorSnapshot> HandleIntentAsync(
|
||||
LegacyUiIntent intent,
|
||||
CancellationToken cancellationToken)
|
||||
@@ -714,10 +824,14 @@ public sealed partial class MainWindow : Window
|
||||
cancellationToken),
|
||||
LegacyDeleteOperatorCatalogIntent =>
|
||||
await _controller.DeleteOperatorCatalogAsync(cancellationToken),
|
||||
LegacyRefreshNamedPlaylistsIntent =>
|
||||
await _controller.RefreshNamedPlaylistsAsync(cancellationToken),
|
||||
LegacyRefreshNamedPlaylistsIntent refresh =>
|
||||
await RefreshNamedPlaylistsForDispatchAsync(
|
||||
refresh.RequestId,
|
||||
cancellationToken),
|
||||
LegacySelectNamedPlaylistIntent selection =>
|
||||
_controller.SelectNamedPlaylist(selection.DefinitionId),
|
||||
SelectNamedPlaylistForDispatch(
|
||||
selection.DefinitionId,
|
||||
selection.RequestId),
|
||||
LegacyLoadSelectedNamedPlaylistIntent =>
|
||||
await _controller.LoadSelectedNamedPlaylistAsync(cancellationToken),
|
||||
LegacyLoadNamedPlaylistByIdIntent load =>
|
||||
@@ -725,8 +839,9 @@ public sealed partial class MainWindow : Window
|
||||
load.DefinitionId,
|
||||
cancellationToken),
|
||||
LegacyCreateNamedPlaylistIntent create =>
|
||||
await _controller.CreateNamedPlaylistAsync(
|
||||
await CreateNamedPlaylistForDispatchAsync(
|
||||
create.Title,
|
||||
create.RequestId,
|
||||
cancellationToken),
|
||||
LegacySaveCurrentNamedPlaylistIntent =>
|
||||
await _controller.SaveCurrentNamedPlaylistAsync(cancellationToken),
|
||||
@@ -734,8 +849,10 @@ public sealed partial class MainWindow : Window
|
||||
await _controller.SaveCurrentNamedPlaylistToAsync(
|
||||
save.DefinitionId,
|
||||
cancellationToken),
|
||||
LegacyDeleteSelectedNamedPlaylistIntent =>
|
||||
await _controller.DeleteSelectedNamedPlaylistAsync(cancellationToken),
|
||||
LegacyDeleteSelectedNamedPlaylistIntent delete =>
|
||||
await DeleteNamedPlaylistForDispatchAsync(
|
||||
delete.RequestId,
|
||||
cancellationToken),
|
||||
LegacyOpenManualFinancialIntent manual =>
|
||||
await _controller.OpenManualFinancialAsync(
|
||||
manual.Screen,
|
||||
@@ -835,18 +952,280 @@ public sealed partial class MainWindow : Window
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<LegacyOperatorSnapshot> RefreshNamedPlaylistsForDispatchAsync(
|
||||
string requestId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var state = await _controller.RefreshNamedPlaylistsAsync(cancellationToken);
|
||||
var succeeded = state.NamedPlaylist?.ReadStatus ==
|
||||
LegacyNamedPlaylistReadStatus.Ready;
|
||||
return _controller.CompleteNamedPlaylistDispatch(
|
||||
"refresh-named-playlists",
|
||||
requestId,
|
||||
succeeded);
|
||||
}
|
||||
|
||||
private LegacyOperatorSnapshot SelectNamedPlaylistForDispatch(
|
||||
string definitionId,
|
||||
string requestId)
|
||||
{
|
||||
var state = _controller.SelectNamedPlaylist(definitionId);
|
||||
var succeeded = string.Equals(
|
||||
state.NamedPlaylist?.SelectedDefinitionId,
|
||||
definitionId,
|
||||
StringComparison.Ordinal);
|
||||
return _controller.CompleteNamedPlaylistDispatch(
|
||||
"select-named-playlist",
|
||||
requestId,
|
||||
succeeded);
|
||||
}
|
||||
|
||||
private async Task<LegacyOperatorSnapshot> CreateNamedPlaylistForDispatchAsync(
|
||||
string title,
|
||||
string requestId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var previousRevision = _controller.Current.NamedPlaylist?.Revision ?? -1;
|
||||
var state = await _controller.CreateNamedPlaylistAsync(title, cancellationToken);
|
||||
var succeeded = (state.NamedPlaylist?.Revision ?? -1) > previousRevision &&
|
||||
IsCommittedNamedPlaylistMutation(state.NamedPlaylist?.LastMutationOutcome);
|
||||
return _controller.CompleteNamedPlaylistDispatch(
|
||||
"create-named-playlist",
|
||||
requestId,
|
||||
succeeded);
|
||||
}
|
||||
|
||||
private async Task<LegacyOperatorSnapshot> DeleteNamedPlaylistForDispatchAsync(
|
||||
string requestId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var previousRevision = _controller.Current.NamedPlaylist?.Revision ?? -1;
|
||||
var state = await _controller.DeleteSelectedNamedPlaylistAsync(cancellationToken);
|
||||
var succeeded = (state.NamedPlaylist?.Revision ?? -1) > previousRevision &&
|
||||
IsCommittedNamedPlaylistMutation(state.NamedPlaylist?.LastMutationOutcome);
|
||||
return _controller.CompleteNamedPlaylistDispatch(
|
||||
"delete-selected-named-playlist",
|
||||
requestId,
|
||||
succeeded);
|
||||
}
|
||||
|
||||
private static bool IsCommittedNamedPlaylistMutation(
|
||||
LegacyNamedPlaylistMutationOutcome? outcome) =>
|
||||
outcome is LegacyNamedPlaylistMutationOutcome.CommittedFresh or
|
||||
LegacyNamedPlaylistMutationOutcome.CommittedOptimistic;
|
||||
|
||||
private void PostState(LegacyOperatorSnapshot snapshot)
|
||||
{
|
||||
if (!_closing && _webViewReady && Browser.CoreWebView2 is not null)
|
||||
{
|
||||
// SystemInformation.DragSize was evaluated on every original
|
||||
// ListView.MouseDown. Refreshing at every native-to-web state boundary,
|
||||
// in addition to the settings/DPI message hook, prevents a startup-only
|
||||
// value from surviving a runtime Windows preference change.
|
||||
RefreshInteractionMetrics();
|
||||
Browser.CoreWebView2.PostWebMessageAsJson(
|
||||
LegacyBridgeProtocol.SerializeState(
|
||||
snapshot,
|
||||
_intentBusy,
|
||||
CreatePlayoutSnapshot()));
|
||||
CreatePlayoutSnapshot(),
|
||||
interactionMetrics: _interactionMetrics));
|
||||
}
|
||||
}
|
||||
|
||||
private void AttachInteractionMetricsRefresh()
|
||||
{
|
||||
if (_windowSubclassAttached || _windowHandle == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_windowSubclassAttached = SetWindowSubclass(
|
||||
_windowHandle,
|
||||
_windowSubclassProcedure,
|
||||
InteractionMetricsSubclassId,
|
||||
0);
|
||||
}
|
||||
catch (DllNotFoundException)
|
||||
{
|
||||
}
|
||||
catch (EntryPointNotFoundException)
|
||||
{
|
||||
}
|
||||
catch (BadImageFormatException)
|
||||
{
|
||||
}
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void DetachInteractionMetricsRefresh()
|
||||
{
|
||||
if (!_windowSubclassAttached || _windowHandle == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
RemoveWindowSubclass(
|
||||
_windowHandle,
|
||||
_windowSubclassProcedure,
|
||||
InteractionMetricsSubclassId);
|
||||
}
|
||||
catch (DllNotFoundException)
|
||||
{
|
||||
}
|
||||
catch (EntryPointNotFoundException)
|
||||
{
|
||||
}
|
||||
catch (BadImageFormatException)
|
||||
{
|
||||
}
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
_windowSubclassAttached = false;
|
||||
}
|
||||
}
|
||||
|
||||
private nint OnWindowSubclassMessage(
|
||||
nint windowHandle,
|
||||
uint message,
|
||||
nuint wParam,
|
||||
nint lParam,
|
||||
nuint subclassId,
|
||||
nuint referenceData)
|
||||
{
|
||||
if (message is WmSettingChange or WmDisplayChange or WmDpiChanged)
|
||||
{
|
||||
QueueInteractionMetricsRefresh();
|
||||
}
|
||||
|
||||
return DefSubclassProc(windowHandle, message, wParam, lParam);
|
||||
}
|
||||
|
||||
private void QueueInteractionMetricsRefresh()
|
||||
{
|
||||
if (_closing || _interactionMetricsRefreshQueued)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_interactionMetricsRefreshQueued = true;
|
||||
if (!DispatcherQueue.TryEnqueue(
|
||||
Microsoft.UI.Dispatching.DispatcherQueuePriority.High,
|
||||
() =>
|
||||
{
|
||||
_interactionMetricsRefreshQueued = false;
|
||||
if (_closing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshInteractionMetrics();
|
||||
if (_webViewReady)
|
||||
{
|
||||
PostState(_controller.Current);
|
||||
}
|
||||
}))
|
||||
{
|
||||
_interactionMetricsRefreshQueued = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshInteractionMetrics()
|
||||
{
|
||||
_interactionMetrics = ReadSystemInteractionMetrics();
|
||||
}
|
||||
|
||||
private LegacyInteractionMetrics ReadSystemInteractionMetrics()
|
||||
{
|
||||
try
|
||||
{
|
||||
// GetSystemMetrics returns physical pixels and is not DPI-aware.
|
||||
// Asking the DPI-aware API for its 96-DPI values produces the same
|
||||
// logical DIP coordinate space used by XAML. The bridge also carries
|
||||
// XamlRoot.RasterizationScale so JavaScript can account for a zoom
|
||||
// value retained by an older WebView profile without private API use.
|
||||
return LegacyInteractionMetrics.FromPixelsAtDpi(
|
||||
GetSystemMetricsForDpi(
|
||||
SmCxDrag,
|
||||
LegacyInteractionMetrics.CssPixelsPerInch),
|
||||
GetSystemMetricsForDpi(
|
||||
SmCyDrag,
|
||||
LegacyInteractionMetrics.CssPixelsPerInch),
|
||||
LegacyInteractionMetrics.CssPixelsPerInch,
|
||||
ReadHostRasterizationScale());
|
||||
}
|
||||
catch (DllNotFoundException)
|
||||
{
|
||||
return CreateFallbackInteractionMetrics();
|
||||
}
|
||||
catch (EntryPointNotFoundException)
|
||||
{
|
||||
return CreateFallbackInteractionMetrics();
|
||||
}
|
||||
catch (BadImageFormatException)
|
||||
{
|
||||
return CreateFallbackInteractionMetrics();
|
||||
}
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
return CreateFallbackInteractionMetrics();
|
||||
}
|
||||
}
|
||||
|
||||
private LegacyInteractionMetrics CreateFallbackInteractionMetrics() =>
|
||||
LegacyInteractionMetrics.Default with
|
||||
{
|
||||
HostRasterizationScale = ReadHostRasterizationScale()
|
||||
};
|
||||
|
||||
private double ReadHostRasterizationScale()
|
||||
{
|
||||
var scale = Root.XamlRoot?.RasterizationScale ?? 1d;
|
||||
return double.IsFinite(scale) && scale > 0d ? scale : 1d;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", ExactSpelling = true)]
|
||||
private static extern int GetSystemMetricsForDpi(int nIndex, uint dpi);
|
||||
|
||||
[DllImport("comctl32.dll", ExactSpelling = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool SetWindowSubclass(
|
||||
nint windowHandle,
|
||||
WindowSubclassProcedure subclassProcedure,
|
||||
nuint subclassId,
|
||||
nuint referenceData);
|
||||
|
||||
[DllImport("comctl32.dll", ExactSpelling = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool RemoveWindowSubclass(
|
||||
nint windowHandle,
|
||||
WindowSubclassProcedure subclassProcedure,
|
||||
nuint subclassId);
|
||||
|
||||
[DllImport("comctl32.dll", ExactSpelling = true)]
|
||||
private static extern nint DefSubclassProc(
|
||||
nint windowHandle,
|
||||
uint message,
|
||||
nuint wParam,
|
||||
nint lParam);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
private delegate nint WindowSubclassProcedure(
|
||||
nint windowHandle,
|
||||
uint message,
|
||||
nuint wParam,
|
||||
nint lParam,
|
||||
nuint subclassId,
|
||||
nuint referenceData);
|
||||
|
||||
private void OnProcessFailed(object? sender, CoreWebView2ProcessFailedEventArgs args)
|
||||
{
|
||||
ObserveOperatorMutationQuarantine(
|
||||
@@ -882,6 +1261,7 @@ public sealed partial class MainWindow : Window
|
||||
Root.Loaded -= OnRootLoaded;
|
||||
Closed -= OnClosed;
|
||||
DetachCloseConfirmation();
|
||||
DetachInteractionMetricsRefresh();
|
||||
_lifetimeCancellation.Cancel();
|
||||
ShutdownPlayoutRuntime();
|
||||
ShutdownManualListsRuntime();
|
||||
|
||||
Reference in New Issue
Block a user