Loading dll using Python Ctypes
Make sure your compiler and version of Python are both 32-bit or both 64-bit. You can't mix, which is the cause of OSError: [WinError 193] %1 is not a valid Win32 application
.
Next, make sure to compile as a C program and not C++. That's the cause of the name mangling mention in your answer.
Example (note compiler is for x86 not x64:
C:\>cl /LD /W4 test.c
Microsoft (R) C/C++ Optimizing Compiler Version 17.00.61030 for x86
Copyright (C) Microsoft Corporation. All rights reserved.
test.c
Microsoft (R) Incremental Linker Version 11.00.61030.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:test.dll
/dll
/implib:test.lib
test.obj
Creating library test.lib and object test.exp
Now use a 32-bit Python:
C:\>py -2
Python 2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 20:42:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> from ctypes import *
>>> lib = CDLL('test')
>>> lib.sum(2, 3)
5
If you compile as C++, you can still call functions by exporting them as C, which prevents the C++ name mangling:
test.cpp
extern "C" __declspec(dllexport) int sum(int a, int b) {
return a + b;
}
Accessing DLL function using ctypes
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.
Import dll using ctypes in virtual environment
So, it turns out the problem was not ctypes, permissions or anything like that: the problem was the virtual environment. in fact there was a problem, not only with my environment, but with the miniconda installation itself. I was not able to reinstall the environment nor to update miniconda. I solved everything by reinstalling miniconda from scratch and recreating the environment. Now the code above works as expected.
Wrapping DLL functions using ctypes
Your problem is that you don't have the namespaces quite right. Its
cppdll.InitNetwork.argtypes = [ctypes.c_char, ctypes.c_char, ctypes.c_int]
You can import the ctypes data you need directly into the module. And since ctypes
creates function wrappers for you, you don't really need your own def
to do the same thing. You can call them with cppdll.Whatever
but if you like having the functions at the namespace level, you can just create variables for them.
from ctypes import WinDLL, c_char, c_int
import os
#load dll
cppdll = ctypes.WinDLL("C:\\VS_projects\\MusicWebService\\MusicWebService\\NetServerInterface.dll")
#initialize network:
InitNetwork = cppdll.InitNetwork
InitNetwork.argtypes = [c_char, c_char, c_int]
#judging if the server is online after network initialization:
GetOnlineStatus = cppdll.GetOnlineStatus
#get song name:
GetMusicSongName = cppdll.GetMusicSongName
GetMusicSongName.argtypes = [c_int, c_int]
#making the device playing music:
PlayServerMusic = cppdll.PlayServerMusic
PlayServerMusic = ...
Python | accessing dll using ctypes
nss3.dll is linked to the following DLLs, which are all located in the Firefox directory: nssutil3.dll, plc4.dll, plds4.dll, nspr4.dll, and mozcrt19.dll. The system library loader looks for these files in the DLL search path of the process, which includes the application directory, system directories, the current directory, and each of the directories listed in the PATH
environment variable.
The simplest solution is to change the current directory to the DLL Firefox directory. However, that's not thread safe, so I wouldn't rely on it in general. Another option is to append the Firefox directory to the PATH
environment variable, which is what I suggested in my original version of this answer. However, that's not much better than modifying the current directory.
Newer versions of Windows (NT 6.0+ with update KB2533623) allow the DLL search path to be updated in a thread-safe manner via SetDefaultDllDirectories
, AddDllDirectory
, and RemoveDllDirectory
. But that approach would be over the top here.
In this case, for the sake of both simplicity and compatibility with older versions of Windows, it suffices to call LoadLibraryEx
with the flag LOAD_WITH_ALTERED_SEARCH_PATH
. You need to load the DLL using an absolute path, else the behavior is undefined. For convenience we can subclass ctypes.CDLL
and ctypes.WinDLL
to call LoadLibraryEx
instead of LoadLibrary
.
import os
import ctypes
if os.name == 'nt':
from ctypes import wintypes
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
def check_bool(result, func, args):
if not result:
raise ctypes.WinError(ctypes.get_last_error())
return args
kernel32.LoadLibraryExW.errcheck = check_bool
kernel32.LoadLibraryExW.restype = wintypes.HMODULE
kernel32.LoadLibraryExW.argtypes = (wintypes.LPCWSTR,
wintypes.HANDLE,
wintypes.DWORD)
class CDLLEx(ctypes.CDLL):
def __init__(self, name, mode=0, handle=None,
use_errno=True, use_last_error=False):
if os.name == 'nt' and handle is None:
handle = kernel32.LoadLibraryExW(name, None, mode)
super(CDLLEx, self).__init__(name, mode, handle,
use_errno, use_last_error)
class WinDLLEx(ctypes.WinDLL):
def __init__(self, name, mode=0, handle=None,
use_errno=False, use_last_error=True):
if os.name == 'nt' and handle is None:
handle = kernel32.LoadLibraryExW(name, None, mode)
super(WinDLLEx, self).__init__(name, mode, handle,
use_errno, use_last_error)
Here are all of the available LoadLibraryEx
flags:
DONT_RESOLVE_DLL_REFERENCES = 0x00000001
LOAD_LIBRARY_AS_DATAFILE = 0x00000002
LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008
LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010 # NT 6.1
LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020 # NT 6.0
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040 # NT 6.0
# These cannot be combined with LOAD_WITH_ALTERED_SEARCH_PATH.
# Install update KB2533623 for NT 6.0 & 6.1.
LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100
LOAD_LIBRARY_SEARCH_APPLICATION_DIR = 0x00000200
LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400
LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000
For example:
firefox_path = r'F:\Softwares\Mozilla Firefox'
nss3 = CDLLEx(os.path.join(firefox_path, 'nss3.dll'),
LOAD_WITH_ALTERED_SEARCH_PATH)
nss3.NSS_GetVersion.restype = c_char_p
>>> nss3.NSS_GetVersion()
'3.13.5.0 Basic ECC'
Trying to get a response from a dll function using ctypes python
The function returns void
, so capturing the return value does nothing. You have created x
and y
variables to hold the result and need to inspect them after calling the function. Here's a working example with a sample DLL function implementation:
test.c
__declspec(dllexport)
void get_xy_pos(short *xpos, short *ypos) {
*xpos = 5;
*ypos = 7;
}
test.py
import ctypes as ct
dll = ct.CDLL('./test')
# Good practice is to define .argtypes and .restype so ctypes can do type-checking
dll.get_xy_pos.argtypes = ct.POINTER(ct.c_short),ct.POINTER(ct.c_short)
dll.get_xy_pos.restype = None
def get_xy_pos():
x = ct.c_short() # storage for output parameters
y = ct.c_short()
dll.get_xy_pos(ct.byref(x), ct.byref(y)) # pass by reference
return x.value,y.value # inspect the return values
print(get_xy_pos())
Output:
(5, 7)
Pass object to DLL function imported with ctypes
On a 64-bit system, return values default to c_int
(32-bit). At a minimum, set the .restype
to at least a c_void_p
to indicate a 64-bit pointer is returned.
Ideally, set .argtypes
and .restype
for each function called.
import ctypes
hllDll = ctypes.WinDLL(r"libsie.dll")
hllDll.sie_context_new.argtypes = () # optional but recommended
hllDll.sie_context_new.restype = ctypes.c_void_p # add this
hllDll.sie_context_new.argtypes = ctypes.c_void_p, ctypes.c_char_p # guess, need prototype
# hllDll.sie_context_new.restype = ???
context = hllDll.sie_context_new()
file = hllDll.sie_file_open(context, b"test.sie")
How to use ctypes to call a DLL function with double underline function name?
The function can be called with the following syntax as well, and bypasses the obfuscation Python applies to "dunder" attributes of class instances:
self.windll['__apiJob']()
Example below:
test.cpp
extern "C" __declspec(dllexport)
int __apiJob() {
return 123;
}
test.py
import ctypes
class Job:
def __init__(self):
dll = ctypes.CDLL('./test')
self.apiJob = dll['__apiJob'] # bypass "dunder" class name mangling
self.apiJob.argtypes = ()
self.apiJob.restype = ctypes.c_int
def execute(self):
return self.apiJob()
a = Job()
result = a.execute()
print(result)
Output:
123
As an aside, WinDLL
is used for DLLs declaring functions using __stdcall
calling convention in 32-bit DLLs. CDLL
is used for the default __cdecl
calling convention. 64-bit DLLs have only one calling convention, so either works, but for portability keep this in mind.
Related Topics
Split a List into Nested Lists on a Value
Screenshot of Inactive Window Printwindow + Win32Gui
Reload Flask App When Template File Changes
Convert a 1D Array to a 2D Array in Numpy
Sending Mail from Python Using Smtp
How to Avoid "Permission Denied" When Using Pip with Virtualenv
Get All Object Attributes in Python
How to Replace Back Slash Character with Empty String in Python
Variable Defined with With-Statement Available Outside of With-Block
Converting Between Datetime and Pandas Timestamp Objects
I'm Getting "Typeerror: 'List' Object Is Not Callable". How to Fix This Error
Comments Not Working in Jinja2
How Do Chained Comparisons in Python Actually Work
In Python, How Does One Catch Warnings as If They Were Exceptions
Regular Expression: Match Start or Whitespace
How to Grab Number After Word in Python
How to Schedule a Function to Run Every Hour on Flask
Replace Column Values Based on Another Dataframe Python Pandas - Better Way