pythonpython-3.xctypes

ctypes.ArgumentError passing a const char * from Python 3 to C function in Windows DLL


I have a function in a Windows DLL:

extern "C" {
   bool __declspec(dllexport) PublicOpenFile(const char * pFilePath)
   {
      return SomeOtherFunc(pFilePath);
   }
}

which I am trying to invoke from a Python 3.12.9 script as follows:

import ctypes
lib = ctypes.cdll.LoadLibrary('The.dll')
funcOpenFile = lib.PublicOpenFile
funcOpenFile.argtypes = [ctypes.c_char_p]
funcOpenFile.restype = ctypes.c_bool
fileName = "TheOutput.txt"
result = funcOpenFile(fileName)
print(result)

and am getting:

Traceback (most recent call last):
  File "C:\Python_Cpp.py", line 9, in <module>
    result = funcOpenFile(fileName)
             ^^^^^^^^^^^^^^^^^^^^^^
ctypes.ArgumentError: argument 1: TypeError: wrong type

What am I doing wrong?

Many thanks


Solution

  • c_char_p corresponds to a bytes object. "TheOutput.txt" is a str object and would be used for the C wchar_t* type (ctypes c_wchar_p).

    Use a bytes object with c_char_p, e.g. b"TheOutput.txt".

    Note that on Windows, C functions that use char* for file paths expect them to be ANSI-encoded. For your all-ASCII filename using a literal bytes works, but for a filename like 'pingüino.jpg' the ü isn't supported in byte strings. Use a str and encode it to bytes with 'pingüino.jpg'.encode('ansi') in that case.

    Example using C FILE *fopen(const char *filename, const char *mode) and Python 3.13:

    >>> import ctypes as ct
    >>> clib = ct.CDLL('msvcrt')
    >>> clib.fopen.argtypes = ct.c_char_p, ct.c_char_p
    >>> clib.fopen.restype = ct.c_void_p
    >>> clib.fopen(b'pingüino.jpg', b'r')
      File "<python-input-11>", line 1
        clib.fopen(b'pingüino.jpg', b'r')
                   ^^^^^^^^^^^^^^^
    SyntaxError: bytes can only contain ASCII literal characters
    >>> clib.fopen('pingüino.jpg'.encode('ansi'), b'r')
    140704303274784
    

    If you can change the code, using C wchar_t* and ctypes c_wchar_p enables using any supported Unicode character in a filename and str can be passed.

    Example using C FILE *_wfopen(const wchar_t *filename, const wchar_t *mode):

    >>> import ctypes as ct
    >>> clib = ct.CDLL('msvcrt')
    >>> clib._wfopen.argtypes = ct.c_wchar_p, ct.c_wchar_p
    >>> clib._wfopen.restype = ct.c_void_p
    >>> clib._wfopen('pingüino.jpg', 'r')
    140704303274928