Reputation: 173
I have been trying to convert a C-code to DLL and load it from Python but I have never succeeded. I am using Windows10 (64bit). The original C code is quite simple:
//Filename: my_functions.c
# include <stdio.h>
int square(int i){
return i*i
}
I summarize the two approaches I have tried so far.
First, I built the 64-bit dll by cl.exe
on Command Prompt:
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\VsDevCmd.bat" -arch=x64
cl.exe /D_USRDL /D_WINDLL my_functions.c /MT /link /DLL /OUT:my_functions.dll /MACHINE:X64
As a result, I got a file my_functions.dll
. Subsequently, I tried to load it by the following Python code (Python 3.8, Spyder):
# Filename: calling_c_functions.py
from ctypes import *
my_functions = CDLL("./my_functions.dll")
print(type(my_functions))
print(my_functions.square(10))
print("Done")
However, I got the error:
AttributeError: function 'square' not found
.
Why does this happen? According to the document in Microsoft: Exporting from a DLL, a DLL file contains an exports table including the name of every function that the DLL exports to other executables. These functions need to be accessed either by ordinal number
or by name
(different from the original name 'square').
This brought me to Method 2 below.
Based on Python: accessing DLL function using ctypes -- access by function name fails, I first created a DLL file by myself using the keyword __declspec(dllexport)
:
//Filename: my_functionsDLL2.dll
#include <stdio.h>
extern "C"
{
__declspec(dllexport) int square(int i);
}
__declspec(dllexport) int square(int i)
{
return i * i;
}
The next step is to find a real function name in the my_functionsDLL2.dll
by link.exe
. I did the following on the Command Prompt.
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\link.exe"
link /dump /exports my_functionsDLL2.dll
I should be able to see a list of function names but ended up getting the following output:
Microsoft (R) COFF/PE Dumper Version 14.29.30037.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file myfunctionsDLL2.dll
myfunctionsDLL2.dll : warning LNK4048: Invalid format file; ignored
Summary
Any idea what I'm doing wrong?
Upvotes: 1
Views: 5518
Reputation: 173
I needed to make 2 modifications to my current approach.
The first modification is the one pointed out by @Daniel Kleinstein. But only with that, I stil got an error: C2059: syntax error: 'string'
. This error is due to extern "C" {
guards that are only understood by C++ (reference: C2059 syntax error 'string' ? ). For a C source file, I needed to add __cplusplus
ifdef.
I summarize the overall procedures that led me to the correct result (there may be some redundancies though).
//Filename: my_functions.c
#include <stdio.h>
int square(int i){
return i * i;
}
__declspec(dllexport)
and __cplusplus ifdef
.//Filename: my_functions2.c
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) int square(int i);
#ifdef __cplusplus
}
#endif
__declspec(dllexport) int square(int i)
{
return i * i;
}
my_functions2.dll
by running the following commands on Command Prompt. You need to locate the cl.exe
carefully on your computer.call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\VsDevCmd.bat" -arch=x64
cl.exe /D_USRDL /D_WINDLL my_functions2.c /MT /link /DLL /OUT:my_functions2.dll /MACHINE:X64
my_functions2.dll
from Python using ctypes
. In this example, I test the function square
and obtain a correct result.#Filename: call_my_functions2.py
import ctypes as ctypes
my_functions = ctypes.CDLL("./my_functions2.dll")
print(type(my_functions))
print(my_functions.square(10))
print("Done")
I get the following output:
<class 'ctypes.CDLL'>
100
Done
Upvotes: 2
Reputation: 5502
You're on the right track but got a bit confused along the way - taking a code file and giving it a .dll
extension isn't enough to turn it into a DLL, you do still need to compile it. Your linker is telling you that it doesn't know how to parse your file:
myfunctionsDLL2.dll : warning LNK4048: Invalid format file; ignored
because it only knows how to parse real executables.
You should rename your second code file (the one you named my_functionsDLL2.dll
) back to my_functions.c
- and compile it the same way you did the first time. But this time, because you added __declspec(dllexport)
to your function definition, your compiler (cl.exe
) will know that these functions are meant to be exported from the DLL and will export them, making them available through ctypes.
Upvotes: 2