Skip to content

Commit b39e583

Browse files
andfoyfmassa
authored andcommitted
Fix external DLL loading on wheels (#2811)
* Fix external DLL loading on wheels * Use SetDefaultDllDirectoriess and AddDllDirectory * Add previous paths * Trigger debug * Do not call SetDefaultDllDirectories. * Do not call loadlibrary if the extensions were not compiled * Fix lint issues
1 parent 4417e18 commit b39e583

File tree

4 files changed

+78
-2
lines changed

4 files changed

+78
-2
lines changed

packaging/wheel/relocate.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,12 @@ def relocate_elf_library(patchelf, output_dir, output_library, binary):
259259

260260

261261
def relocate_dll_library(dumpbin, output_dir, output_library, binary):
262+
"""
263+
Relocate a DLL/PE shared library to be packaged on a wheel.
264+
265+
Given a shared library, find the transitive closure of its dependencies,
266+
rename and copy them into the wheel.
267+
"""
262268
print('Relocating {0}'.format(binary))
263269
binary_path = osp.join(output_library, binary)
264270

torchvision/extension.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,29 @@ def _register_extensions():
1212

1313
# load the custom_op_library and register the custom ops
1414
lib_dir = os.path.dirname(__file__)
15+
if os.name == 'nt':
16+
# Register the main torchvision library location on the default DLL path
17+
import ctypes
18+
import sys
19+
20+
kernel32 = ctypes.WinDLL('kernel32.dll', use_last_error=True)
21+
with_load_library_flags = hasattr(kernel32, 'AddDllDirectory')
22+
prev_error_mode = kernel32.SetErrorMode(0x0001)
23+
24+
if with_load_library_flags:
25+
kernel32.AddDllDirectory.restype = ctypes.c_void_p
26+
27+
if sys.version_info >= (3, 8):
28+
os.add_dll_directory(lib_dir)
29+
elif with_load_library_flags:
30+
res = kernel32.AddDllDirectory(lib_dir)
31+
if res is None:
32+
err = ctypes.WinError(ctypes.get_last_error())
33+
err.strerror += f' Error adding "{lib_dir}" to the DLL directories.'
34+
raise err
35+
36+
kernel32.SetErrorMode(prev_error_mode)
37+
1538
loader_details = (
1639
importlib.machinery.ExtensionFileLoader,
1740
importlib.machinery.EXTENSION_SUFFIXES

torchvision/io/_video_opt.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
_HAS_VIDEO_OPT = False
1414

1515
try:
16-
lib_dir = os.path.join(os.path.dirname(__file__), "..")
16+
lib_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
1717

1818
loader_details = (
1919
importlib.machinery.ExtensionFileLoader,
@@ -22,6 +22,29 @@
2222

2323
extfinder = importlib.machinery.FileFinder(lib_dir, loader_details)
2424
ext_specs = extfinder.find_spec("video_reader")
25+
26+
if os.name == 'nt':
27+
# Load the video_reader extension using LoadLibraryExW
28+
import ctypes
29+
import sys
30+
31+
kernel32 = ctypes.WinDLL('kernel32.dll', use_last_error=True)
32+
with_load_library_flags = hasattr(kernel32, 'AddDllDirectory')
33+
prev_error_mode = kernel32.SetErrorMode(0x0001)
34+
35+
if with_load_library_flags:
36+
kernel32.LoadLibraryExW.restype = ctypes.c_void_p
37+
38+
if ext_specs is not None:
39+
res = kernel32.LoadLibraryExW(ext_specs.origin, None, 0x00001100)
40+
if res is None:
41+
err = ctypes.WinError(ctypes.get_last_error())
42+
err.strerror += (f' Error loading "{ext_specs.origin}" or any or '
43+
'its dependencies.')
44+
raise err
45+
46+
kernel32.SetErrorMode(prev_error_mode)
47+
2548
if ext_specs is not None:
2649
torch.ops.load_library(ext_specs.origin)
2750
_HAS_VIDEO_OPT = True

torchvision/io/image.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
_HAS_IMAGE_OPT = False
88

99
try:
10-
lib_dir = osp.join(osp.dirname(__file__), "..")
10+
lib_dir = osp.abspath(osp.join(osp.dirname(__file__), ".."))
1111

1212
loader_details = (
1313
importlib.machinery.ExtensionFileLoader,
@@ -16,6 +16,30 @@
1616

1717
extfinder = importlib.machinery.FileFinder(lib_dir, loader_details) # type: ignore[arg-type]
1818
ext_specs = extfinder.find_spec("image")
19+
20+
if os.name == 'nt':
21+
# Load the image extension using LoadLibraryExW
22+
import ctypes
23+
import sys
24+
25+
kernel32 = ctypes.WinDLL('kernel32.dll', use_last_error=True)
26+
with_load_library_flags = hasattr(kernel32, 'AddDllDirectory')
27+
prev_error_mode = kernel32.SetErrorMode(0x0001)
28+
29+
kernel32.LoadLibraryW.restype = ctypes.c_void_p
30+
if with_load_library_flags:
31+
kernel32.LoadLibraryExW.restype = ctypes.c_void_p
32+
33+
if ext_specs is not None:
34+
res = kernel32.LoadLibraryExW(ext_specs.origin, None, 0x00001100)
35+
if res is None:
36+
err = ctypes.WinError(ctypes.get_last_error())
37+
err.strerror += (f' Error loading "{ext_specs.origin}" or any or '
38+
'its dependencies.')
39+
raise err
40+
41+
kernel32.SetErrorMode(prev_error_mode)
42+
1943
if ext_specs is not None:
2044
torch.ops.load_library(ext_specs.origin)
2145
_HAS_IMAGE_OPT = True

0 commit comments

Comments
 (0)