Skip to content

Release managed WTS plugin reference when registration fails - #190

Merged
Marc-André Moreau (mamoreau-devolutions) merged 1 commit into
masterfrom
copilot/fix-plugin-registration-failure
Sep 24, 2026
Merged

Marc-André Moreau (mamoreau-devolutions) merged 1 commit into
masterfrom
copilot/fix-plugin-registration-failure

Conversation

@mamoreau-devolutions

Copy link
Copy Markdown
Contributor

Summary

  • Keep the managed IUnknown reference owned until SetWTSPluginObject returns successfully; release it in finally if the COM call throws.
  • Preserve the native ownership-transfer ABI and success path from Fix ownership of WTS plugin references #189 without changing the native setter.
  • Add headless managed tests that verify exception cleanup and successful ownership transfer via a fake IMsRdpExInstance.

Validation

  • dotnet build dotnet\Devolutions.MsRdpEx\Devolutions.MsRdpEx.csproj --framework net48 --no-restore --verbosity quiet -p:WarningLevel=0
  • dotnet build dotnet\Devolutions.MsRdpEx\Devolutions.MsRdpEx.csproj --framework net8.0-windows --no-restore --verbosity quiet -p:WarningLevel=0
  • dotnet test dotnet\MsRdpEx_Test\MsRdpEx_Test.csproj --no-build --filter "FullyQualifiedName~MsRdpEx.Tests.RdpInstancePluginTests" --verbosity quiet — 2 passed, 0 failed.

No native COM registration or RDP client is required by the tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings September 24, 2026 10:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The ownership logic matches the native contract, and focused tests cover both transfer outcomes.

Review effort: Balanced
Findings: None

What changed in this PR

Ensures managed WTS plugin references are released when COM registration fails while preserving native ownership transfer on success.

Changes:

  • Adds exception-safe IUnknown cleanup.
  • Adds headless tests for failure and success paths.
File Description
dotnet/​Devolutions.MsRdpEx/​RdpInstance.cs Releases ownership unless registration succeeds.
dotnet/​MsRdpEx_Test/​RdpInstancePluginTests.cs Verifies reference-count behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@mamoreau-devolutions
Marc-André Moreau (mamoreau-devolutions) merged commit 0ab7dd2 into master Sep 24, 2026
1 check passed
Marc-André Moreau (mamoreau-devolutions) added a commit that referenced this pull request Sep 24, 2026
## Summary
- Capture the session GUID rather than a borrowed native instance
pointer in the DVC class factory; a removed session returns
`REGDB_E_CLASSNOTREG`.
- Pin registered plugins with an internal reference holder, so plugin
COM `AddRef`/`Release` and `QueryInterface` run **outside** the instance
and registry locks. The borrowed getter and success-only
ownership-transfer setter ABI—including same-pointer replacement—remain
unchanged. If holder allocation fails, the caller retains its incoming
reference, matching #190.
- Synchronize the production `DllGetClassObject` session lookup and
factory plugin acquisition with manager teardown. Keep manager
destruction and plugin releases outside the global lock.
- **Detach and close the instance's plugin slot before final destructor
release.** Reentrant plugin callbacks observe an empty getter; setters
reject registration with `E_UNEXPECTED` and preserve caller ownership.
This closes the previously identified destructor reentrancy
use-after-free/double-release path.
- Test removal, replacement during `QueryInterface`, reentrant
`AddRef`/`Release`, destructor reentrancy, manager shutdown during an
in-flight factory call, deterministic allocation failure (including
same-pointer retry), and failed plugin `QueryInterface` without
connecting RDP.

## Validation
- Native x64 CMake/Ninja build (DLL, launchers, and tests): passed
against current `master` after integrating #190 and #191.
- `ctest --test-dir build-dvc-x64 --output-on-failure`: **31/31
passed**, including `logging.dvc-factory.destructor-reentrant` and the
detached-instance regression from #191.

A plugin callback using a raw instance pointer after the instance's
final COM `Release` is outside the normal COM lifetime contract; this
regression specifically guards synchronous reentry during native
teardown. Local build used an **ignored, untracked**
`build-dvc-x64/stubs/atlbase.h` compatibility shim because this VS2026
installation lacks ATL. Nothing from the shim, package versions, or
workflows is included. Validation with the full ATL toolchain or a live
RDP connection remains outstanding. No change to the original mstscax
`pUnknown` release behavior.

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants