Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
junkmd committed Sep 23, 2024
1 parent a3a8733 commit 7c100ac
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 101 deletions.
1 change: 1 addition & 0 deletions comtypes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ def CoUninitialize():
# IUnknown, the root of all evil...
from comtypes._post_coinit import _shutdown
from comtypes._post_coinit.unknwn import IUnknown # noqa
from comtypes._post_coinit.base import _compointer_base # noqa

atexit.register(_shutdown)

Expand Down
105 changes: 105 additions & 0 deletions comtypes/_post_coinit/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from ctypes import c_void_p
import logging

from comtypes._post_coinit.unknwn import _cominterface_meta


logger = logging.getLogger(__name__)


class _compointer_meta(type(c_void_p), _cominterface_meta):
"metaclass for COM interface pointer classes"
# no functionality, but needed to avoid a metaclass conflict


class _compointer_base(c_void_p, metaclass=_compointer_meta):
"base class for COM interface pointer classes"

def __del__(self, _debug=logger.debug):
"Release the COM refcount we own."
if self:
# comtypes calls CoUninitialize() when the atexit handlers
# runs. CoUninitialize() cleans up the COM objects that
# are still alive. Python COM pointers may still be
# present but we can no longer call Release() on them -
# this may give a protection fault. So we need the
# _com_shutting_down flag.
#
if not type(self)._com_shutting_down:
_debug("Release %s", self)
self.Release()

def __cmp__(self, other):
"""Compare pointers to COM interfaces."""
# COM identity rule
#
# XXX To compare COM interface pointers, should we
# automatically QueryInterface for IUnknown on both items, and
# compare the pointer values?
if not isinstance(other, _compointer_base):
return 1

# get the value property of the c_void_p baseclass, this is the pointer value
return cmp(
super(_compointer_base, self).value, super(_compointer_base, other).value
)

def __eq__(self, other):
if not isinstance(other, _compointer_base):
return False
# get the value property of the c_void_p baseclass, this is the pointer value
return (
super(_compointer_base, self).value == super(_compointer_base, other).value
)

def __hash__(self):
"""Return the hash value of the pointer."""
# hash the pointer values
return hash(super(_compointer_base, self).value)

# redefine the .value property; return the object itself.
def __get_value(self):
return self

value = property(__get_value, doc="""Return self.""")

def __repr__(self):
ptr = super(_compointer_base, self).value
return "<%s ptr=0x%x at %x>" % (self.__class__.__name__, ptr or 0, id(self))

# This fixes the problem when there are multiple python interface types
# wrapping the same COM interface. This could happen because some interfaces
# are contained in multiple typelibs.
#
# It also allows to pass a CoClass instance to an api
# expecting a COM interface.
@classmethod
def from_param(cls, value):
"""Convert 'value' into a COM pointer to the interface.
This method accepts a COM pointer, or a CoClass instance
which is QueryInterface()d."""
if value is None:
return None
# CLF: 2013-01-18
# A default value of 0, meaning null, can pass through to here.
if value == 0:
return None
if isinstance(value, cls):
return value
# multiple python interface types for the same COM interface.
# Do we need more checks here?
if cls._iid_ == getattr(value, "_iid_", None):
return value
# Accept an CoClass instance which exposes the interface required.
try:
table = value._com_pointers_
except AttributeError:
pass
else:
try:
# a kind of QueryInterface
return table[cls._iid_]
except KeyError:
raise TypeError("Interface %s not supported" % cls._iid_)
return value.QueryInterface(cls.__com_interface__)
102 changes: 1 addition & 101 deletions comtypes/_post_coinit/unknwn.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def __new__(cls, name, bases, namespace):
# then we need to make sure that POINTER(IDispatch) is a
# subclass of POINTER(IUnknown) because of the way ctypes
# typechecks work.
from comtypes._post_coinit.base import _compointer_base
if bases == (object,):
_ptr_bases = (new_cls, _compointer_base)
else:
Expand Down Expand Up @@ -360,107 +361,6 @@ def _make_methods(self, methods: List[_ComMemberSpec]) -> None:
self.__map_case__[name.lower()] = name


################################################################


class _compointer_meta(type(c_void_p), _cominterface_meta):
"metaclass for COM interface pointer classes"
# no functionality, but needed to avoid a metaclass conflict


class _compointer_base(c_void_p, metaclass=_compointer_meta):
"base class for COM interface pointer classes"

def __del__(self, _debug=logger.debug):
"Release the COM refcount we own."
if self:
# comtypes calls CoUninitialize() when the atexit handlers
# runs. CoUninitialize() cleans up the COM objects that
# are still alive. Python COM pointers may still be
# present but we can no longer call Release() on them -
# this may give a protection fault. So we need the
# _com_shutting_down flag.
#
if not type(self)._com_shutting_down:
_debug("Release %s", self)
self.Release()

def __cmp__(self, other):
"""Compare pointers to COM interfaces."""
# COM identity rule
#
# XXX To compare COM interface pointers, should we
# automatically QueryInterface for IUnknown on both items, and
# compare the pointer values?
if not isinstance(other, _compointer_base):
return 1

# get the value property of the c_void_p baseclass, this is the pointer value
return cmp(
super(_compointer_base, self).value, super(_compointer_base, other).value
)

def __eq__(self, other):
if not isinstance(other, _compointer_base):
return False
# get the value property of the c_void_p baseclass, this is the pointer value
return (
super(_compointer_base, self).value == super(_compointer_base, other).value
)

def __hash__(self):
"""Return the hash value of the pointer."""
# hash the pointer values
return hash(super(_compointer_base, self).value)

# redefine the .value property; return the object itself.
def __get_value(self):
return self

value = property(__get_value, doc="""Return self.""")

def __repr__(self):
ptr = super(_compointer_base, self).value
return "<%s ptr=0x%x at %x>" % (self.__class__.__name__, ptr or 0, id(self))

# This fixes the problem when there are multiple python interface types
# wrapping the same COM interface. This could happen because some interfaces
# are contained in multiple typelibs.
#
# It also allows to pass a CoClass instance to an api
# expecting a COM interface.
@classmethod
def from_param(cls, value):
"""Convert 'value' into a COM pointer to the interface.
This method accepts a COM pointer, or a CoClass instance
which is QueryInterface()d."""
if value is None:
return None
# CLF: 2013-01-18
# A default value of 0, meaning null, can pass through to here.
if value == 0:
return None
if isinstance(value, cls):
return value
# multiple python interface types for the same COM interface.
# Do we need more checks here?
if cls._iid_ == getattr(value, "_iid_", None):
return value
# Accept an CoClass instance which exposes the interface required.
try:
table = value._com_pointers_
except AttributeError:
pass
else:
try:
# a kind of QueryInterface
return table[cls._iid_]
except KeyError:
raise TypeError("Interface %s not supported" % cls._iid_)
return value.QueryInterface(cls.__com_interface__)


################################################################
# IUnknown, the root of all evil...

Expand Down

0 comments on commit 7c100ac

Please sign in to comment.