3

I am almost sure that I did this once a year ago... Not it just wouldn't work. Weird. I must be making a minor mistake somewhere... Please help!

I have the following toy c code:

// testdll.c
int sum(int a, int b)
{
    return(a+b);
}

And then, since I am using Windows 7, I used WinSDK 7.1's x64 C/C++ compiler to compile it:

cl testdll.c /TC /LD

The output is testdll.dll.

Then, in my Python 3.3, I used:

In [12]: import ctypes

In [13]: lib = ctypes.cdll.LoadLibrary('./testdll.dll')

In [14]: lib
Out[14]: <CDLL './testdll.dll', handle f7000000 at a43ea58>

In [15]: lib.sum
Traceback (most recent call last):

  File "<ipython-input-15-309017dbbec8>", line 1, in <module>
    lib.sum

  File "C:\WinPython2\python-2.7.6.amd64\lib\ctypes\__init__.py", line 378, in __getattr__
    func = self.__getitem__(name)

  File "C:\WinPython2\python-2.7.6.amd64\lib\ctypes\__init__.py", line 383, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))

AttributeError: function 'sum' not found

It can't find this function! It's driving me crazy. Since I used pure C, and I used /TC during compiling, it shouldn't be a name mangling issue.

Any idea would be appreciated. Thank you all so much!

EDIT 2014/02/13 I tried to compile it with gcc also, but with no luck. Same old problem happens. I used dir() in python, and realized everything should be in it - just not the correct name, by which I mean it can't be called via fun.sum. It is able to recognize that the result type of the function is an int.

In [34]: dir(lib)
Out[34]: 
['_FuncPtr',
 '__class__',
 '__delattr__',
 '__dict__',
 '__doc__',
 '__format__',
 '__getattr__',
 '__getattribute__',
 '__getitem__',
 '__hash__',
 '__init__',
 '__module__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_func_flags_',
 '_func_restype_',
 '_handle',
 '_name']

In [35]: lib._func_restype_
Out[35]: ctypes.c_long
2
  • @eryksun It worked! Thank you so much. That saved me days! 1) I don't think this problem can be easily googled (compared to how often it happens), and then I realized this is because it is windows-specific; linux'd be fine. 2) From the link stackoverflow.com/questions/6721364/… I guess gcc needs to do the same thing, but I can't find a good document link from cygwin/gcc to elaborate about this. Can't believe that this page cygwin.com/cygwin-ug-net/dll.html don't even mention it! 3) And finally, could you please make it an answer so I can accept it? Commented Feb 14, 2014 at 3:15
  • @eryksun Or if you'd prefer saving some time, I don't mind organizing and posting it as a solution at all. Again, thank you so much! Commented Feb 14, 2014 at 3:27

1 Answer 1

3

One way to export a symbol is via an option to the linker:

cl testdll.c /LD /link /export:sum

This doesn't scale. A better option is to use the __declspec(dllexport) modifier in the declaration. Refer to Exporting from a DLL in the Visual studio docs.

Apply it conditionally as follows:

#ifdef BUILD_TESTDLL
#define TESTAPI __declspec(dllexport)
#else
#define TESTAPI __declspec(dllimport)
#endif

/* declaration */
TESTAPI int sum(int a, int b);

/* definition */
int sum(int a, int b)
{
    return(a+b);
}

When linking via the import lib, declarations should use __declspec(dllimport), which is the default for TESTAPI. When building the DLL, define BUILD_TESTDLL in the project settings or on the command-line via /D.

Finally, for the ultimate in flexibility, use a .def file such as the following:

LIBRARY TESTDLL
EXPORTS
    sum @ 10 NONAME 
    sum_alias=sum @ 20

This lets you export a function using a different name, or export by ordinal only. Add it as a source file as follows:

cl testdll.c testdll.def /LD

Then in Python, for example:

>>> from ctypes import *
>>> lib = cdll.testdll
>>> lib.sum_alias(1, 2)
3
>>> lib[10](1, 2)      
3
>>> lib[20](1, 2)
3

BTW, ctypes doesn't preload the exports from a DLL. dir(lib) won't show any function pointers at first. They're cached when accessed:

>>> from ctypes import *
>>> lib = cdll.testdll
>>> sorted(vars(lib))
['_FuncPtr', '_handle', '_name']
>>> lib.sum_alias(1, 2)
3
>>> sorted(vars(lib))
['_FuncPtr', '_handle', '_name', 'sum_alias']
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.