mirror of
https://github.com/python/cpython.git
synced 2025-12-23 09:19:18 +00:00
[3.13] gh-83424: Allow empty name if handle is non-null when create ctypes.CDLL on Windows (GH-136878) (#138547)
gh-83424: Allow empty name if handle is non-null when create ctypes.CDLL on Windows (GH-136878)
(cherry picked from commit ed522ed211)
Co-authored-by: AN Long <aisk@users.noreply.github.com>
This commit is contained in:
parent
15a2cd6a1d
commit
12ada79fe9
3 changed files with 51 additions and 36 deletions
|
|
@ -107,7 +107,7 @@ def CFUNCTYPE(restype, *argtypes, **kw):
|
|||
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 = {}
|
||||
|
|
@ -344,52 +344,59 @@ class CDLL(object):
|
|||
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>" % \
|
||||
|
|
|
|||
|
|
@ -100,6 +100,12 @@ class LoaderTest(unittest.TestCase):
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
Allows creating a :class:`ctypes.CDLL` without name when passing a handle as
|
||||
an argument.
|
||||
Loading…
Add table
Add a link
Reference in a new issue