使用ctypes加载特定运行时库(MSVCRT80)

1 投票
2 回答
1262 浏览
提问于 2025-04-17 02:11

我们正在使用 ctypes.windll 来加载一个第三方库。这个库使用了 'MSVCRT80',并说明调用者需要负责释放资源。因此,我们尝试使用 windll.msvcrt.free(pointer) 来释放外部库返回的资源。但这失败了,因为 windll.msvcrt 是另一个运行时('MSVCRT90.DLL',这是 Python 连接的库)。

所以我们需要明确加载 'MSVCRT80.DLL',但我找不到加载这个库的方法。我尝试使用 ctypes.util.find_library('msvcrt80'),但它返回了 None。我猜这是因为这个函数只在路径中查找,而实际的库位于 c:\windows\winsxs\amd64_microsoft.vc80.crt_1fc8b3b9a1e18e3b_8.0.50727.6195_none_88e41e092fab0294

有没有办法让我使用 ctypes 加载正确的运行时?

2 个回答

2

根据Hans在你其他问题中的评论,你可以使用GetModuleHandle来获取已经加载的C运行时的句柄。这样做:

handle = windll.kernel32.GetModuleHandleA('msvcr80')
msvcr80 = WinDLL('', handle=handle)
msvcr80.free(...)

顺便提一下,windll.msvcrt实际上是指Windows系统提供的C运行时,它的名字是msvcrt.dll,并且位于你的system32目录下。

1

我终于找到了解决这个问题的方法。在加载外部库之后,我使用 EnumProcessModules 来列出已加载的模块,然后用 GetModuleFileName 确定文件名,接着引用正确的模块并从这个运行时加载 free() 函数。

我用来实现这个的代码如下:

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

为了加载正确的运行时并找到 free() 函数,我使用了上面的代码:

    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

撰写回答