如何从 Python 中的 SXS 加载 C DLL?

2021-12-25 00:00:00 python dll c++ manifest winsxs

这通常通过在可执行文件所在的清单文件中指定 DLL 依赖项来完成.但是,我不知道如何在 Python 中完成此操作.加载 DLL 不是问题,问题在于在 SXS 中找到合适的 DLL 来加载.

This is typically done by specifying the DLL dependency in a manifest file that resides with the executable. However, I don't know how to accomplish this in Python. Loading the DLL isn't an issue, but rather finding the appropriate DLL in the SXS to load is the problem.

是否有标准程序来指定在哪里可以找到 DLL?对于这个例子,我们假设它住在这里:

Is there a standard procedure for specifying where to find the DLL ? For this example let's assume it lives here:

c:windowswinsxsamd64_my_handy_lib_<public_key_token>_1.0.0.0_none_<some_ID>

我真的需要手动搜索 c:windowswinsxs 目录以按名称查找我的 DLL,然后检查父目录是否包含正确的版本?

Do I REALLY need to manually search the c:windowswinsxs directory looking for my DLL by name, then check the parent directory to see if it contains the correct version?

我只是做的 Python 项目不够多,所以不知道什么是实现这一目标的合适方法.

I just don't do Python projects enough to know what the appropriate way to accomplish this.

推荐答案

这是从 WinSxS 目录加载 CRT 的示例.

Here's an example that loads the CRT from the WinSxS directory.

actctx.manifest:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <dependency>
        <dependentAssembly>
            <assemblyIdentity
                type="win32"
                name="Microsoft.VC90.CRT"
                version="9.0.21022.8"
                processorArchitecture="amd64"
                publicKeyToken="1fc8b3b9a1e18e3b">
            </assemblyIdentity>
        </dependentAssembly>
    </dependency>
</assembly>

actctx.py:

from ctypes import *
from ctypes.wintypes import *

kernel32 = WinDLL("kernel32", use_last_error=True)

ACTCTX_FLAG_PROCESSOR_ARCHITECTURE_VALID = 0x001
ACTCTX_FLAG_LANGID_VALID = 0x002
ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID = 0x004
ACTCTX_FLAG_RESOURCE_NAME_VALID = 0x008
ACTCTX_FLAG_SET_PROCESS_DEFAULT = 0x010
ACTCTX_FLAG_APPLICATION_NAME_VALID = 0x020
ACTCTX_FLAG_HMODULE_VALID = 0x080
DEACTIVATE_ACTCTX_FLAG_FORCE_EARLY_DEACTIVATION = 1

INVALID_HANDLE_VALUE = HANDLE(-1).value
ULONG_PTR = WPARAM  # pointer-sized unsigned integer

class ACTCTX(Structure):
    _fields_ = (("cbSize", ULONG),
                ("dwFlags", DWORD),
                ("lpSource", LPCWSTR),
                ("wProcessorArchitecture", USHORT),
                ("wLangId", LANGID),
                ("lpAssemblyDirectory", LPCWSTR),
                ("lpResourceName", LPCWSTR),
                ("lpApplicationName", LPCWSTR),
                ("hModule", HMODULE))

    def __init__(self, *args, **kwds):
        super(ACTCTX, self).__init__(sizeof(self), *args, **kwds)

CreateActCtxW = kernel32.CreateActCtxW
CreateActCtxW.restype = HANDLE
CreateActCtxW.argtypes = (POINTER(ACTCTX),)
ReleaseActCtx = kernel32.ReleaseActCtx
ReleaseActCtx.restype = None
ReleaseActCtx.argtypes = (HANDLE,)
ActivateActCtx = kernel32.ActivateActCtx
ActivateActCtx.argtypes = (HANDLE, POINTER(ULONG_PTR))
DeactivateActCtx = kernel32.DeactivateActCtx
DeactivateActCtx.argtypes = (DWORD, ULONG_PTR)

if __name__ == "__main__":
    manifest_path = "actctx.manifest" # keep ref
    ctx = ACTCTX(lpSource=manifest_path)
    hActCtx = CreateActCtxW(byref(ctx))
    if hActCtx == INVALID_HANDLE_VALUE:
        raise WinError(get_last_error())

    cookie = ULONG_PTR()
    if not ActivateActCtx(hActCtx, byref(cookie)):
        raise WinError()
    msvcr90 = CDLL("msvcr90")
    if not DeactivateActCtx(0, cookie):
        raise WinError(get_last_error())

    ReleaseActCtx(hActCtx)

    # show DLL path
    hModule = HANDLE(msvcr90._handle)
    path = (c_wchar * 260)()    
    kernel32.GetModuleFileNameW(hModule, path, len(path))
    print(path.value)

输出:

C:WindowsWinSxSamd64_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.6161_none_08e61857a83bc251msvcr90.DLL

<小时>

这是在 Python 3.4.2 下测试的,Python 3.4.2 是用 VS 2010 构建的,而是与 msvcr100.dll 链接.因此,至少在这种情况下,确实需要设置激活上下文,否则加载 msvcr90.dll 将失败并显示 ERROR_MOD_NOT_FOUND.

相关文章