

Calling Windows APIs in Dart with win32
Introduction​
As Dart expands its reach beyond web and mobile development, interacting with native Windows APIs unlocks a world of possibilities.
package:win32 serves as a bridge to the powerful features of
the Windows operating system, allowing Dart developers to harness these
capabilities directly in their applications.
In this blog post, we'll explore how to use package:win32 to call Windows APIs
in Dart, enabling you to create powerful Windows applications with ease.
Here's what we'll cover:
Getting Started​
Let's dive into how you can get started with package:win32 package and see it
in action.
Add the ffi and win32 packages to your project with:
dart pub add ffi win32
Interacting with Windows APIs​
Let's explore how you can interact with the Windows APIs using package:win32.
Displaying a Message Box​
Let's start with a simple example that displays a message box using the
MessageBox function from the Windows API.
import 'package:ffi/ffi.dart';
import 'package:win32/win32.dart';
void main() {
using((arena) {
final Win32Result(:value, :error) = MessageBox(
null,
arena.pcwstr('Hello from Dart'),
arena.pcwstr('Dart Win32 MessageBox Demo'),
MB_OK | MB_ICONINFORMATION,
);
switch (value) {
case IDOK:
print('OK pressed');
case 0:
throw WindowsException(error.toHRESULT());
}
});
}
Retrieving the System Memory​
Next, let's retrieve the total amount of physical memory installed on the
system using the
GetPhysicallyInstalledSystemMemory
function.
String getSystemMemorySize() {
return using((arena) {
final lpMemorySize = arena<ULONGLONG>();
final Win32Result(:value, :error) = GetPhysicallyInstalledSystemMemory(
lpMemorySize,
);
if (!value) throw WindowsException(error.toHRESULT());
return lpMemorySize.value ~/ 1024;
});
}
Creating a Classic Win32 Window​
Finally, let's create a classic Win32 window. First, we'll define the window procedure that will handle messages sent to the window.
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:win32/win32.dart';
int mainWindowProc(Pointer hWnd, int uMsg, int wParam, int lParam) {
final hwnd = HWND(hWnd);
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
final ps = adaptiveCalloc<PAINTSTRUCT>();
final hdc = BeginPaint(hwnd, ps);
final rect = adaptiveCalloc<RECT>();
final msg = 'Hello, Dart!'.toPcwstr();
GetClientRect(hwnd, rect);
DrawText(hdc, msg, -1, rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
EndPaint(hwnd, ps);
free(msg);
free(rect);
free(ps);
return 0;
}
return DefWindowProc(hwnd, uMsg, WPARAM(wParam), LPARAM(lParam));
}
Next, we'll define the winMain entry point, which creates the window and runs
the message loop.
// ...
void winMain(HINSTANCE hInstance, List<String> args, SHOW_WINDOW_CMD nShowCmd) {
using((arena) {
// Register the window class.
final className = arena.pcwstr('Sample Window Class');
final lpfnWndProc = NativeCallable<WNDPROC>.isolateLocal(
mainWindowProc,
exceptionalReturn: 0,
);
final wc = arena<WNDCLASS>();
wc.ref
..style = CS_HREDRAW | CS_VREDRAW
..lpfnWndProc = lpfnWndProc.nativeFunction
..hInstance = hInstance
..lpszClassName = PWSTR(className)
..hCursor = LoadCursor(null, IDC_ARROW).value
..hbrBackground = HBRUSH(GetStockObject(WHITE_BRUSH));
RegisterClass(wc);
// Create the window.
final windowCaption = arena.pcwstr('Dart Native Win32 window');
final Win32Result(value: hWnd, :error) = CreateWindowEx(
WS_EX_LEFT, // Optional window styles.
className, // Window class
windowCaption, // Window caption
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
null, // Parent window
null, // Menu
hInstance, // Instance handle
null, // Additional application data
);
if (hWnd.isNull) throw WindowsException(error.toHRESULT());
ShowWindow(hWnd, nShowCmd);
UpdateWindow(hWnd);
// Run the message loop.
final msg = arena<MSG>();
while (GetMessage(msg, null, 0, 0).value) {
TranslateMessage(msg);
DispatchMessage(msg);
}
lpfnWndProc.close();
});
}
Finally, we'll define the main() function to initialize the application and
call the winMain() function. The package:win32 provides the
initApp helper function, which sets up the WinMain function with
all the necessary information, including the entry point and command line
arguments, simplifying the process for you.
// ...
void main() => initApp(winMain);
Conclusion​
package:win32 is a powerful tool for Dart developers aiming to leverage the
full capabilities of the Windows operating system. By bridging the gap between
Dart and the extensive Windows APIs, package:win32 enables you to create
feature-rich applications with ease.
Whether you're displaying simple message boxes, querying system information, or
creating complex graphical interfaces, package:win32 unlocks new possibilities
for your Dart projects. Start exploring today and discover what you can build
with Dart and Windows APIs!
For more detailed information, check out our documentation.