Skip to content

Commit 1ddd653

Browse files
johnml1135Copilot
andcommitted
Cleanup from rebase
Added deterministic cleanup for managed VwDrawRootBuffered cached GDI buffers in VwDrawRootBuffered.cs. Disposed capture-created VwDrawRootBuffered instances in CompositeViewCapture.cs and RenderBenchmarkHarness.cs. Hardened DataTree render harness teardown in DataTreeRenderHarness.cs, keeping root-site quiescing on shutdown rather than altering capture semantics. Guarded LabeledMultiStringView layout/disposal against late root-site painting/layout in LabeledMultiStringView.cs. Removed the temporary [ROOTSITE-DRAW-ERROR] diagnostic from SimpleRootSite.cs. Co-authored-by: Copilot <copilot@github.com>
1 parent 8b6e02e commit 1ddd653

7 files changed

Lines changed: 206 additions & 15 deletions

File tree

Src/Common/Controls/Widgets/LabeledMultiStringView.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ protected override void Dispose(bool disposing)
150150
if (IsDisposed)
151151
return;
152152

153+
if (disposing && m_innerView != null)
154+
{
155+
m_innerView.AllowPainting = false;
156+
m_innerView.AllowLayout = false;
157+
}
158+
153159
base.Dispose(disposing);
154160

155161
if (disposing)
@@ -253,6 +259,8 @@ CoreWritingSystemDefinition WsForSoundField(ShortSoundFieldControl sc, out int w
253259
protected override void OnLayout(LayoutEventArgs levent)
254260
{
255261
base.OnLayout(levent);
262+
if (IsDisposed || Disposing || !IsHandleCreated)
263+
return;
256264
if (m_innerView.VC == null || m_innerView.RootBox == null) // We can come in with no rootb from a dispose call.
257265
return;
258266
if (Visible)

Src/Common/Controls/XMLViews/XmlBrowseRDEView.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using SIL.LCModel.Core.Text;
1313
using SIL.LCModel.Core.KernelInterfaces;
1414
using SIL.FieldWorks.Common.FwUtils;
15+
using static SIL.FieldWorks.Common.FwUtils.FwUtils;
1516
using SIL.LCModel;
1617
using SIL.FieldWorks.Common.RootSites;
1718
using SIL.LCModel.Application;

Src/Common/RenderVerification/CompositeViewCapture.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,14 @@ private static void EnsureAllSlicesInitialized(DataTree dataTree)
185185
// Set the slice visible (required for DrawToBitmap to include it).
186186
if (!slice.Visible)
187187
slice.Visible = true;
188+
189+
var viewSlice = slice as ViewSlice;
190+
if (viewSlice != null)
191+
{
192+
var rootSite = viewSlice.RootSite;
193+
if (rootSite != null && rootSite.RootBox != null)
194+
rootSite.AllowLayout = true;
195+
}
188196
}
189197
catch (Exception ex)
190198
{
@@ -350,10 +358,12 @@ private static void OverlaySingleViewSlice(ViewSlice viewSlice, DataTree dataTre
350358
IntPtr tempHdc = tempGraphics.GetHdc();
351359
try
352360
{
353-
var vdrb = new SIL.FieldWorks.Views.VwDrawRootBuffered();
354-
var localRect = new Rect(0, 0, rootSiteRect.Width, rootSiteRect.Height);
355-
const uint whiteColor = 0x00FFFFFF;
356-
vdrb.DrawTheRoot(rootBox, tempHdc, localRect, whiteColor, true, rootSite);
361+
using (var vdrb = new SIL.FieldWorks.Views.VwDrawRootBuffered())
362+
{
363+
var localRect = new Rect(0, 0, rootSiteRect.Width, rootSiteRect.Height);
364+
const uint whiteColor = 0x00FFFFFF;
365+
vdrb.DrawTheRoot(rootBox, tempHdc, localRect, whiteColor, true, rootSite);
366+
}
357367
}
358368
finally
359369
{

Src/Common/RenderVerification/DataTreeRenderHarness.cs

Lines changed: 152 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
using System.Xml;
1313
using SIL.FieldWorks.Common.Framework.DetailControls;
1414
using SIL.FieldWorks.Common.FwUtils;
15+
using SIL.FieldWorks.Common.RootSites;
16+
using SIL.FieldWorks.Common.ViewsInterfaces;
1517
using SIL.LCModel;
1618
using XCore;
1719

@@ -282,8 +284,10 @@ public Bitmap CaptureCompositeBitmap()
282284
if (m_dataTree == null)
283285
throw new InvalidOperationException("Call PopulateSlices before capturing.");
284286

287+
bool wasVisible = m_dataTree.Visible;
285288
try
286289
{
290+
m_dataTree.Visible = true;
287291
var bitmap = CompositeViewCapture.CaptureDataTree(m_dataTree, m_captureWidth);
288292
LastCapture = bitmap;
289293
return bitmap;
@@ -293,6 +297,11 @@ public Bitmap CaptureCompositeBitmap()
293297
Trace.TraceWarning($"[DataTreeRenderHarness] Composite capture failed: {ex.Message}");
294298
return null;
295299
}
300+
finally
301+
{
302+
if (m_dataTree != null)
303+
m_dataTree.Visible = wasVisible;
304+
}
296305
}
297306

298307
/// <summary>
@@ -439,19 +448,30 @@ private void LoadTestInventories()
439448

440449
private void DisposeResources()
441450
{
442-
if (m_dataTree != null)
443-
{
444-
// DataTree gets disposed by form.Close since it's in Controls
445-
m_dataTree = null;
446-
}
447-
448451
if (m_hostForm != null)
449452
{
453+
if (m_dataTree != null)
454+
{
455+
m_dataTree.Enabled = false;
456+
m_dataTree.Visible = false;
457+
m_hostForm.Visible = false;
458+
SuppressHostedRootSiteDrawing(m_dataTree);
459+
DrainHostedRootBoxDrawingErrors(m_dataTree);
460+
QuiesceHostedRootSites(m_dataTree);
461+
m_dataTree.Reset();
462+
}
463+
450464
m_hostForm.Close();
451465
m_hostForm.Dispose();
452466
m_hostForm = null;
453467
}
454468

469+
if (m_dataTree != null)
470+
{
471+
// DataTree gets disposed by form.Close since it's in Controls
472+
m_dataTree = null;
473+
}
474+
455475
if (m_propertyTable != null)
456476
{
457477
m_propertyTable.Dispose();
@@ -465,6 +485,132 @@ private void DisposeResources()
465485
}
466486
}
467487

488+
private void QuiesceHostedRootSites(DataTree dataTree)
489+
{
490+
var rootSites = new HashSet<SimpleRootSite>();
491+
CollectHostedRootSites(dataTree, rootSites);
492+
foreach (var rootSite in rootSites)
493+
QuiesceRootSite(rootSite);
494+
}
495+
496+
private void DrainHostedRootBoxDrawingErrors(DataTree dataTree)
497+
{
498+
var rootSites = new HashSet<SimpleRootSite>();
499+
CollectHostedRootSites(dataTree, rootSites);
500+
foreach (var rootSite in rootSites)
501+
DrainRootBoxDrawingErrors(rootSite);
502+
}
503+
504+
private void SuppressHostedRootSiteDrawing(DataTree dataTree)
505+
{
506+
var rootSites = new HashSet<SimpleRootSite>();
507+
CollectHostedRootSites(dataTree, rootSites);
508+
foreach (var rootSite in rootSites)
509+
SuppressRootSiteDrawing(rootSite);
510+
}
511+
512+
private void CollectHostedRootSites(DataTree dataTree, ISet<SimpleRootSite> rootSites)
513+
{
514+
if (dataTree.Slices != null)
515+
{
516+
foreach (Slice slice in dataTree.Slices)
517+
{
518+
var viewSlice = slice as ViewSlice;
519+
if (viewSlice == null)
520+
continue;
521+
522+
try
523+
{
524+
var rootSite = viewSlice.RootSite;
525+
if (rootSite != null)
526+
rootSites.Add(rootSite);
527+
}
528+
catch
529+
{
530+
// Shutdown path only: slices may already be partially disposed.
531+
}
532+
}
533+
}
534+
535+
CollectRootSites(dataTree, rootSites);
536+
}
537+
538+
private void CollectRootSites(Control control, ISet<SimpleRootSite> rootSites)
539+
{
540+
if (control == null)
541+
return;
542+
543+
var rootSite = control as SimpleRootSite;
544+
if (rootSite != null)
545+
rootSites.Add(rootSite);
546+
547+
foreach (Control child in control.Controls)
548+
CollectRootSites(child, rootSites);
549+
}
550+
551+
private void QuiesceRootSite(SimpleRootSite rootSite)
552+
{
553+
try
554+
{
555+
SuppressRootSiteDrawing(rootSite);
556+
rootSite.AboutToDiscard();
557+
rootSite.CloseRootBox();
558+
}
559+
catch
560+
{
561+
// Shutdown path only: controls may already be partially disposed.
562+
}
563+
}
564+
565+
private void SuppressRootSiteDrawing(SimpleRootSite rootSite)
566+
{
567+
try
568+
{
569+
rootSite.AllowPainting = false;
570+
rootSite.AllowLayout = false;
571+
rootSite.Visible = false;
572+
}
573+
catch
574+
{
575+
// Shutdown path only: controls may already be partially disposed.
576+
}
577+
}
578+
579+
private void DrainRootBoxDrawingErrors(SimpleRootSite rootSite)
580+
{
581+
IVwRootBox rootBox = null;
582+
try
583+
{
584+
rootBox = rootSite.RootBox;
585+
}
586+
catch
587+
{
588+
return;
589+
}
590+
591+
if (rootBox == null)
592+
return;
593+
594+
IVwGraphics graphics = null;
595+
try
596+
{
597+
Rect sourceRectangle;
598+
Rect destinationRectangle;
599+
rootSite.GetGraphics(rootBox, out graphics, out sourceRectangle, out destinationRectangle);
600+
rootBox.DrawingErrors(graphics);
601+
}
602+
catch (Exception ex)
603+
{
604+
Trace.TraceWarning(
605+
$"[DataTreeRenderHarness] Suppressed deferred drawing error for '{rootSite.Name}': {ex.Message}");
606+
}
607+
finally
608+
{
609+
if (graphics != null)
610+
rootSite.ReleaseGraphics(rootBox, graphics);
611+
}
612+
}
613+
468614
/// <summary>Releases all resources used by the harness.</summary>
469615
public void Dispose()
470616
{

Src/Common/RootSite/RootSiteTests/RenderBenchmarkHarness.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,12 @@ public Bitmap CaptureViewBitmap()
272272
IntPtr hdc = graphics.GetHdc();
273273
try
274274
{
275-
var vdrb = new SIL.FieldWorks.Views.VwDrawRootBuffered();
276-
var clientRect = new Rect(0, 0, width, height);
277-
const uint whiteColor = 0x00FFFFFF;
278-
vdrb.DrawTheRoot(m_view.RootBox, hdc, clientRect, whiteColor, true, m_view);
275+
using (var vdrb = new SIL.FieldWorks.Views.VwDrawRootBuffered())
276+
{
277+
var clientRect = new Rect(0, 0, width, height);
278+
const uint whiteColor = 0x00FFFFFF;
279+
vdrb.DrawTheRoot(m_view.RootBox, hdc, clientRect, whiteColor, true, m_view);
280+
}
279281
}
280282
finally
281283
{

Src/Common/SimpleRootSite/SimpleRootSite.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,8 @@ protected override void Dispose(bool disposing)
533533

534534
if (m_vdrb != null && Marshal.IsComObject(m_vdrb))
535535
Marshal.ReleaseComObject(m_vdrb);
536+
else if (m_vdrb is IDisposable disposableVdrb)
537+
disposableVdrb.Dispose();
536538
m_vdrb = null;
537539
if (m_styleSheet != null && Marshal.IsComObject(m_styleSheet))
538540
Marshal.ReleaseComObject(m_styleSheet);

Src/ManagedVwDrawRootBuffered/VwDrawRootBuffered.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ namespace SIL.FieldWorks.Views
1717
/// </summary>
1818
[ComVisible(true)]
1919
[Guid("97199458-10C7-49da-B3AE-EA922EA64859")]
20-
public class VwDrawRootBuffered : IVwDrawRootBuffered
20+
public class VwDrawRootBuffered : IVwDrawRootBuffered, IDisposable
2121
{
2222
private GdiMemoryBuffer m_cachedBuffer;
23+
private bool m_isDisposed;
2324

2425
private class GdiMemoryBuffer : IDisposable
2526
{
@@ -105,6 +106,27 @@ private void ReplaceCachedBuffer(GdiMemoryBuffer newBuffer)
105106
m_cachedBuffer = newBuffer;
106107
}
107108

109+
public void Dispose()
110+
{
111+
Dispose(true);
112+
GC.SuppressFinalize(this);
113+
}
114+
115+
~VwDrawRootBuffered()
116+
{
117+
Dispose(false);
118+
}
119+
120+
protected virtual void Dispose(bool disposing)
121+
{
122+
if (m_isDisposed)
123+
return;
124+
125+
m_cachedBuffer?.Dispose();
126+
m_cachedBuffer = null;
127+
m_isDisposed = true;
128+
}
129+
108130
/// <summary>
109131
/// See C++ documentation
110132
/// </summary>

0 commit comments

Comments
 (0)