gh-83424: Allow empty name if handle is non-null when create ctypes.CDLL on Windows (GH-136878)

This commit is contained in:
AN Long 2025-09-06 00:51:35 +09:00 committed by GitHub
parent 9158bcf86b
commit ed522ed211
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 51 additions and 36 deletions

View file

@ -108,7 +108,7 @@ class CFunctionType(_CFuncPtr):
return CFunctionType
if _os.name == "nt":
from _ctypes import LoadLibrary as _dlopen
from _ctypes import LoadLibrary as _LoadLibrary
from _ctypes import FUNCFLAG_STDCALL as _FUNCFLAG_STDCALL
_win_functype_cache = {}
@ -410,52 +410,59 @@ def __init__(self, name, mode=DEFAULT_MODE, handle=None,
use_errno=False,
use_last_error=False,
winmode=None):
class _FuncPtr(_CFuncPtr):
_flags_ = self._func_flags_
_restype_ = self._func_restype_
if use_errno:
_flags_ |= _FUNCFLAG_USE_ERRNO
if use_last_error:
_flags_ |= _FUNCFLAG_USE_LASTERROR
self._FuncPtr = _FuncPtr
if name:
name = _os.fspath(name)
self._handle = self._load_library(name, mode, handle, winmode)
if _os.name == "nt":
def _load_library(self, name, mode, handle, winmode):
if winmode is None:
import nt as _nt
winmode = _nt._LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
# WINAPI LoadLibrary searches for a DLL if the given name
# is not fully qualified with an explicit drive. For POSIX
# compatibility, and because the DLL search path no longer
# contains the working directory, begin by fully resolving
# any name that contains a path separator.
if name is not None and ('/' in name or '\\' in name):
name = _nt._getfullpathname(name)
winmode |= _nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
self._name = name
if handle is not None:
return handle
return _LoadLibrary(self._name, winmode)
else:
def _load_library(self, name, mode, handle, winmode):
# If the filename that has been provided is an iOS/tvOS/watchOS
# .fwork file, dereference the location to the true origin of the
# binary.
if name.endswith(".fwork"):
if name and name.endswith(".fwork"):
with open(name) as f:
name = _os.path.join(
_os.path.dirname(_sys.executable),
f.read().strip()
)
self._name = name
flags = self._func_flags_
if use_errno:
flags |= _FUNCFLAG_USE_ERRNO
if use_last_error:
flags |= _FUNCFLAG_USE_LASTERROR
if _sys.platform.startswith("aix"):
"""When the name contains ".a(" and ends with ")",
e.g., "libFOO.a(libFOO.so)" - this is taken to be an
archive(member) syntax for dlopen(), and the mode is adjusted.
Otherwise, name is presented to dlopen() as a file argument.
"""
if name and name.endswith(")") and ".a(" in name:
mode |= ( _os.RTLD_MEMBER | _os.RTLD_NOW )
if _os.name == "nt":
if winmode is not None:
mode = winmode
else:
import nt
mode = nt._LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
if '/' in name or '\\' in name:
self._name = nt._getfullpathname(self._name)
mode |= nt._LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
class _FuncPtr(_CFuncPtr):
_flags_ = flags
_restype_ = self._func_restype_
self._FuncPtr = _FuncPtr
if handle is None:
self._handle = _dlopen(self._name, mode)
else:
self._handle = handle
if _sys.platform.startswith("aix"):
"""When the name contains ".a(" and ends with ")",
e.g., "libFOO.a(libFOO.so)" - this is taken to be an
archive(member) syntax for dlopen(), and the mode is adjusted.
Otherwise, name is presented to dlopen() as a file argument.
"""
if name and name.endswith(")") and ".a(" in name:
mode |= _os.RTLD_MEMBER | _os.RTLD_NOW
self._name = name
return _dlopen(name, mode)
def __repr__(self):
return "<%s '%s', handle %x at %#x>" % \

View file

@ -100,6 +100,12 @@ def test_load_ordinal_functions(self):
self.assertRaises(AttributeError, dll.__getitem__, 1234)
@unittest.skipUnless(os.name == "nt", 'Windows-specific test')
def test_load_without_name_and_with_handle(self):
handle = ctypes.windll.kernel32._handle
lib = ctypes.WinDLL(name=None, handle=handle)
self.assertIs(handle, lib._handle)
@unittest.skipUnless(os.name == "nt", 'Windows-specific test')
def test_1703286_A(self):
# On winXP 64-bit, advapi32 loads at an address that does

View file

@ -0,0 +1,2 @@
Allows creating a :class:`ctypes.CDLL` without name when passing a handle as
an argument.