comtypes icon indicating copy to clipboard operation
comtypes copied to clipboard

Problems that arise when remote functions are defined in interfaces related to structured storage.

Open junkmd opened this issue 1 year ago • 1 comments

Some of methods in the interfaces generated by GetModule are based on remote-side information, which makes them uncallable from the local side.

See also #604.

IStorage

OpenStream and RemoteOpenStream

cbReserved1 is not needed when calling from the local side. If we specify cbReserved1 as 0 and call from the local side, a COMError with the error code STG_E_INVALIDPOINTER (-2147287031, 0x80030009) will be raised.

EnumElements and RemoteEnumElements

cbReserved2 is not needed when calling from the local side. If we specify cbReserved2 as 0 and call from the local side, a COMError with the error code STG_E_INVALIDPOINTER (-2147287031, 0x80030009) will be raised.

IEnumSTATSTG

Next and RemoteNext

Because RemoteNext is defined instead of Next, __iter__, __next__, and __getitem__ are not implemented in the interface, so it does NOT become an iterator or index-accessible object.

Appendix

CopyTo and RemoteCopyTo of IStorage and CopyTo and RemoteCopyTo of IStream have the same parameters on both the remote side and the local side, so it is possible to call the method even if it is defined based on the information from RemoteCopyTo.

junkmd avatar Aug 26 '24 23:08 junkmd

Reproducers

We can reproduce the COMError by adding these tests to test_storage.Test_IStorage.

    def test_RemoteOpenStream(self):
        storage = self._create_docfile()
        created_stream = storage.CreateStream("example", self.CREATE_STM_FLAG, 0, 0)
        test_data = "Some data".encode("utf-8")
        pv = (c_ubyte * len(test_data)).from_buffer(bytearray(test_data))
        created_stream.RemoteWrite(pv, len(test_data))
        created_stream.Commit(STGC_DEFAULT)
        del created_stream
        storage.RemoteOpenStream("example", 0, None, self.OPEN_STM_FLAG, 0)  # error

    def test_RemoteEnumElements(self):
        storage = self._create_docfile()
        storage.RemoteEnumElements(0, 0, None, 0)  # error
======================================================================
ERROR: test_RemoteEnumElements (test_storage.Test_IStorage)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\a\comtypes\comtypes\comtypes\test\test_storage.py", line 141, in test_RemoteEnumElements
    storage.RemoteEnumElements(0, 0, None, 0)  # error
_ctypes.COMError: (-2147287031, 'Invalid pointer error.', (None, None, None, 0, None))

======================================================================
ERROR: test_RemoteOpenStream (test_storage.Test_IStorage)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "D:\a\comtypes\comtypes\comtypes\test\test_storage.py", line 137, in test_RemoteOpenStream
    storage.RemoteOpenStream("example", 0, None, self.OPEN_STM_FLAG, 0)  # error
_ctypes.COMError: (-2147287031, 'Invalid pointer error.', (None, None, None, 0, None))

Since the return value of RemoteEnumElements cannot be obtained, it is not possible to test whether IEnumSTATSTG works as an iterator. However, by looking at the generated wrapper module, it can be confirmed that __iter__ is not present.

class IEnumSTATSTG(comtypes.gen._00020430_0000_0000_C000_000000000046_0_2_0.IUnknown):
    _case_insensitive_ = True
    _iid_ = GUID('{0000000D-0000-0000-C000-000000000046}')
    _idlflags_ = []

    if TYPE_CHECKING:  # commembers
        def RemoteNext(self, celt: hints.Incomplete) -> hints.Tuple[hints.Incomplete, hints.Incomplete]: ...
        def Skip(self, celt: hints.Incomplete) -> hints.Hresult: ...
        def Reset(self) -> hints.Hresult: ...
        def Clone(self) -> 'IEnumSTATSTG': ...
IEnumSTATSTG._methods_ = [
    COMMETHOD(
        [],
        HRESULT,
        'RemoteNext',
        (['in'], c_ulong, 'celt'),
        (['out'], POINTER(tagSTATSTG), 'rgelt'),
        (['out'], POINTER(c_ulong), 'pceltFetched')
    ),
    COMMETHOD(
        [],
        HRESULT,
        'Skip',
        (['in'], c_ulong, 'celt')
    ),
    COMMETHOD([], HRESULT, 'Reset'),
    COMMETHOD(
        [],
        HRESULT,
        'Clone',
        (['out'], POINTER(POINTER(IEnumSTATSTG)), 'ppenum')
    ),
]

junkmd avatar Aug 26 '24 23:08 junkmd