Skip to main content
Calling Windows APIs in Dart with win32
4 min read
Software Engineer / Maintainer of win32

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:

Terminal
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());
}
});
}

MessageBox

Retrieving the System Memory​

Next, let's retrieve the total amount of physical memory installed on the system using the GetPhysicallyInstalledSystemMemory function.

sysinfo.dart
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;
});
}

System Memory

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.

hello.dart
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.

hello.dart
// ...

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.

hello.dart
// ...

void main() => initApp(winMain);

Dart Native Win32 Window

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.