Using ctypes to load a specific runtime library (MSVCRT80)
We're using ctypes.windll
to load a third-party library. This library uses 'MSVCRT80' and states that it's callers responsibility to free resources. Therefor we've tried using windll.msvcrt.free(pointer)
to free resources returned by the external library. This fails as windll.msvcrt
is another runtime ('MSVCRT90.DLL' which Python is linked with)
Therefor we explicitly need to load 'MSVCRT80.DLL', but I can't find a way to load this library. I've tried using ctypes.util.find_library('msvcrt80')
but this returns None
. I guess this is because this function only looks through the path, while the actual library is located at c:\windows\winsxs\amd64_microsoft.vc80.crt_1fc8b3b9a1e18e3b_8.0.50727.6195_none_88e41开发者_如何转开发e092fab0294
.
Is there any way I can load the correct runtime using ctypes
?
According to Hans' comment in your other question, you can use GetModuleHandle to get the handle to the already loaded CRT. Like this:
handle = windll.kernel32.GetModuleHandleA('msvcr80')
msvcr80 = WinDLL('', handle=handle)
msvcr80.free(...)
For what it's worth, windll.msvcrt
actually refers to the Windows system supplied C runtime, which is named msvcrt.dll
and resides in your system32
directory.
I finally figured out a workaround for this problem. After loading the external library, I enumerate over the loaded modules using EnumProcessModules
, determine the filenames using GetModuleFileName
and reference the correct module and load the free()
-function from this runtime.
The code I'm using to do this is as follows:
from ctypes import *
def enumProcessModules():
# Get handle of current process
kernel32 = windll.kernel32
kernel32.GetCurrentProcess.restype = c_void_p
hProcess = kernel32.GetCurrentProcess()
# Load EnumProcessModules either from kernel32.dll or psapi.dll
try:
EnumProcessModulesProc = windll.psapi.EnumProcessModules
except AttributeError:
EnumProcessModulesProc = windll.kernel32.EnumProcessModules
EnumProcessModulesProc.restype = c_bool
EnumProcessModulesProc.argtypes = [c_void_p, POINTER(c_void_p), c_ulong, POINTER(c_ulong)]
hProcess = kernel32.GetCurrentProcess()
hMods = (c_void_p * 1024)()
cbNeeded = c_ulong()
if EnumProcessModulesProc(hProcess, hMods, sizeof(hMods), byref(cbNeeded)):
return hMods
return None
def getLoadedModule(moduleName):
kernel32 = windll.kernel32
kernel32.GetModuleFileNameA.restype = c_ulong
kernel32.GetModuleFileNameA.argtypes = [c_void_p, c_char_p, c_ulong]
modules = enumProcessModules()
if modules is None:
return None
for module in modules:
cPath = c_char_p(' ' * 1024)
kernel32.GetModuleFileNameA(module, cPath, c_ulong(1024))
path = cPath.value
if path.lower().endswith(moduleName):
return module
return None
To load the correct runtime and find the free()
function I use the code above:
runtimeModuleHandle = getLoadedModule("msvcr80.dll")
runtimeModule = ctypes.CDLL('', handle = runtimeModuleHandle) # cdecl calling convention
runtimeModule.free.restype = None
runtimeModule.free.argtypes = [ctypes.c_void_p]
myFreeProc = runtimeModule.free
精彩评论