fix: close all automation-opened documents

This commit is contained in:
codex
2026-08-03 17:10:02 +03:00
parent ef615a1f8e
commit 33f91c26aa
2 changed files with 15 additions and 9 deletions
@@ -129,6 +129,7 @@ internal sealed class FirstLimitedApplyAutomationCoordinator
} }
private FirstApplyAutomationConfiguration? config; private FirstApplyAutomationConfiguration? config;
private Document? document; private Document? document;
private readonly List<Document> automationOpenedDocuments = [];
private readonly List<string> completed = []; private readonly List<string> completed = [];
private int step; private int step;
private int? before, after1, after2, afterUndo; private int? before, after1, after2, afterUndo;
@@ -159,9 +160,10 @@ internal sealed class FirstLimitedApplyAutomationCoordinator
var project = PlantProject.LoadProject(projectXml, false, string.Empty, string.Empty); var project = PlantProject.LoadProject(projectXml, false, string.Empty, string.Empty);
PlantApplication.SetCurrentProject(project, true); PlantApplication.SetCurrentProject(project, true);
transitions.Add("ProjectActivated"); transitions.Add("ProjectActivated");
if (!string.IsNullOrWhiteSpace(config.ModelDrawingPath)) AcadApplication.DocumentManager.Open(config.ModelDrawingPath, false); if (!string.IsNullOrWhiteSpace(config.ModelDrawingPath)) automationOpenedDocuments.Add(AcadApplication.DocumentManager.Open(config.ModelDrawingPath, false));
transitions.Add("DocumentOpenStarted"); transitions.Add("DocumentOpenStarted");
document = AcadApplication.DocumentManager.Open(config.OrthoDrawingPath, false); document = AcadApplication.DocumentManager.Open(config.OrthoDrawingPath, false);
automationOpenedDocuments.Add(document);
AcadApplication.DocumentManager.MdiActiveDocument = document; AcadApplication.DocumentManager.MdiActiveDocument = document;
transitions.Add("DocumentOpened"); transitions.Add("DocumentOpened");
document.CommandEnded += OnCommandEnded; document.CommandEnded += OnCommandEnded;
@@ -315,12 +317,15 @@ internal sealed class FirstLimitedApplyAutomationCoordinator
exitHandlerSubscribed = false; exitHandlerSubscribed = false;
// Do not close a cached automation document and then reuse it. Resolve // Do not close a cached automation document and then reuse it. Resolve
// the currently-live document only at the final quiescent callback. // the currently-live document only at the final quiescent callback.
var active = AcadApplication.DocumentManager.MdiActiveDocument; // Only documents opened by this coordinator may be closed. Keep no
if (active is not null) // document reference after closing it; DocumentDestroyed can run
// immediately as part of CloseAndDiscard.
foreach (var opened in automationOpenedDocuments.ToArray().Reverse())
{ {
transitions.Add("ActiveDocumentCloseRequested"); transitions.Add("AutomationDocumentCloseRequested");
active.CloseAndDiscard(); opened.CloseAndDiscard();
} }
automationOpenedDocuments.Clear();
// Application.Quit alone leaves the script host alive. Close the // Application.Quit alone leaves the script host alive. Close the
// live document first, then request normal application shutdown. // live document first, then request normal application shutdown.
transitions.Add("ApplicationQuitRequested"); transitions.Add("ApplicationQuitRequested");
@@ -82,12 +82,13 @@ public sealed class FoundationTests
Assert.Contains("PostStartupReady", source); Assert.Contains("PostStartupReady", source);
} }
[Fact] public void AutomationControlledExit_ClosesLiveDocumentThenQuitsFromIdle() [Fact] public void AutomationControlledExit_ClosesOnlyAutomationDocumentsThenQuits()
{ {
var source = File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "FirstLimitedApplyAutomation.cs")); var source = File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "FirstLimitedApplyAutomation.cs"));
Assert.Contains("RequestControlledExit", source); Assert.Contains("RequestControlledExit", source);
Assert.Contains("AcadApplication.Idle += OnExitIdle", source); Assert.Contains("AcadApplication.Idle += OnExitIdle", source);
Assert.Contains("active.CloseAndDiscard();", source); Assert.Contains("automationOpenedDocuments", source);
Assert.Contains("opened.CloseAndDiscard();", source);
Assert.Contains("AcadApplication.Quit();", source); Assert.Contains("AcadApplication.Quit();", source);
} }
@@ -101,7 +102,7 @@ public sealed class FoundationTests
[Fact] public void Idle_WhenActiveDocumentNull_DoesNotThrow() [Fact] public void Idle_WhenActiveDocumentNull_DoesNotThrow()
{ {
var source = File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "FirstLimitedApplyAutomation.cs")); var source = File.ReadAllText(Path.Combine(ProjectRoot, "src", "PRecAnnot.Plant3D2026", "FirstLimitedApplyAutomation.cs"));
Assert.Contains("if (active is not null)", source); Assert.DoesNotContain("MdiActiveDocument", source[source.IndexOf("private void DispatchNativeQuit", StringComparison.Ordinal)..]);
Assert.Contains("AcadApplication.Quit();", source); Assert.Contains("AcadApplication.Quit();", source);
} }
@@ -143,7 +144,7 @@ public sealed class FoundationTests
var controlledExit = source[source.IndexOf("private void RequestControlledExit", StringComparison.Ordinal)..]; var controlledExit = source[source.IndexOf("private void RequestControlledExit", StringComparison.Ordinal)..];
Assert.DoesNotContain("document.CloseAndDiscard", controlledExit); Assert.DoesNotContain("document.CloseAndDiscard", controlledExit);
Assert.Contains("Resolve", controlledExit, StringComparison.OrdinalIgnoreCase); Assert.Contains("Resolve", controlledExit, StringComparison.OrdinalIgnoreCase);
Assert.Contains("MdiActiveDocument", controlledExit); Assert.DoesNotContain("MdiActiveDocument", controlledExit);
} }
[Fact] public void FinalCommandCompletion_UsesItsQuiescentExitBoundary() [Fact] public void FinalCommandCompletion_UsesItsQuiescentExitBoundary()