Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions comtypes/automation.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,11 @@ def _set_value(self, value):
ri.AddRef()
self._.pRecInfo = ri
self._.pvRecord = cast(value, c_void_p)
elif isinstance(ref, _Pointer) and isinstance(
ref.contents, _safearray.tagSAFEARRAY
):
self.vt = VT_ARRAY | ref._vartype_ | VT_BYREF
self._.pparray = cast(value, POINTER(POINTER(_safearray.tagSAFEARRAY)))
else:
self.vt = _ctype_to_vartype[type(ref)] | VT_BYREF
elif isinstance(value, _Pointer):
Expand All @@ -422,6 +427,15 @@ def _set_value(self, value):
ri.AddRef()
self._.pRecInfo = ri
self._.pvRecord = cast(value, c_void_p)
elif isinstance(ref, _safearray.tagSAFEARRAY):
obj = _midlSAFEARRAY(value._itemtype_).create(value.unpack())
memmove(byref(self._), byref(obj), sizeof(obj))
self.vt = VT_ARRAY | obj._vartype_
elif isinstance(ref, _Pointer) and isinstance(
ref.contents, _safearray.tagSAFEARRAY
):
self.vt = VT_ARRAY | ref._vartype_ | VT_BYREF
self._.pparray = cast(value, POINTER(POINTER(_safearray.tagSAFEARRAY)))
else:
self.vt = _ctype_to_vartype[type(ref)] | VT_BYREF
else:
Expand Down
89 changes: 89 additions & 0 deletions comtypes/test/test_dispifc_safearrays.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# coding: utf-8

from ctypes import byref, c_double, pointer
import unittest

import comtypes
import comtypes.safearray
from comtypes import CLSCTX_LOCAL_SERVER
from comtypes.client import CreateObject, GetModule

ComtypesCppTestSrvLib_GUID = "{07D2AEE5-1DF8-4D2C-953A-554ADFD25F99}"

try:
GetModule((ComtypesCppTestSrvLib_GUID, 1, 0, 0))
import comtypes.gen.ComtypesCppTestSrvLib as ComtypesCppTestSrvLib

IMPORT_FAILED = False
except (ImportError, OSError):
IMPORT_FAILED = True


@unittest.skipIf(IMPORT_FAILED, "This depends on the out of process COM-server.")
class Test_DispMethods(unittest.TestCase):
"""Test dispmethods with safearray and safearray pointer parameters."""

UNPACKED_ZERO_VALS = tuple(0.0 for _ in range(10))
UNPACKED_CONSECUTIVE_VALS = tuple(float(i) for i in range(10))

def _create_dispifc(self) -> "ComtypesCppTestSrvLib.IDispSafearrayParamTest":
# Explicitely ask for the dispinterface of the component.
return CreateObject(
"Comtypes.DispSafearrayParamTest",
clsctx=CLSCTX_LOCAL_SERVER,
interface=ComtypesCppTestSrvLib.IDispSafearrayParamTest,
)

def _create_zero_array(self):
return comtypes.safearray._midlSAFEARRAY(c_double).create(
[c_double() for _ in range(10)]
)

def _create_consecutive_array(self):
return comtypes.safearray._midlSAFEARRAY(c_double).create(
[c_double(i) for i in range(10)]
)

def test_inout_byref(self):
dispifc = self._create_dispifc()
# Passing a safearray by reference to a method that has declared the parameter
# as [in, out] we expect modifications of the safearray on the server side to
# also change the safearray on the client side.
test_array = self._create_zero_array()
self.assertEqual(test_array.unpack(), self.UNPACKED_ZERO_VALS)
dispifc.InitArray(byref(test_array))
self.assertEqual(test_array.unpack(), self.UNPACKED_CONSECUTIVE_VALS)

def test_inout_pointer(self):
dispifc = self._create_dispifc()
# Passing a safearray pointer to a method that has declared the parameter
# as [in, out] we expect modifications of the safearray on the server side to
# also change the safearray on the client side.
test_array = self._create_zero_array()
self.assertEqual(test_array.unpack(), self.UNPACKED_ZERO_VALS)
dispifc.InitArray(pointer(test_array))
self.assertEqual(test_array.unpack(), self.UNPACKED_CONSECUTIVE_VALS)

def test_in_safearray(self):
# Passing a safearray to a method that has declared the parameter just as [in]
# we expect modifications of the safearray on the server side NOT to change
# the safearray on the client side.
# We also need to test if the safearray gets properly passed to the method on
# the server side. For this, the 'VerifyArray' method returns 'True' if
# the safearray items have values equal to the initialization values
# provided by 'InitArray'.
for sa, expected, unpacked_content in [
(self._create_consecutive_array(), True, self.UNPACKED_CONSECUTIVE_VALS),
# Also perform the inverted test. For this, create a safearray with zeros.
(self._create_zero_array(), False, self.UNPACKED_ZERO_VALS),
]:
with self.subTest(expected=expected, unpacked_content=unpacked_content):
# Perform the check on initialization values.
self.assertEqual(self._create_dispifc().VerifyArray(sa), expected)
# Check if the safearray is unchanged although the method
# modifies the safearray on the server side.
self.assertEqual(sa.unpack(), unpacked_content)


if __name__ == "__main__":
unittest.main()
16 changes: 15 additions & 1 deletion source/CppTestSrv/CFACTORY.CPP
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,21 @@ HRESULT CFactory::UnregisterAll()
{
UnregisterServer(*(g_FactoryDataArray[i].m_pCLSID),
g_FactoryDataArray[i].m_szVerIndProgID,
g_FactoryDataArray[i].m_szProgID) ;
g_FactoryDataArray[i].m_szProgID,
g_FactoryDataArray[i].m_pLIBID) ;
// We can only unregister a TypeLib once.
// So if we just unregistered a TypeLib set all pointers to the same TypeLib to NULL.
if (g_FactoryDataArray[i].m_pLIBID != NULL)
{
const GUID* libid = g_FactoryDataArray[i].m_pLIBID ;
for(int j = 0 ; j < g_cFactoryDataEntries ; j++)
{
if (g_FactoryDataArray[j].m_pLIBID != NULL && g_FactoryDataArray[j].m_pLIBID == libid)
{
g_FactoryDataArray[j].m_pLIBID = NULL ;
}
}
}
}
return S_OK ;
}
Expand Down
Loading