Eryk Sun <eryk...@gmail.com> added the comment:
It may suit the needs of NumPy and SciPy to use an assembly for DLL dependencies. With an assembly it's possible for two DLLs with the same name to load in a process and possible for a DLL to extend the assembly search path with up to nine relative paths at load time. The target directory can be up to two levels above the DLL directory (e.g. "..\..\assembly_dir"). An assembly can thus be packaged as a common dependency for other packages, and packages can depend on different versions of the assembly. For example, let's make a package that changes the _tkinter.pyd extension module to use a private assembly, which consists of the two DLL dependencies, "tcl86t.dll" and "tk86t.dll". Begin by copying "DLLs\_tkinter.pyd" to a package directory such as "Lib\site-packages\mytk". Modify the embedded #2 manifest in "_tkinter.pyd" (use mt.exe, or a GUI resource editor) to include a dependency on an assembly named "amd64_tcl_tk_8.6.6.0": <dependency> <dependentAssembly> <assemblyIdentity name="amd64_tcl_tk_8.6.6.0" version="8.6.6.0" type="win32" processorArchitecture="amd64" /> </dependentAssembly> </dependency> Next, add the following component configuration file beside the extension module, named "_tkinter.pyd.2.config": <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <configuration> <windows> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="..\__winsxs__" /> </assemblyBinding> </windows> </configuration> This extends the assembly probing path that's used by the Fusion loader in the session server (csrss.exe). The Fusion loader probes for the assembly in four locations per directory. It checks for the assembly both as a DLL and as a manifest file, both in the directory and in a subdirectory that's named for the assembly. We'll be using a subdirectory with a manifest. "..\__winsxs__" resolves to "site-packages\__winsxs__". Create this directory and a subdirectory named "amd64_tcl_tk_8.6.6.0". To this, add the two DLL dependencies -- tcl86t.dll and tk86t.dll -- plus the following manifest file named "amd64_tcl_tk_8.6.6.0.manifest": <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assemblyIdentity name="amd64_tcl_tk_8.6.6.0" version="8.6.6.0" type="win32" processorArchitecture="amd64" /> <file name="tcl86t.dll" /> <file name="tk86t.dll" /> </assembly> That's all it takes. If configured properly, you should be able to import the extension module via `from mytk import _tkinter`. This will work in a virtual environment. However, I haven't checked whether the loader handles private assemblies in the same way in a store app. That's off my radar. > packages need to adopt to calling AddDllDirectory. As long as > python is built with ctypes, this is easy enough to adopt, even > though there are some caveats Avoid using ctypes.windll in libraries. It caches the ctypes.WinDLL instance, which caches function pointers. Projects that use the same DLLs thus can interfere with each other by setting incompatible prototypes. It also doesn't allow us to enable use_last_error to get reliable error handling. Also, the DLL_DIRECTORY_COOKIE return value is a pointer type, not a 32-bit integer. Even if we're not using it to cleanup afterwards (i.e. AddDllDirectory; LoadLibraryExW; RemoveDllDirectory), which we should be doing, we need the full 64-bit value to reliably check for failure (NULL). By some fluke, the low DWORD of the cookie could be 0. Here are the ctypes definitions using a private WinDLL instance and an errcheck function: import ctypes from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) LOAD_LIBRARY_SEARCH_DEFAULT_DIRS = 0x00001000 DLL_DIRECTORY_COOKIE = wintypes.LPVOID def _errcheck_zero(result, func, args): if not result: raise ctypes.WinError(ctypes.get_last_error()) return args kernel32.AddDllDirectory.errcheck = _errcheck_zero kernel32.AddDllDirectory.restype = DLL_DIRECTORY_COOKIE kernel32.AddDllDirectory.argtypes = (wintypes.LPCWSTR,) kernel32.RemoveDllDirectory.errcheck = _errcheck_zero kernel32.RemoveDllDirectory.argtypes = (DLL_DIRECTORY_COOKIE,) kernel32.LoadLibraryExW.errcheck = _errcheck_zero kernel32.LoadLibraryExW.restype = wintypes.HMODULE kernel32.LoadLibraryExW.argtypes = ( wintypes.LPCWSTR, wintypes.HANDLE, wintypes.DWORD) Don't call SetDefaultDllDirectories. Use LoadLibraryExW(path, None, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS). ---------- nosy: +eryksun _______________________________________ Python tracker <rep...@bugs.python.org> <https://bugs.python.org/issue35688> _______________________________________ _______________________________________________ Python-bugs-list mailing list Unsubscribe: https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com