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
-
@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?Yuxiang Wang– Yuxiang Wang2014年02月14日 03:15:06 +00:00Commented 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!Yuxiang Wang– Yuxiang Wang2014年02月14日 03:27:22 +00:00Commented Feb 14, 2014 at 3:27
1 Answer 1
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']