90 lines
2.7 KiB
Python
90 lines
2.7 KiB
Python
"""
|
|||
|
|
PyInstaller 运行时钩子:固定 MSVC 运行库加载顺序,再注册 DLL 搜索路径。
|
||
|
|
|
||
|
|
打包阶段在 strip 掉各 wheel 自带 CRT 后,会 **仅嵌入** 构建所用 Python ``DLLs``
|
||
|
|
下的 ``vcruntime140.dll`` / ``vcruntime140_1.dll`` / ``msvcp140.dll`` 至 ``_MEIPASS``。
|
||
|
|
本钩子在导入 chroma/torch 等扩展 **之前**,用 ``ctypes.WinDLL(绝对路径)`` 从 **同一目录**
|
||
|
|
(优先 exe 旁若用户自行放置了完整三件套,否则 ``_MEIPASS``)预加载,避免进程落到
|
||
|
|
目标机 ``System32`` 里较旧版本(如 14.29)再与 native 扩展不兼容触发 ``0xc0000005``。
|
||
|
|
|
||
|
|
随后再 ``add_dll_directory`` exe 目录与 ``_MEIPASS``,并调整 ``PATH``。不使用 ``SetDllDirectoryW``。
|
||
|
|
"""
|
||
|
|
|
||
|
|
|
||
|
|
def _preload_msvc_crt_once():
|
||
|
|
"""若 exe 目录或 ``_MEIPASS`` 下存在 ``msvcp140.dll``,则按依赖顺序预加载该目录内 CRT。"""
|
||
|
|
import ctypes
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
if sys.platform != "win32" or not getattr(sys, "frozen", False):
|
||
|
|
return
|
||
|
|
|
||
|
|
order = ("vcruntime140.dll", "vcruntime140_1.dll", "msvcp140.dll")
|
||
|
|
search_dirs = []
|
||
|
|
try:
|
||
|
|
exe_parent = Path(sys.executable).resolve().parent
|
||
|
|
search_dirs.append(exe_parent)
|
||
|
|
except OSError:
|
||
|
|
pass
|
||
|
|
meipass = getattr(sys, "_MEIPASS", None)
|
||
|
|
if meipass:
|
||
|
|
search_dirs.append(Path(meipass))
|
||
|
|
|
||
|
|
for d in search_dirs:
|
||
|
|
if not d.is_dir():
|
||
|
|
continue
|
||
|
|
if not (d / "msvcp140.dll").is_file():
|
||
|
|
continue
|
||
|
|
for name in order:
|
||
|
|
p = d / name
|
||
|
|
if p.is_file():
|
||
|
|
try:
|
||
|
|
ctypes.WinDLL(str(p))
|
||
|
|
except OSError:
|
||
|
|
pass
|
||
|
|
return
|
||
|
|
|
||
|
|
|
||
|
|
def _apply_dll_search_paths():
|
||
|
|
"""将 exe 目录与 ``_MEIPASS`` 注册为 DLL 目录,并置于 PATH 最前(exe 目录优先)。"""
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
if not getattr(sys, "frozen", False):
|
||
|
|
return
|
||
|
|
|
||
|
|
dirs = []
|
||
|
|
try:
|
||
|
|
exe_dir = str(Path(sys.executable).resolve().parent)
|
||
|
|
if exe_dir:
|
||
|
|
dirs.append(exe_dir)
|
||
|
|
except OSError:
|
||
|
|
pass
|
||
|
|
|
||
|
|
meipass = getattr(sys, "_MEIPASS", None)
|
||
|
|
if meipass:
|
||
|
|
dirs.append(meipass)
|
||
|
|
|
||
|
|
for d in dirs:
|
||
|
|
if not d:
|
||
|
|
continue
|
||
|
|
try:
|
||
|
|
if hasattr(os, "add_dll_directory"):
|
||
|
|
os.add_dll_directory(d)
|
||
|
|
except OSError:
|
||
|
|
pass
|
||
|
|
|
||
|
|
path = os.environ.get("PATH", "")
|
||
|
|
prefix = os.pathsep.join([d for d in dirs if d])
|
||
|
|
if not prefix:
|
||
|
|
return
|
||
|
|
if path.startswith(prefix):
|
||
|
|
return
|
||
|
|
os.environ["PATH"] = f"{prefix}{os.pathsep}{path}"
|
||
|
|
|
||
|
|
|
||
|
|
_preload_msvc_crt_once()
|
||
|
|
_apply_dll_search_paths()
|