Skip to main content

Structs

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

Creating Structs

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 dart:ffi's 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, as the performance cost of initializing memory is negligible compared to the benefits of avoiding potential side effects.

TIP

Some structs include a field (typically the first one) for their size. This is usually 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 structs like this:

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

Accessing Struct Fields

To access the fields of SYSTEM_POWER_STATUS using lpSystemPowerStatus, which is of type Pointer<SYSTEM_POWER_STATUS>, you can use the ref property. This property creates a reference to the struct and provides access to its fields directly.

Here's a simple example that demonstrates how to detect the power status of the system:

power_status.dart
import 'dart:ffi';

import 'package:ffi/ffi.dart';
import 'package:win32/win32.dart';

void main() {
final lpSystemPowerStatus = calloc<SYSTEM_POWER_STATUS>();

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

For a more detailed demonstration of retrieving system information, see the sysinfo.dart example.