Search code examples
pythondllctypes

ctypes opens a DLL, calling a function returns AttributeError: function not found, but function exists in the DLL


I'm trying to use a third-party DLL in my code and I have to call a function from that DLL using Python. According to documentation, the DLL has got just 4 functions, and none of those can be called from my Python code. I've tried both x86 and x64 DLLs (with the corresponding Python distribution). I tried Python 3.7, 3.9, 3.10 and 3.11, all those cannot call any of the functions.

from ctypes import cdll
sdk = cdll.LoadLibrary("./Library.dll")
get_data = sdk.sdk_get_data
print(get_data)

This returns an error:

  File "D:\sandbox\еуые.py", line 7, in <module>
    get_data = sdk.sdk_get_data
  File "C:\Python39-64\lib\ctypes\__init__.py", line 387, in __getattr__
    func = self.__getitem__(name)
  File "C:\Python39-64\lib\ctypes\__init__.py", line 392, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: function 'sdk_get_data' not found

I've used lucasg's Dependencies tool to make sure the function documented exists in the DLL and here is the output:

1 (0x0001),  (0x), int __cdecl sdk_get_data(char * __ptr64,int,unsigned short * __ptr64,unsigned char * __ptr64), 0x00001b30, Microsoft

How do I call functions from this DLL? Perhaps writing a wrapper in C++ is necessary?


Solution

  • C ABI exports don't have the information to list the parameter types, so that indicates the function is a C++ function. Since the parameters are C-compatible, you may get away with using the C++ decorated name of the function. It looks like ?sdk_get_data@@YAHPEADHPEAGPEAE@Z using the Windows SDK dumpbin /exports tool, but can vary by compiler.

    For example, with a C++-compiled function with your given signature in test.cpp with the Microsoft compiler:

    __declspec(dllexport)
    int __cdecl sdk_get_data(char* p, int n, unsigned short* w, unsigned char* data) {
        return 1;
    }
    

    The following will look up the function:

    import ctypes as ct
    dll = ct.CDLL('./test')
    func = dll['?sdk_get_data@@YAHPEADHPEAGPEAE@Z']
    print(func())  # outputs 1
    

    But if you make a C-wrapper such as the following that forwards the parameters to the C++ function using an exported C function and link it to your 3rd-party DLL, then your original code should work without a decorated name:

    extern "C" __declspec(dllexport)
    int sdk_get_data_c(char* p, int n, unsigned short w, unsigned char* data) {
        return sdk_get_data(p, n, w, data);
    }