When interacting with the Windows API (Win32), you will inevitably face two facts: libraries are named things like user32.dll, and text is almost always expected to be UTF-16 (Wide Characters). In XS, bridging this gap involved tedious calls to MultiByteToWideChar and manual buffer management.
Affix handles the translation for you.
The Recipe
This recipe displays a native Windows message box containing Unicode characters (an emoji). It looks like this:
Okay, here's the sauce. ...the source:
use v5.40;
use utf8; # Required for literal emojis in source
use Affix;
# Define some standard Windows constants
use constant MB_OK => 0x00000000;
use constant MB_DEFAULT_DESKTOP_ONLY => 0x00020000;
# Bind the function
# Library: user32.dll
# Symbol: MessageBoxW (The Unicode version)
# Alias: MessageBox (What we call it in Perl)
affix 'user32', [ MessageBoxW => 'MessageBox' ] =>
[ Pointer [Void], WString, WString, UInt ] => Int;
# Call it
# undef becomes NULL (No owner window)
MessageBox( undef, 'Keep your stick on the ice.', '🏒', MB_OK | MB_DEFAULT_DESKTOP_ONLY );
How It Works
-
1. The A vs. W Suffix
Most Windows functions come in two flavors: ANSI (ending in
A) and Wide (ending inW).MessageBoxAexpects system-codepage strings (like ASCII), whileMessageBoxWexpects UTF-16.Since modern Perl is Unicode-aware, we almost always want the W version.
-
2. Aliasing
We bind the symbol
MessageBoxW, but that is a clumsy name to type in Perl. By passing an array reference[ 'MessageBoxW' => 'MessageBox' ], we tell Affix to look up the real symbol in the DLL but install it into our namespace asMessageBox. -
3. The WString Type
This is the magic ingredient.
[ ..., WString, WString, ... ]When you pass a Perl string to a
WStringargument, Affix performs the following steps on the fly:- Check the internal encoding of the Perl scalar.
- Transcode the string into UTF-16LE (Little Endian).
- Append a double-null terminator (
\0\0). - Pass the pointer to the C function.
This allows us to pass the hockey stick emoji
'🏒'directly. -
4. The Null Pointer
The first argument to
MessageBoxis a handle to an owner window (HWND). Since we don't have a parent window, we passNULL.In Affix,
NULLis represented by Perl'sundef. When passed to aPointertype,undefis automatically converted to address0x0.
Kitchen Reminders
-
Input Only
The
WStringtype is designed for input strings (LPCWSTRorconst wchar_t*). Affix creates a temporary buffer for the duration of the call. If the C function intends to modify the string buffer, you must usePointer[WChar]and manage the memory yourself usingcalloc. -
use utf8If you are typing special characters like '🏒' directly into your Perl script, always remember
use utf8;at the top of your file, or Perl might interpret the bytes incorrectly before Affix even sees them.