Skip to main content

Using Win32 structs from Dart

Win32 programming frequently uses C structs to pass data between functions. This topic provides more information on how to create, pass and access struct objects from Dart code.

For example, let's assume you want to call the Win32 API GetSystemPowerStatus, which retrieves the current power status of the system (e.g. battery or AC powered).

The Dart function signature looks like this:

int GetSystemPowerStatus(Pointer<SYSTEM_POWER_STATUS> lpSystemPowerStatus) {}

As the documentation indicates, SYSTEM_POWER_STATUS inherits from the dart:ffi Struct class.

To create such a struct, you can use calloc to allocate memory for it:

final lpSystemPowerStatus = calloc<SYSTEM_POWER_STATUS>();
info

Dart also provides malloc, which corresponds to the equivalent C runtime function that allocates uninitialized memory. In practice, calloc is a good general choice when writing Windows code, since the performance cost of initializing memory is negligible in return for the eradication of potential side-effects.

tip

Some structs include a field (typically the first one) for their size. This is typically because they're used with a function that can accept multiple variants of the same struct (e.g. WNDCLASS and WNDCLASSEX), with the size field being used to disambiguate the struct being passed.

The Dart cascade notation provides a convenient way to initialize classes like this. For example:

final wndClass = calloc<WNDCLASSEX>()..ref.cbSize = sizeOf<WNDCLASSEX>();

Since lpSystemPowerStatus is of type Pointer<SYSTEM_POWER_STATUS>, how do we access the fields of SYSTEM_POWER_STATUS? Struct pointers have a ref property which creates a reference accessing the fields of the struct.

Here's a simple example that shows how to detect the power status of a machine:

final lpSystemPowerStatus = calloc<SYSTEM_POWER_STATUS>();
final hr = GetSystemPowerStatus(lpSystemPowerStatus);

if (SUCCEEDED(hr)) {
if (lpSystemPowerStatus.ref.BatteryFlag >= 128) {
// This value is only less than 128 if a battery is detected.
print('No system battery detected.');
} else {
final batteryRemainingPercent = lpSystemPowerStatus.ref.BatteryLifePercent;
if (batteryRemainingPercent <= 100) {
print('Battery detected with $batteryRemainingPercent% remaining.');
} else {
// Windows sets this value to 255 if it can't detect remaining life.
print('Battery detected but status unknown.');
}
}
}

free(lpSystemPowerStatus);

Here we pass a newly allocated and initialized SYSTEM_POWER_STATUS struct to the GetSystemPowerStatus function, which fills it with values corresponding to the machine's state. We then access fields of the returned struct by dereferencing the pointer.