Fix mstscax control instance and TS property-set reference leaks - #193
Merged
Marc-André Moreau (mamoreau-devolutions) merged 1 commit intoSep 24, 2026
Merged
Marc-André Moreau (mamoreau-devolutions) merged 1 commit into
Marc-André Moreau (mamoreau-devolutions) merged 1 commit into
Conversation
- CClassFactory::CreateInstance held onto the reference IClassFactory:: CreateInstance returns for the real mstscax control after wrapping it in CMsRdpClient, which already takes its own AddRef in its constructor. The factory never released its copy, so the real control's refcount never reached zero when the wrapper was destroyed. This is the primary reported leak: mstscax!CSND::SND_Main and mstscax!CRCV::RCVMain threads (and ~100 handles) parked forever per closed embedded RDP session. - CMsRdpClient destructor released its wrapper objects (m_pMsRdpExtendedSettings, m_pMsRdpExInstance) after releasing the underlying control interfaces, even though those wrappers hold references into control-internal objects owned by the control. Reordered to release wrappers first. - CMsRdpPropertySet stored the internal ITSPropertySet/TS core-props IUnknown it was given without taking its own reference, but its destructor unconditionally released it anyway, decrementing a refcount it never incremented. This could prematurely free control-internal property-set objects still in use elsewhere, producing heap corruption during teardown. Added the missing AddRef to balance the destructor Release. Verified: full x64 build, ctest 31/31 passing. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot started reviewing on behalf of
Marc-André Moreau (mamoreau-devolutions)
September 24, 2026 18:39
View session
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The changes correctly balance COM ownership and preserve dependent-object lifetimes during teardown.
Review effort: Balanced
Findings: None
What changed in this PR
Fixes COM reference-count imbalances that prevented embedded RDP controls and internal property sets from being released correctly.
Changes:
- Balances
CMsRdpPropertySetownership withAddRef. - Releases wrapper objects before control interfaces.
- Releases the factory-owned control reference after wrapping.
| File | Description |
|---|---|
dll/RdpSettings.cpp |
Balances property-set reference ownership. |
dll/MsRdpClient.cpp |
Corrects control teardown order and factory reference release. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Marc-André Moreau (mamoreau-devolutions)
merged commit Sep 24, 2026
a7aa301
into
master
1 check passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This closes the primary root cause of RDMW-24263 / the forum report ("Embedded RDP sessions leak the ActiveX control after the tab is closed"): the wrapped
mstscaxcontrol instance itself was never released, so every closed embedded session permanently leaked 2 native threads (mstscax!CSND::SND_Main,mstscax!CRCV::RCVMain) and ~100 handles.It also fixes a refcount-imbalance bug that is a strong suspect for the intermittent native heap-corruption crash (
ntdll.dll 0xC0000374) observed during teardown/forced-GC testing on the RDM side.Bugs fixed
CClassFactory::CreateInstanceleaked the real control's reference (primary leak).IClassFactory::CreateInstancereturns the realmstscaxcontrol object with refcount 1, owned by the caller.CMsRdpClient's constructor takes its ownAddRef()on that pointer, but the factory never released its copy after wrapping — so the real control's refcount never reached 0 when theCMsRdpClientwrapper was later destroyed. Nothing ever drove the control's core shutdown, matching the reported symptom exactly (send/receive threads parked forever inCTSThread::ThreadMsgLoop).CMsRdpClientdestructor released its wrapper objects after the control interfaces.m_pMsRdpExtendedSettingsandm_pMsRdpExInstancehold references into control-internal objects whose lifetime is owned by the control. Reordered so wrappers are released first, before the control interfaces.CMsRdpPropertySetreleased a reference it never took.It stored the internal
ITSPropertySetpointer (for core/base/transport TS property sets) without anAddRef, but its destructor unconditionally calledRelease()on it anyway — decrementing a refcount it never incremented, which could prematurely free a control-internal object still referenced elsewhere. Added the missingAddRef()in the constructor to balance the destructor'sRelease().Verification
ctest— 31/31 passing, including all existing DVC factory / plugin-reference / detached-instance regression tests from Fix ownership of WTS plugin references #189–Fix DVC plugin class factory lifetime after session removal #192.How to verify the primary leak fix manually
Open and close N embedded RDP sessions, take a full-memory dump, count threads whose stack contains
mstscax!CSND::SND_Main. Before this fix: N threads remain. After: 0.Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com