Found by: the issue #201 oracle sweep, docs/notes/oracle-sweep-2026-07-10.md; catalogued as docs/notes/field-notes-patterns.md entries 12-13.
The patterns
(a) Controls/ToolStripItemCollection membership. A WinForms Control-derived field is added to this.Controls (or a ToolStrip's Items) inside InitializeComponent(); the class never calls .Dispose() on the field directly.
private System.Windows.Forms.Label lblStatus;
lblStatus = new Label();
this.Controls.Add(this.lblStatus);
protected override void Dispose(bool disposing) {
if (disposing) components?.Dispose();
base.Dispose(disposing); // <- Control.Dispose(bool) recursively disposes every Controls child, incl. lblStatus
}
System.Windows.Forms.Control.Dispose(bool) recursively disposes every child in its own Controls collection — that's the framework's own designer-generated contract. Same holds for a ToolStrip/ContextMenuStrip's ToolStripItemCollection.
(b) IContainer-registered component. A component constructed via new T(components) (or explicitly components.Add(x)) is registered with the designer's IContainer; components.Dispose() (the one-liner every WinForms Form/UserControl already has) disposes it.
protected NotifyIcon TrayIcon;
TrayIcon = new NotifyIcon(components); // registers with the IContainer
// Dispose(bool): components.Dispose() disposes everything registered above
Evidence — ShareX/ShareX, ~30 own-only findings from these two shapes
Controls.Add shape (~24 findings, spot-verified): ShareX.HelpersLib/Forms/ImageViewer.cs:396-399 (pbPreview, lblStatus, lblLeft, lblRight), Colors/ColorPicker.cs:84-85 (colorBox, colorSlider), Forms/InputBox.cs:145-147 (btnOK, btnCancel, txtInputText), Forms/ClipboardViewerForm.designer.cs:107-112, and more.
IContainer-registration shape (~6 findings, spot-verified): ShareX.HelpersLib/Forms/TrayForm.cs:41 (TrayIcon).
Suggested direction (not prescriptive)
Extend the field-disposal scan's release recognition: a Control field reachable via Controls.Add/AddRange (or a ToolStripItem reachable via .Items.Add/AddRange) whose containing control itself reaches a disposed root is released transitively. Separately, a field constructed via new T(components) or explicitly passed to components.Add(...) is released when components.Dispose() runs.
Scope
No analyzer code changed as part of the sweep — this issue tracks the fix as its own unit of work.
Found by: the issue #201 oracle sweep,
docs/notes/oracle-sweep-2026-07-10.md; catalogued asdocs/notes/field-notes-patterns.mdentries 12-13.The patterns
(a)
Controls/ToolStripItemCollectionmembership. A WinFormsControl-derived field is added tothis.Controls(or aToolStrip'sItems) insideInitializeComponent(); the class never calls.Dispose()on the field directly.System.Windows.Forms.Control.Dispose(bool)recursively disposes every child in its ownControlscollection — that's the framework's own designer-generated contract. Same holds for aToolStrip/ContextMenuStrip'sToolStripItemCollection.(b)
IContainer-registered component. A component constructed vianew T(components)(or explicitlycomponents.Add(x)) is registered with the designer'sIContainer;components.Dispose()(the one-liner every WinFormsForm/UserControlalready has) disposes it.Evidence — ShareX/ShareX, ~30 own-only findings from these two shapes
Controls.Addshape (~24 findings, spot-verified):ShareX.HelpersLib/Forms/ImageViewer.cs:396-399(pbPreview,lblStatus,lblLeft,lblRight),Colors/ColorPicker.cs:84-85(colorBox,colorSlider),Forms/InputBox.cs:145-147(btnOK,btnCancel,txtInputText),Forms/ClipboardViewerForm.designer.cs:107-112, and more.IContainer-registration shape (~6 findings, spot-verified):ShareX.HelpersLib/Forms/TrayForm.cs:41(TrayIcon).Suggested direction (not prescriptive)
Extend the field-disposal scan's release recognition: a
Controlfield reachable viaControls.Add/AddRange(or aToolStripItemreachable via.Items.Add/AddRange) whose containing control itself reaches a disposed root is released transitively. Separately, a field constructed vianew T(components)or explicitly passed tocomponents.Add(...)is released whencomponents.Dispose()runs.Scope
No analyzer code changed as part of the sweep — this issue tracks the fix as its own unit of work.