Guess this is a bug in the documentation .; -)
In Python 3, all strings are Unicode by default, but the example calls the ANSI MessageBoxA function, not the MessageBoxW version of Unicode. See 16.17.1.2. Access to functions from loaded ctypes in ctypes documentation.
So, for MessageBoxA in this example, you can make it work by encoding the arguments of the input lines of the function to what is required by calling locale.getpreferredencoding() :
from ctypes import c_int, WINFUNCTYPE, windll from ctypes.wintypes import HWND, LPCSTR, UINT import locale preferred_encoding = locale.getpreferredencoding(False) prototype = WINFUNCTYPE(c_int, HWND, LPCSTR, LPCSTR, UINT) paramflags = ((1, "hwnd", 0), (1, "text", "Hi".encode(preferred_encoding)), (1, "caption", None), (1, "flags", 0)) MessageBox = prototype(("MessageBoxA", windll.user32), paramflags) MessageBox() MessageBox(text="Spam, spam, spam".encode(preferred_encoding)) MessageBox(flags=2, text="foo bar".encode(preferred_encoding))
It would be much more inconvenient to use the MessageBoxW function of Windows, which supports Unicode "wide" string arguments ( LPCWSTR instead of LPCSTR ), which makes them explicitly encoded on almost every call unnecessary. In addition, I would replace most of the "magic numbers" in the example with named constants:
from ctypes import c_int, WINFUNCTYPE, windll from ctypes.wintypes import HWND, LPCWSTR, UINT import win32con # contains Win32 constants pulled from the C header files INPUT_PARM, OUTPUT_PARAM, INPUT_PARM_DEFAULT_ZERO = 1, 2, 4 prototype = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT) paramflags = ((INPUT_PARM, "hwnd", 0), (INPUT_PARM, "text", "Hi"), (INPUT_PARM, "caption", None), (INPUT_PARM, "flags", win32con.MB_HELP)) MessageBox = prototype(("MessageBoxW", windll.user32), paramflags) MessageBox() MessageBox(text="Spam, spam, spam") MessageBox(flags=win32con.MB_ABORTRETRYIGNORE, text="foo bar")