[issue29131] Calling printf from the cdll does not print the full string

Eryk Sun report at bugs.python.org
Mon Jan 2 20:35:50 EST 2017


Eryk Sun added the comment:

> from ctypes import *
> msvcrt = cdll.msvcrt
> message_string = "Hello World!\n"
> msvcrt.printf("Testing: %s", message_string)

Avoid using libraries as attributes of cdll and windll. They get cached, and in turn they cache function pointers. Thus all modules contend for cdll and windll function prototype definitions (i.e. argtypes, restype, errcheck), and the last one to modify the definition wins. Even if you're not setting prototypes (in which case you must really like segfaults, data corruption, and difficult-to-diagnose errors), your code will still be at the mercy of whichever module does set them. Instead use the following:

    msvcrt = ctypes.CDLL('msvcrt', use_errno=True)
    msvcrt.printf.argtypes = (ctypes.c_char_p,)

CDLL uses the cdecl calling convention. This convention uses caller stack cleanup, so we allow passing a variable number of arguments even though the above argtypes definition only checks the first argument.

Examples

You'll get an exception if the first argument isn't bytes:

    >>> msvcrt.printf("spam and %d %s\n", 42, "eggs")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type

Since we're not checking additional arguments, you might accidentally pass a wide-character string. In that case of course the output will be wrong:

    >>> msvcrt.printf(b"spam and %d %s\n", 42, "eggs")
    spam and 42 e
    14

Here we get it right:

    >>> msvcrt.printf(b"spam and %d %s\n", 42, b"eggs")
    spam and 42 eggs
    17

Or maybe you need to print a wide-character string, i.e. "%ls" or "%S":

    >>> msvcrt.printf(b"spam and %d %ls\n", 42, "eggs")
    spam and 42 eggs
    17

or

    >>> msvcrt.printf(b"spam and %d %S\n", 42, "eggs")
    spam and 42 eggs
    17

The "%s" and "%S" convention in MSVC predates standard C. In Microsoft's printf, "%s" expects a char pointer and "%S" expects a wchar_t pointer, but in their wprintf it's the other way around. In standard C "%s" and "%ls" are always a char string and a wchar_t string, respectively. MSVC also supports the standard "%ls" for wide-character strings, but its non-standard use of "%s" requires the introduction of a non-standard "%hs" for char strings.

----------

_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue29131>
_______________________________________


More information about the Python-bugs-list mailing list