Letting VS2015 make a dll called SomeDLL for me with these implementations
// SomeDll.cpp :
// Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#include "SomeDll.h"
// This is an example of an exported variable
SOMEDLL_API int nSomeDll=0;
// This is an example of an exported function.
SOMEDLL_API int fnSomeDll(void)
{
return 42;
}
// This is the constructor of a class that has been exported.
// see SomeDll.h for the class definition
CSomeDll::CSomeDll()
{
return;
}
I then make a python script, using ctypes and loading the dll, using os to find it:
import os
import ctypes
os.chdir("C:\\Users\\sbkg525\\src\\SomeDll\\Debug")
SomeDll = ctypes.WinDLL("SomeDll.dll")
I can either use attributes of the library or use protoypes. I tried protoypes first.The function returns an int and takes no parameters:
proto = ctypes.WINFUNCTYPE(ctypes.c_int)
params = ()
answer = proto(("fnSomeDll", SomeDll), params)
Unfortunately this says
AttributeError: function 'fnSomeDll' not found
because C++ is name mangled. extern "C" FTW another time; for now
link.exe /dump /exports Debug\SomeDll.dll
1 0 0001114F ??0CSomeDll@@QAE@XZ =
@ILT+330(??0CSomeDll@@QAE@XZ)
2 1 00011244 ??4CSomeDll@@QAEAAV0@$$QAV0@@Z =
@ILT+575(??4CSomeDll@@QAEAAV0@$$QAV0@@Z)
3 2 0001100A ??4CSomeDll@@QAEAAV0@ABV0@@Z =
@ILT+5(??4CSomeDll@@QAEAAV0@ABV0@@Z)
4 3 0001111D ?fnSomeDll@@YAHXZ =
@ILT+280(?fnSomeDll@@YAHXZ)
5 4 00018138 ?nSomeDll@@3HA =
?nSomeDll@@3HA (int nSomeDll)
Looks like we want 4;
answer = proto(("?fnSomeDll@@YAHXZ", SomeDll), params)
print answer
>>> <WinFunctionType object at 0x0248A7B0>
Of course. It's a function. Let's call the function
print answer()
>>> 42
Done. I'll try attributes next. And functions which take parameters. Later, I'll look at other calling conventions.
First, the docs says, "foreign functions can be accessed as attributes of loaded shared libraries.
Given
extern "C"
{
SOMEDLL_API void hello_world()
{
//now way of telling this has worked
}
}
we call it like this
lib = ctypes.cdll.LoadLibrary('SomeDll.dll')
lib.hello_world()
It seems to assume a return type of int. For example,
extern "C"
{
SOMEDLL_API double some_number()
{
return 1.01;
}
}
called as follows
lib = ctypes.cdll.LoadLibrary('SomeDll.dll')
lib.hello_world()
val = lib.some_number()
print val, type(val)
Gives
-858993460, <type 'int'>
We need to specify the return type, since it's not an int:
lib.some_number.restype = ctypes.c_double
val = lib.some_number()
print val, type(val)
then we get what we want
1.01 <type 'float'>
We need to do likewise for parameters
extern "C"
{
SOMEDLL_API double add_numbers(double x, double y)
{
return x + y;
}
}
If we just try to call it with some floats we get an error
total = lib.add_numbers(10.5, 25.7)
ctypes.ArgumentError: argument 1: <type 'exceptions.TypeError'>: Don't know how to convert parameter 1
Stating the parameter type fixes this
lib.add_numbers.restype = ctypes.c_double
lib.add_numbers.argtypes = [ctypes.c_double, ctypes.c_double]
total = lib.add_numbers(10.5, 25.7)
print total, type(total)
36.2 <type 'float'>
Just one starter thought on strings. Returning a const char * seems to give a str in python
SOMEDLL_API const char * speak()
{
return "Hello";
}
called like this
lib.speak.restype = ctypes.c_char_p
print lib.speak()
says "Hello"; using parameters as strings and particularly as refrences that can be changed needs some investigation.
Does a similar approach using attributes of the loaded library work for non-extern "C" functions? Can I use the proto approach on the extern "C" functions?
Let's see...
its very good explanation.
ReplyDeletethank you..!
Thanks for taking the time to leave a lovely comment
DeleteThank you very much for this explanation and the investigation you made. This is the only readable source of information which explains it all.
ReplyDeleteI finally learned how to approach a function from a DLL i was given, using its full function name.
What I was missing is how to get a class object and not a single function, and also how can I use 'params' variable for non-C implementations, such as the one you showed in the beginning, using WINFUNCTYPE.