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>();
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.
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:
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.