2

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.

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

c:\windows\winsxs\amd64_my_handy_lib_<public_key_token>_1.0.0.0_none_<some_ID>

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

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

Οurous
  • 348
  • 6
  • 17
skiloup
  • 33
  • 5
  • You can embed the manifest in the .pyd if it's an extension module, or just use a private assembly. If it's ctypes you can include a manifest to create an activation context. I can give an example for the latter if that's what you need. – Eryk Sun Dec 09 '14 at 22:57
  • It's not an extension module, but that's certainly a path that could be explored. Any idea how to embed the manifest in the .pyd? I am currently importing the DLL with ctypes as follows: `self._lib = ctypes.CDLL(path)` Where **path** is the path to the dll. I'd like to see the example you referred to. – skiloup Dec 10 '14 at 00:07

1 Answers1

5

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)

output:

C:\Windows\WinSxS\amd64_microsoft.vc90.crt_1fc8b3b9a1e18e3b_9.0.30729.6161_none_08e61857a83bc251\msvcr90.DLL

This was tested under Python 3.4.2, which is built with VS 2010 and links with msvcr100.dll instead. So at least in this case setting the activation context was really required, else loading msvcr90.dll would fail with ERROR_MOD_NOT_FOUND.

Eryk Sun
  • 33,190
  • 5
  • 92
  • 111