Functions
package:win32 exposes Win32 APIs as
global Dart functions, projected into a safer and more idiomatic Dart
surface.
In most cases, you can call Win32 APIs as if they were normal Dart functions.
Reserved and Optional Parameters​
Some Win32 APIs include parameters that are not always meaningful to the caller.
These fall into two distinct categories.
Reserved Parameters​
Reserved parameters exist only for ABI or historical compatibility. They
must always be set to 0 or nullptr and have no semantic meaning.
In package:win32, reserved parameters are removed entirely from the Dart
signature and supplied automatically behind the scenes.
// Native: CoInitializeEx(NULL, COINIT_MULTITHREADED)
CoInitializeEx(COINIT_MULTITHREADED);
You never see or pass reserved parameters in Dart.
Optional Parameters​
Optional parameters are semantically meaningful but not required for all calls. They may accept real values (such as pointers or integers), but can also be omitted.
In package:win32, optional parameters are projected as
nullable Dart types. Passing null maps to 0 or nullptr on the native
side.
This makes it explicit at the call site which parameters are optional, while still allowing real values to be passed when needed.
Win32 BOOL Becomes Dart bool​
Win32 APIs that use the BOOL type are projected to Dart bool.
final success = MagShowSystemCursor(true);
if (!success) {
// handle error
}
You never work with TRUE, FALSE, or integer return values — only Dart
bool.
HRESULT-Based APIs​
Most COM and system APIs return an HRESULT. In package:win32, you
almost never handle HRESULT values directly.
Failures Throw​
If a Win32 API reports failure via HRESULT, the Dart function throws a
WindowsException.
CoGetApartmentType(pAptType, pAptQualifier); // throws on failure
You can still catch and handle failures explicitly:
try {
CoGetApartmentType(pAptType, pAptQualifier);
} on WindowsException catch (e) {
// handle error
if (e.hr == CO_E_NOTINITIALIZED) {
// specific handling
}
}
Output Values are Returned Directly​
If a function logically produces a value via an out parameter, the Dart projection returns that value directly.
final guid = CoCreateGuid(); // returns Pointer<GUID>
You do not pass output buffers or inspect HRESULTs manually.
Returned pointers and COM objects follow the same ownership rules as their
native counterparts. Be sure to free() or
IUnknown.release() them as appropriate.
APIs That Call SetLastError()​
Many Win32 APIs indicate failure by their return value and record corresponding
error code in the thread-local last-error slot via
SetLastError(). This error code can be queried using
GetLastError().
In Dart, calling GetLastError() directly after a Win32 API invocation is
not reliable. Between the original native call and a Dart-side
GetLastError() invocation, the Dart runtime may perform additional native
operations that overwrite the last-error state.
This is a known limitation of Dart FFI (see dart-lang/sdk#38832) and cannot be reliably mitigated in user code.
To address this, package:win32 adopts a different convention for APIs that
follow this pattern. These APIs return a Win32Result<T>,
which captures both the logical return value and the associated last-error
code atomically on the native side, ensuring the error state is preserved.
final Win32Result(:value, :error) = WriteFile(...);
if (!value) {
print('Error: ${error.toHRESULT()}');
}
When calling APIs that return Win32Result<T>:
- Always inspect
valuefirst to determine success or failure. - Consult
erroronly when the API contract indicates failure.
COM Queries Return Typed Interfaces​
APIs that conceptually query an interface are projected as generic Dart functions that return the requested type directly.
final clsid = FileOpenDialog.toNative();
final dialog = CoCreateInstance<IFileDialog>(
clsid,
null,
CLSCTX_ALL,
); // returns IFileDialog
You don't pass IIDs or output pointers. The returned object is already typed and ready to use.