Repository navigation
Fix IEnumerator adapter initialization for COM RCWs - #136392
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ee70b0a5-4f16-4838-9d10-fe9924d6864c
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @dotnet/interop-contrib |
|
@jkoritzinsky I will also be taking this into .NET 11 and .NET 10 servicing. |
There was a problem hiding this comment.
🟢 Approval recommended
The focused change removes the initialization dependency on interface resolution, covers all three entry points, and has no unresolved review findings.
No open issues 🎉
What changed in this PR
Fixes COM runtime-callable wrapper (RCW) enumeration by initializing adapters lazily at each enumerator entry point, rather than relying on interface resolution that cached dispatch can bypass.
Changes:
- Reuses the existing per-RCW adapter cache for
MoveNext,Reset, andCurrent. - Adds regression coverage for fresh RCWs, each initial entry point, empty/nonempty collections, and reset after exhaustion.
| File | Description |
|---|---|
| src/tests/Interop/COM/Dynamic/CollectionTest.cs | Adds coverage for adapter initialization and enumeration state. |
| src/coreclr/System.Private.CoreLib/src/System/__ComObject.cs | Moves adapter initialization from interface resolution to enumerator calls. |
Review effort: Balanced
|
/ba-g HTTP failures |
|
/backport to release/10.0 |
|
Started backporting to |
|
/backport to release/11.0 |
|
Started backporting to |
…136429) Backport of #136392 to release/11.0 /cc @AaronRobinsonMSFT ## Customer Impact - [x] Customer reported - [ ] Found internally Customer-reported in #136118 on .NET 10.0.9 / Windows 11 Arm64 and reproduced locally on Windows x64. Every fresh native IEnumVARIANT RCW cast to IEnumerator should enumerate successfully. Instead, cached interface dispatch can bypass per-RCW adapter initialization after the first instance, causing NullReferenceException when using subsequent enumerators. This breaks COM collection enumeration, including Windows Firewall rules, for applications ported from .NET Framework 4.8. ## Regression - [x] Yes - [ ] No Introduced by #105965 (commit 0d8e565, merged August 17, 2024), which moved RCW enumeration to IDynamicInterfaceCastable and initialized per-instance adapter state during interface resolution. That resolution can be cached without initializing later RCWs. The customer reports this worked on .NET Framework 4.8. ## Testing The new regression test reproduced the NullReferenceException without the fix and passed with it. It covers three fresh native RCWs per case, MoveNext/Reset/Current as the first member called, empty and nonempty collections, and reset after exhaustion. The existing COM Dynamic collection test did not repeatedly cast fresh native IEnumVARIANT RCWs at the same call sites. Original main-branch validation on Windows x64 with Checked CoreCLR: - `.\build.cmd clr.corelib -arch x64 -c Release -rc Checked` - passed. - `.\src\tests\build.cmd x64 Checked -Test Interop\COM\Dynamic\Dynamic.csproj -SkipNative -SkipGenerateLayout` - passed using existing native test assets. - With `CORE_ROOT` set to `artifacts\tests\coreclr\windows.x64.Checked\Tests\Core_Root`, `.\artifacts\tests\coreclr\windows.x64.Checked\Interop\COM\Dynamic\Dynamic\Dynamic.cmd` - passed normally and with `DOTNET_TieredCompilation=0`. No additional local builds or tests were run for this backport; the local validation above was performed for the original main-branch fix. ## Risk Low. The change is limited to the Windows built-in COM IEnumerator adapter path. It lazily obtains each RCW's adapter through the existing marshaler cache instead of depending on interface-resolution side effects. There are no public API or native COM server changes. Regression coverage exercises fresh RCWs and all three enumerator members, and the existing COM Dynamic test program passed. **IMPORTANT**: If this backport is for a servicing release, please verify that: - For .NET 8 and .NET 9: The PR target branch is `release/X.0-staging`, not `release/X.0`. - For .NET 10+: The PR target branch is `release/X.0` (no `-staging` suffix). ## Package authoring no longer needed in .NET 9 **IMPORTANT**: Starting with .NET 9, you no longer need to edit a NuGet package's csproj to enable building and bump the version. Keep in mind that we still need package authoring in .NET 8 and older versions. > [!NOTE] > The servicing details were prepared with GitHub Copilot. Co-authored-by: Aaron R Robinson <arobins@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ee70b0a5-4f16-4838-9d10-fe9924d6864c
Initialize COM enumerator adapters lazily per RCW instead of during interface resolution, which cached dispatch can bypass. Reuse the existing adapter cache.
Add regression coverage for repeated fresh RCWs, all three enumerator entry points, empty/nonempty collections, and reset after exhaustion. Confirmed the test fails with
NullReferenceExceptionbefore the fix.Fixes #136118