pythonc++ctypespython-bindings

How do I allow NoneType Arguments OR a callback function to be passed to a function in ctypes


I've started writing a very simple Python binding around a C/C++ library, which I can't share here for legal reasons as I don't have permission.

This contains a WINFUNCTYPE() as a callback, which can then be used to decorate a function to use as the callback in python, and is defined in the argtypes of my function that I'm binding.

Here is what this looks like in code:

from ctypes import *
from ctypes.wintypes import *

dll = WinDLL('path\\to\\dll')
callback = WINFUNCTYPE(None, POINTER(c_void_p), POINTER(LPBYTE), DWORD, c_ulonglong)
func = dll.FunctionImBinding
func.restype = POINTER(c_void_p)
func.argtypes = [LPCWSTR, callback, c_ulonglong, DWORD, DWORD]

I can then use that like this

@callback
def callback_func(*args):
    #Do something with args
    pass

func('string', callback_func, 0, 1, 2)

This is fine, and works as expected with no problems.

The problem comes when I do something like this:

func('string', None, 0, 1, 2)

I get the following error:

ctypes.ArgumentError: argument 2: <class 'TypeError'>: expected WinFunctionType instance instead of NoneType

Which is completely clear why. ctypes is expecting to see a WINFUNCTYPE() as the second argument and not NoneType, but in the documentation for the SDK I'm using it states the setting the callback as NULL is possible. But I just don't know how to fix it, and I'm struggling to find anything online or in the ctypes docs.

This is the first time I've tried binding a C/C++ library so forgive me if I'm overlooking something obvious. If anyone can help me solve this I would be really greatful.


Solution

  • You can give it a null function pointer by passing address 0 when creating the callback:

    func('string', callback(0), 0, 1, 2)
    

    In fact I looked at the link in the OP's posted answer and later in the thread it has basically the same thing...cast None to the callback type, e.g. cast(None, callback).