winget_dart 0.4.0
winget_dart: ^0.4.0 copied to clipboard
Dart FFI bindings for WinGet (Windows Package Manager). Search, install, upgrade, and uninstall packages with async progress on x64 and ARM64.
winget_dart #
Typed Dart API for the Windows Package Manager
via the Microsoft.Management.Deployment COM/WinRT interface.
Supports search, install, upgrade, uninstall, and simulate operations with async progress streams. Targets Windows x64 and ARM64.
Platform support #
| Platform | Search | Install / Upgrade / Uninstall | Simulate |
|---|---|---|---|
| Windows 10 x64 | Yes | Yes | Yes |
| Windows 11 x64 | Yes | Yes | Yes |
| Windows 11 ARM64 | Yes | Yes | Yes |
| macOS / Linux | -- | -- | -- |
Prerequisites #
WinGet ships in-box as part of App Installer on:
- Windows 10 1809+ (x64)
- Windows 11 (x64 and ARM64)
On older machines or Windows Server, install manually from WinGet releases.
Build requirements #
The native bridge DLL (winget_nc.dll) is compiled automatically by
the Dart Build Hook during dart pub get / dart run / dart test.
The following must be installed on the build machine:
- CMake >= 3.21
- Visual Studio 2022 Build Tools with:
- MSVC v143 C++ x64/x86 build tools
- MSVC v143 C++ ARM64 build tools (for ARM64 target)
- Windows SDK 10.0.19041 or later
- vcpkg with
cppwinrt(optional; the Windows SDK built-in headers are used as fallback)
Quick start #
import 'package:winget_dart/winget_dart.dart';
import 'package:winget_dart/src/bridge/native_winget_bridge.dart';
Future<void> main() async {
final client = await WgClient.connect(NativeWingetBridge());
// Search for packages
final tx = client.searchName('cmake');
await for (final pkg in tx.packages) {
print('${pkg.id} ${pkg.version}');
}
// Simulate then install
final plan = await client.simulateInstall('Kitware.CMake');
print('Will install: ${plan.installing.map((p) => p.id).join(', ')}');
final install = client.installPackage('Kitware.CMake');
await for (final p in install.progress) {
print('${p.percent}% ${p.label}');
}
await install.result;
await client.close();
}
API overview #
WgClient #
The primary entry point. Create via WgClient.connect(bridge) where bridge
is a WingetBridge implementation:
NativeWingetBridge()for production (requires the native DLL)FakeWingetBridge()for unit testing (pure Dart, no DLL needed)
| Method | Returns | Description |
|---|---|---|
searchName(query) |
WgTransaction<List<WgPackage>> |
Stream packages matching query |
findById(id) |
Future<WgPackage?> |
Exact ID lookup |
listCatalogs() |
Future<List<WgCatalog>> |
All configured catalogs |
listInstalled() |
WgTransaction<List<WgPackage>> |
All installed packages |
simulateInstall() |
Future<WgInstallPlan> |
Dry-run dependency resolution |
installPackage() |
WgTransaction<void> |
Install with progress events |
upgradePackage() |
WgTransaction<void> |
Upgrade with progress events |
uninstallPackage() |
WgTransaction<void> |
Uninstall with progress events |
getUpdates() |
WgTransaction<List<WgPackage>> |
Packages with available upgrades |
cancel() |
void |
Cancel any in-flight operation |
WgTransaction<T> #
Wraps an async operation with three access points:
packages-- stream ofWgPackageresults (search/list operations)progress-- stream ofWgProgressevents (install/upgrade/uninstall)result-- future that completes when the operation finishes
Simulate-first pattern #
Always call simulateInstall before installPackage to show the user what
will change. The simulate and install are separate WinGet transactions --
the actual install re-resolves dependencies independently.
final plan = await client.simulateInstall('Kitware.CMake');
if (!plan.isEmpty) {
print('Installing: ${plan.installing.map((p) => p.id).join(', ')}');
await client.installPackage('Kitware.CMake').result;
}
Testing #
Unit tests use FakeWingetBridge and run without the native DLL or WinGet:
dart test --exclude-tags=integration
Integration tests require a live Windows environment with WinGet:
dart test --tags=integration
Sharp edges #
-
Elevation detection: The bridge auto-detects process elevation via
GetTokenInformation(TokenElevation)and selects the correct COM factory CLSID. Using the wrong factory causes a hard crash (not an exception). -
Parallel install prohibition: WinGet's COM API does not support concurrent install operations. The bridge enforces a sequential guard; attempting a second install/upgrade/uninstall returns an error immediately.
-
Fresh-login race: On freshly provisioned machines,
WgClient.connect()supports retry with exponential backoff viamaxRetriesandretryDelayparameters. -
COM apartment threading: All WinRT calls run on a dedicated MTA thread.
Dart_PostCObject_DLis safe to call from any thread. -
ARM64: Requires a native ARM64 DLL -- x64 emulation does not apply to COM server activation. Windows 11 build 22000+ required on ARM64.
Architecture #
Dart (WgClient)
| dart:ffi (@Native -- resolved via Build Hook CodeAsset)
v
winget_bridge.h (flat C ABI -- Windows x64 and ARM64)
|
+-- WgManager (COM MTA apartment thread, factory selection)
|
+-- WgTransaction (per-operation context, cancellation)
|
| C++/WinRT coroutines IAsyncOperationWithProgress<>
v
Microsoft.Management.Deployment COM server
(part of App Installer / WinGet)
|
v
WinGet catalogs (winget community repo, msstore, ...)
Flutter example #

Building the native library manually #
# x64
cmake -B build native/ -G "Visual Studio 17 2022" -A x64 -DTARGET_ARCH=x64
cmake --build build --config Release
# ARM64 (cross-compile on x64 host)
cmake -B build-arm64 native/ -G "Visual Studio 17 2022" -A ARM64 -DTARGET_ARCH=arm64
cmake --build build-arm64 --config Release
The Dart Build Hook (hook/build.dart) runs these commands automatically.
License #
Apache-2.0