Skip to content

Platform prerequisites

Melih Ercan edited this page Sep 21, 2026 · 4 revisions

Platform prerequisites

Everything each platform needs beyond dotnet add package.

Three of these fail silently — no exception, no log line, or an error that does not mention WebRTCme. They are first, because they account for most of the time people lose here.

Platform Symptom if you miss it
1 Windows The app hangs before Main. No error, nothing in any log
2 MAUI, all four error NU1605 naming Microsoft.Maui.Controls, not WebRTCme
3 Blazor Nothing works. No message

1. Windows: the Windows App Runtime

Not a defect, and not fixable in the package — a prerequisite that has to be stated.

WebRTCme is a MAUI library on Windows by design, so its package depends on Microsoft.Maui.Controls, which brings the Windows App SDK, which injects a bootstrapper into the module constructor of anything that references the package.

That bootstrapper looks for a matching Windows App Runtime framework package. When it cannot find one it does not fail — it blocks:

[Native Frames]
Microsoft.WindowsAppRuntime.Bootstrap.Net!...Bootstrap.TryInitialize(...)
...BootstrapCS.AutoInitialize.AccessWindowsAppSDK()
...WindowsAppRuntime.Common.AutoInitialize.InitializeWindowsAppSDK()
<Module>..cctor()

Your app stops before Main, having written nothing. A missing library would have thrown by name — this does not throw at all, which is what makes it so hard to recognise.

Who has the runtime and who does not

A development machine Yes. Visual Studio's MAUI workload installs it — which is why you will not see this locally
A clean Windows machine No. It is not part of Windows
A packaged (MSIX) app Yes. The dependency is declared and the installer or Store supplies it
An unpackaged app — what plain dotnet publish produces No

The fix

Carry the runtime inside your app:

<PropertyGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">
  <WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
</PropertyGroup>

That is the recommendation for unpackaged apps: it costs some size and nothing else, and it cannot be forgotten at deployment time.

The alternative is to ship WindowsAppRuntimeInstall.exe alongside and require users to run it, or to declare it as a documented prerequisite. Both work; both rely on somebody remembering.

Packaged apps need none of this.

How this was found, because the path is worth knowing if you ever meet a silent hang. The device test suite ran in two seconds on real Windows and hung on a CI runner for six hours, until the job limit stopped it. Six theories were wrong before a stack dump settled it. What worked was a module initializer writing to stderr — xUnit captures stdout and replays it only when a test ends, so a test that never ends prints nothing — and then dotnet-stack report on the live process.


2. MAUI: pin the MAUI package versions

Your restore fails without this, and the error does not mention WebRTCme:

error NU1605: Detected package downgrade: Microsoft.Maui.Controls from 10.0.101 to 10.0.20
  YourApp -> WebRTCme 26.9.21 -> Microsoft.Maui.Controls (>= 10.0.101)
  YourApp -> Microsoft.Maui.Controls (>= 10.0.20)

The packages depend on Microsoft.Maui.Controls 10.0.101. Your MAUI workload's own implicit reference is whatever that workload bundles — 10.0.20 for a current SDK — and NuGet calls the difference a downgrade and refuses the restore.

Name the versions yourself, exactly as NU1605 instructs:

<ItemGroup Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) != ''">
  <PackageReference Include="Microsoft.Maui.Controls" Version="10.0.101" />
  <PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="10.0.101" />
</ItemGroup>

It is not optional and it is not discoverable until the restore fails. Keep it in step when you upgrade WebRTCmeReleases states the version each release wants, and a pin left behind produces the same NU1605 in the opposite direction.

This applies to MAUI target frameworks only. A Blazor WebAssembly app never sees it.


3. Blazor: the interop script

The Blazor binding is JSInterop over the browser's own WebRTC, and the JavaScript half ships as a static web asset. Without the tag, nothing works and nothing says why.

In wwwroot/index.html, before _framework/blazor.webassembly.js:

<script src="_content/WebRTCme/JsInterop.js"></script>

The path changed in this release. It used to be _content/WebRTCme.Bindings.Blazor/JsInterop.js, and that package no longer exists. See Releases.

Blazor WebAssembly only. Blazor Server and Blazor Hybrid are not supported — see What works where.


Build tooling

.NET SDK 10.0.400 or later. dotnet --version
MAUI workload dotnet workload install maui — for MAUI apps only

A Blazor WebAssembly app needs neither the MAUI workload nor anything else on this page beyond the script tag.

Per-platform floors

Platform Target framework Minimum OS
Blazor WebAssembly net10.0 any current browser
Android net10.0-android API 28 (Android 9)
iOS net10.0-ios iOS 15.0
Mac Catalyst net10.0-maccatalyst macOS 12 (Catalyst 15.0)
Windows net10.0-windows10.0.22621.0 Windows 10 22H2 (build 22621)

Asking for less than a floor gets you an error telling you to raise it.

Runtime permissions

Not needed for Hello world, which uses a data channel and touches no hardware. Needed the moment you call GetUserMedia or GetDisplayMedia.

Android

Platforms/Android/AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

Screen sharing needs more. Android requires a foreground service of type mediaProjection, and from Android 13 a runtime POST_NOTIFICATIONS permission, because a foreground service must post a notification. The library provides the consent activity and the service itself — the consent flow belongs to the library rather than your activity, so you do not have to override OnActivityResult and forward the result.

iOS and Mac Catalyst

Platforms/iOS/Info.plist and Platforms/MacCatalyst/Info.plist:

<key>NSCameraUsageDescription</key>
<string>Allow video camera access</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allow microphone access</string>
<key>NSLocalNetworkUsageDescription</key>
<string>Local network access is required to reach the signalling server and connect calls.</string>

NSLocalNetworkUsageDescription matters more than it looks: without it, a call to a signalling server on the same LAN fails in a way that does not obviously point at permissions.

Mac Catalyst wants a real signing identity, not ad-hoc. macOS ties camera, microphone and Screen Recording grants to a stable code identity, and an ad-hoc signature with no team gives it nothing to hold on to — a grant can evaporate on relaunch and every rebuild invalidates it. That looks like three unrelated bugs: camera permission re-prompting after each build, Screen Recording refusing after being granted, and the app appearing to hang while a permission call never returns. One cause.

Windows and Blazor

Windows needs nothing declared. Blazor gets the browser's own permission prompt, and requires a secure context — HTTPS, or localhost. getUserMedia does not exist on a plain-HTTP page served from anything else.

Deploying to a device

Android Android SDK and a JDK. The JDK must be recent enough to read libwebrtc.aar
iOS A Mac, Xcode, and a signing identity
Mac Catalyst A Mac
Windows Nothing extra
Blazor Nothing extra

Building is not deploying. Every slice — including iOS and Mac Catalyst — builds with plain dotnet build on Windows. Only linking and deploying an app for those two needs a Mac.

If it still will not start

Troubleshooting maps symptoms to causes, including all three of the silent failures above.

Clone this wiki locally