Skip to content

fix(connectivity_plus): defer Windows COM cleanup to final manager owner - #4009

Open
Adherentman wants to merge 1 commit into
fluttercommunity:mainfrom
Adherentman:codex/fix-connectivity-windows-com-cleanup
Open

Adherentman wants to merge 1 commit into
fluttercommunity:mainfrom
Adherentman:codex/fix-connectivity-windows-com-cleanup

Conversation

@Adherentman

@Adherentman Adherentman commented Oct 23, 2026 •

Copy link
Copy Markdown

Description

The Windows plugin and its event-stream handler share a NetworkManager. The plugin destructor currently calls Cleanup() while the stream can still own the manager. When the last owner releases it, NetworkManager::~NetworkManager() calls StopListen() and Cleanup() again. This performs two CoUninitialize() calls for the manager's single CoInitialize(), prematurely consuming the caller's COM initialization, and releases manager interfaces before the final owner is gone.

Change the plugin destructor to stop the listener immediately and let the final shared owner clean up COM once:

-  manager->Cleanup();
+  manager->StopListen();

StopListen() unregisters the callback and clears the listener state. The manager destructor retains responsibility for COM cleanup. No Dart API, dependency, version, changelog, or other-platform behavior changes.

Native regression and before/after results

The new standalone test in windows/tests/ compiles the actual plugin class and NetworkManager with the Flutter Windows wrapper; COM and the network listener are real, not mocked. It initializes the caller's apartment, creates the shared plugin/stream ownership, destroys the plugin, checks that its listener has stopped while the manager remains usable, then releases the stream's owner and queries the caller's apartment.

On Windows 10 build 19045, MSVC 19.51.36260, Windows SDK 10.0.26100, Flutter 3.47.6, both Release test executables built successfully without compiler warnings/errors. The same test was run against original upstream source (907994b0e52c2c11ae2f2ccc08d0658e1c73dbaf, matching connectivity_plus 7.3.2) and this patch:

Source Listener stopped, manager still usable Caller apartment HRESULT CTest
Original false 0x800401f0 (CO_E_NOTINITIALIZED) Failed, as expected
Patched true 0x0 (S_OK) Passed

Actual output:

Original:
listener_stopped=false caller_apartment_hresult=0x800401f0
0% tests passed, 1 tests failed out of 1

Patched:
listener_stopped=true caller_apartment_hresult=0x0
100% tests passed, 0 tests failed out of 1

windows/tests/README.md contains the exact CMake/CTest commands. The test is independent of production CMake targets and adds no test-framework dependency. Applying the same test to the parent revision reproduces the failure.

Additional validation and field context

  • flutter analyze --no-pub: no issues.
  • flutter test --no-pub in the connectivity_plus package: 16 tests passed.
  • Focused clang-format --dry-run --Werror and git diff --check: passed.
  • A complete downstream Windows release-app copy with only a rebuilt connectivity DLL containing the same one-call correction passed two normal-exit flows: play/switch content/return home/exit, and exit during active playback. Both processes returned OS exit code 0, released video outputs, and produced no Application Error/WER 1000/1001 events. This used the existing published app's Dart bundle, not a fresh app rebuild from this upstream commit.

The investigation started from an exit-time native access violation at a COM Release() in NetworkManager::Cleanup() during Flutter engine destruction. The test above proves the COM ownership defect and its correction; it does not claim to replay every frame of that production access violation.

Upstream CI status (2026-10-23)

The Windows example build and Windows integration test passed on this PR head. The Android integration tests for API 24/29/32/36, Dart analysis, package tests, formatting, and the other completed platform builds also passed. The iOS integration job was still running at this check.

The failing android_example_build job stops while applying the Flutter Gradle plugin: CI installed Flutter 3.47.7 stable, which requires Gradle >= 8.14.0, while the example wrapper pins 8.13. This is a baseline toolchain compatibility failure before application compilation.

The wrapper file is identical in the base revision and this PR (Git blob 3641ebded3581e0db0f4f92917dde8932cf69479). This PR changes only the four Windows source/test files; Android files and workflows are unchanged. The upstream example's Gradle configuration needs a separate compatibility update; repeating the same configuration will not address the version check.

Related Issues

Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I titled the PR using Conventional Commits.
  • I did not modify the CHANGELOG.md nor the plugin version in pubspec.yaml files.
  • All existing and new tests are passing. The scoped package/native tests above pass; the full upstream CI/integration matrix is pending.
  • The analyzer (flutter analyze) does not report any problems on my PR.

Breaking Change

  • Yes, this is a breaking change.
  • No, this is not a breaking change.

Stop the listener when the plugin is destroyed, but leave COM cleanup to
the final shared NetworkManager owner. The event-stream handler can outlive
the plugin, and both destructors previously called Cleanup for one Init.

Add a standalone native regression using the real plugin and COM apartment.
On Windows 10 build 19045, the original source fails with listener_stopped
false and CO_E_NOTINITIALIZED (0x800401f0); the patched source passes with
listener_stopped true and S_OK. Both Release executables build successfully.

Validation: Windows native before/after regression, 16 package Flutter tests,
flutter analyze --no-pub, focused clang-format and git diff --check passed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant