Ogiad
Ogiad

Reputation: 173

Accessing DLL function using ctypes

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.

Method1

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.

Method2

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

Answers (2)

Ogiad
Ogiad

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).

  1. I want to call the following C file from Python.
//Filename: my_functions.c
#include <stdio.h>

int square(int i){
   return i * i;
}
  1. Create a C file using __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;
}
  1. Create a 64-bit dll file 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
  1. Call the 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

Daniel Kleinstein
Daniel Kleinstein

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

Related Questions