0

I wrote a wrapper around a binary file (libmpsse) for use in Windows (there already exists a wrapper around this binary, but only for Linux) while maintaining compatability for an existing wrapper around a related binary (ftd2xx). This uses WinDLL to load the library (trimmed out the bit that finds the dll path):

from ctypes import (
    CFUNCTYPE,
    POINTER,
    Structure,
    c_char,
    c_char_p,
    c_int,
    c_ubyte,
    c_uint32,
    c_int32,
    c_ulong,
    c_ulonglong,
    c_void_p,
    c_long
)
BOOL = c_long
DWORD = c_ulong
HANDLE = c_void_p
LPVOID = c_void_p
STRING = c_char_p
UCHAR = c_ubyte
PUCHAR = POINTER(c_ubyte)
PVOID = c_void_p
LPTSTR = STRING
LPDWORD = POINTER(DWORD)
LPUWORD = POINTER(c_uint32)
VOID: None = None
ULONGLONG = c_ulonglong
FT_HANDLE = c_void_p
FT_STATUS = c_ulong

from ctypes import WinDLL
_libraries["libmpsse.dll"] = WinDLL(str(dll_path))
import_done = True

if import_done:
    from ftd2xx._ftd2xx import _ft_device_list_info_node

    I2C_GetNumChannels = _libraries["libmpsse.dll"].I2C_GetNumChannels
    I2C_GetNumChannels.argtypes = [LPUWORD]
    I2C_GetNumChannels.restype = FT_STATUS

    I2C_GetChannelInfo = _libraries["libmpsse.dll"].I2C_GetChannelInfo
    I2C_GetChannelInfo.argtypes = [c_uint32, POINTER(_ft_device_list_info_node)]
    I2C_GetChannelInfo.restype = FT_STATUS

    I2C_OpenChannel = _libraries["libmpsse.dll"].I2C_OpenChannel
    I2C_OpenChannel.argtypes = [c_uint32, POINTER(FT_HANDLE)]
    I2C_OpenChannel.restype = FT_STATUS

    class _ft_channel_config(Structure):
        _fields_ = [
            ("ClockRate", c_int32, 32),
            ("LatencyTimer", c_ubyte, 8),
            ("Options", c_uint32, 32),
        ]

    I2C_InitChannel = _libraries["libmpsse.dll"].I2C_InitChannel
    I2C_InitChannel.argtypes = [FT_HANDLE, POINTER(_ft_channel_config)]
    I2C_InitChannel.restype = FT_STATUS

    I2C_CloseChannel = _libraries["libmpsse.dll"].I2C_CloseChannel
    I2C_CloseChannel.argtypes = [FT_HANDLE]
    I2C_CloseChannel.restype = FT_STATUS

    I2C_DeviceRead = _libraries["libmpsse.dll"].I2C_DeviceRead
    I2C_DeviceRead.argtypes = [FT_HANDLE, c_uint32, c_uint32, LPVOID, LPUWORD, c_uint32]
    I2C_DeviceRead.restype = FT_STATUS

    I2C_DeviceWrite = _libraries["libmpsse.dll"].I2C_DeviceWrite
    I2C_DeviceWrite.argtypes = [FT_HANDLE, c_uint32, c_uint32, LPVOID, LPUWORD, c_uint32]
    I2C_DeviceWrite.restype = FT_STATUS

    FT_WriteGPIO = _libraries["libmpsse.dll"].FT_WriteGPIO
    FT_WriteGPIO.argtypes = [FT_HANDLE, c_uint32, c_uint32]
    FT_WriteGPIO.restype = FT_STATUS

    FT_ReadGPIO = _libraries["libmpsse.dll"].FT_ReadGPIO
    FT_ReadGPIO.argtypes = [FT_HANDLE, PUCHAR]
    FT_ReadGPIO.restype = FT_STATUS

    Init_libMPSSE = _libraries["libmpsse.dll"].Init_libMPSSE
    Init_libMPSSE.restype = None

    Cleanup_libMPSSE = _libraries["libmpsse.dll"].Cleanup_libMPSSE
    Cleanup_libMPSSE.restype = None

This has been functional, but when my application is closed, there are 17 messages of GetProcAddress failed: 0x7f. This is messing with some of our workflows and other processes. I assume it has something to do with some cleanup on exit for the WinDLL object, but I can't find where this might be happening to debug further. Moreover it is confusing as number of times it shows up doesn't correlate with anything within the code as I'm only using 11 functions.

Edit: The problem occurs from just importing this file. To boil the down problem down to it's most simple case I have been running a file like this:

if __name__ == "__main__":
    print("Main Started")
    import libmpsse
    print("Import Done")

I can execute the methods and they perform the task(s) specified. I didn't include this code because the error is not caused by calling the methods as shown by the above example. The output for this file as shown in visual studio is this (VS is adding the thread/process exit information, if run in a terminal they are not present):

Main Started
Import DoneThe thread 'MainThread' (0x1) has exited with code 0 (0x0).

GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
GetProcAddress failed: 0x7f
The program 'python.exe' has exited with code 0 (0x0).
Fr33dan
  • 4,227
  • 3
  • 35
  • 62
  • What do you mean by "*has been functional*"? I see no functionality. Also https://stackoverflow.com/q/20897842/4788546. What happens if `_libraries["libmpsse.dll"] = CDLL(str(dll_path))`? – CristiFati Aug 16 '23 at 17:31
  • @CristiFati Yes this is a wrapper, so the code that utilizes these functions is not included. Updated the question with clarification on these points. Just tried using CDLL generates the same error. – Fr33dan Aug 16 '23 at 19:10
  • Check if the functions are exported by the *.dll*. Use one of the (1st 2) tools from [\[SO\]: Discover missing module using command-line ("DLL load failed" error) (@CristiFati's answer)](https://stackoverflow.com/a/74883130/4788546). – CristiFati Aug 16 '23 at 19:23
  • @CristiFati Sorry for the delay, got caught in a 4 hour meeting and the end of which my brain was too fried to come back to this. I've already used both of those tools to discover an undocumented dependency on VC++ 2013 redistributable binaries. Yes the functions are exported. Looking again with this new issue in mind I notice that it does use `LoadLibraryW` from *kernel32.dll*. I wonder if I've been trying to diagnose an issue within the DLL and not my code/ctypes. – Fr33dan Aug 17 '23 at 13:24
  • Try reducing the code to [\[SO\]: How to create a Minimal, Reproducible Example (reprex (mcve))](https://stackoverflow.com/help/minimal-reproducible-example). No need to have all the functions (1 it's enough). Also remove threads from the equation. And get rid of those duplicate definitions. Are you sure the names in the *.dll* are the ones you specify? – CristiFati Aug 17 '23 at 17:49

0 Answers0