1717import win32com .test .util
1818import win32timezone
1919import winerror
20- from win32com .client import VARIANT , CastTo , DispatchBaseClass , constants
20+ from win32com .client import (
21+ VARIANT ,
22+ CastTo ,
23+ DispatchBaseClass ,
24+ constants ,
25+ Record ,
26+ register_record_class ,
27+ )
2128
2229importMsg = "**** PyCOMTest is not installed ***\n PyCOMTest is a Python test specific COM client and server.\n It is likely this server is not installed on this machine\n To install the server, you must get the win32com sources\n and build it using MS Visual C++"
2330
4552verbose = 0
4653
4754
55+ # Subclasses of pythoncom.com_record.
56+ # Registration is performed in 'TestGenerated'.
57+ class TestStruct1 (pythoncom .com_record ):
58+ __slots__ = tuple ()
59+ TLBID = "{6BCDCB60-5605-11D0-AE5F-CADD4C000000}"
60+ MJVER = 1
61+ MNVER = 1
62+ LCID = 0
63+ GUID = "{7A4CE6A7-7959-4E85-A3C0-B41442FF0F67}"
64+
65+
66+ class TestStruct2 (pythoncom .com_record ):
67+ __slots__ = tuple ()
68+ TLBID = "{6BCDCB60-5605-11D0-AE5F-CADD4C000000}"
69+ MJVER = 1
70+ MNVER = 1
71+ LCID = 0
72+ GUID = "{78F0EA07-B7CF-42EA-A251-A4C6269F76AF}"
73+
74+
75+ # We don't need to stick with the struct name in the TypeLibrry for the subclass name.
76+ # The following class has the same GUID as TestStruct2 from the TypeLibrary.
77+ class ArrayOfStructsTestStruct (pythoncom .com_record ):
78+ __slots__ = tuple ()
79+ TLBID = "{6BCDCB60-5605-11D0-AE5F-CADD4C000000}"
80+ MJVER = 1
81+ MNVER = 1
82+ LCID = 0
83+ GUID = "{78F0EA07-B7CF-42EA-A251-A4C6269F76AF}"
84+
85+
86+ class NotInTypeLibraryTestStruct (pythoncom .com_record ):
87+ __slots__ = tuple ()
88+ TLBID = "{6BCDCB60-5605-11D0-AE5F-CADD4C000000}"
89+ MJVER = 1
90+ MNVER = 1
91+ LCID = 0
92+ GUID = "{79BB6AC3-12DE-4AC5-88AC-225C29A58043}"
93+
94+
4895def check_get_set (func , arg ):
4996 got = func (arg )
5097 assert got == arg , f"{ func } failed - expected { arg !r} , got { got !r} "
@@ -198,18 +245,6 @@ def TestCommon(o, is_generated):
198245 progress ("Checking structs" )
199246 r = o .GetStruct ()
200247 assert r .int_value == 99 and str (r .str_value ) == "Hello from C++"
201- # Dynamic does not support struct byref as [ in, out ] parameters
202- if hasattr (o , "CLSID" ):
203- progress ("Checking struct byref as [ in, out ] parameter" )
204- mod_r = o .ModifyStruct (r )
205- # We expect the input value to stay unchanged
206- assert r .int_value == 99 and str (r .str_value ) == "Hello from C++"
207- # and the return value to reflect the modifications performed on the COM server side
208- assert (
209- mod_r .int_value == 100
210- and str (mod_r .str_value ) == "Nothing is as constant as change"
211- )
212-
213248 assert o .DoubleString ("foo" ) == "foofoo"
214249
215250 progress ("Checking var args" )
@@ -409,9 +444,46 @@ def TestDynamic():
409444 # assert o.ParamProp(0) == 1, o.paramProp(0)
410445
411446
447+ def TestStructByref (o , r ):
448+ progress ("Checking struct byref as [ in, out ] parameter" )
449+ mod_r = o .ModifyStruct (r )
450+ # If 'TestStruct1' was registered as an instantiable subclass
451+ # of pythoncom.com_record, the return value should have this type.
452+ if isinstance (r , TestStruct1 ):
453+ assert type (mod_r ) is TestStruct1
454+ else :
455+ assert type (mod_r ) is pythoncom .com_record
456+ # We expect the input value to stay unchanged
457+ assert r .int_value == 99 and str (r .str_value ) == "Hello from C++"
458+ # and the return value to reflect the modifications performed on the COM server side
459+ assert (
460+ mod_r .int_value == 100
461+ and str (mod_r .str_value ) == "Nothing is as constant as change"
462+ )
463+
464+
465+ def TestArrayOfStructs (o , test_rec ):
466+ progress ("Testing struct with SAFEARRAY(VT_RECORD) fields." )
467+ rec_list = []
468+ for i in range (3 ):
469+ # If 'ArrayOfStructsTestStruct' and 'TestStruct1' were registered as instantiable
470+ # subclasses of pythoncom.com_record, we expect to work with these types.
471+ if isinstance (test_rec , ArrayOfStructsTestStruct ):
472+ rec = TestStruct1 ()
473+ assert type (rec ) is TestStruct1
474+ else :
475+ rec = Record ("TestStruct1" , o )
476+ assert type (rec ) is pythoncom .com_record
477+ rec .str_value = "This is record number"
478+ rec .int_value = i + 1
479+ rec_list .append (rec )
480+ test_rec .array_of_records = rec_list
481+ test_rec .rec_count = i + 1
482+ assert o .VerifyArrayOfStructs (test_rec )
483+
484+
412485def TestGenerated ():
413486 # Create an instance of the server.
414- from win32com .client import Record
415487 from win32com .client .gencache import EnsureDispatch
416488
417489 o = EnsureDispatch ("PyCOMTest.PyCOMTest" )
@@ -434,18 +506,45 @@ def TestGenerated():
434506 coclass = GetClass ("{B88DD310-BAE8-11D0-AE86-76F2C1000000}" )()
435507 TestCounter (coclass , True )
436508
437- # Test records with SAFEARRAY(VT_RECORD) fields.
438- progress ("Testing records with SAFEARRAY(VT_RECORD) fields." )
439- l = []
440- for i in range (3 ):
441- rec = Record ("TestStruct1" , o )
442- rec .str_value = "This is record number"
443- rec .int_value = i + 1
444- l .append (rec )
509+ # Test plain pythoncom.com_record structs.
510+ progress ("Testing baseclass pythoncom.com_record structs." )
511+ r = o .GetStruct ()
512+ assert type (r ) is pythoncom .com_record
513+ TestStructByref (o , r )
445514 test_rec = Record ("TestStruct2" , o )
446- test_rec .array_of_records = l
447- test_rec .rec_count = i + 1
448- assert o .VerifyArrayOfStructs (test_rec )
515+ assert type (test_rec ) is pythoncom .com_record
516+ TestArrayOfStructs (o , test_rec )
517+
518+ progress ("Testing registration of pythoncom.com_record subclasses." )
519+ # Instantiating a pythoncom.com_record subclass, which has proper GUID attributes,
520+ # does return an instance of the base class, as long as we have not registered it.
521+ r_base = TestStruct1 ()
522+ assert type (r_base ) is pythoncom .com_record
523+ # Register the subclasses in pythoncom.
524+ register_record_class (TestStruct1 )
525+ register_record_class (ArrayOfStructsTestStruct )
526+ # Now the type of the instance is the registered subclass.
527+ r_sub = TestStruct1 ()
528+ assert type (r_sub ) is TestStruct1
529+ # Now also the 'Record' factory function returns an instance of the registered subtype.
530+ r_sub = Record ("TestStruct1" , o )
531+ assert type (r_sub ) is TestStruct1
532+ # It should not be possible to register multiple classes with the same GUID, e.g.
533+ # 'TestStruct2' has the same GUID class attribute value as 'ArrayOfStructsTestStruct'.
534+ check_get_set_raises (ValueError , register_record_class , TestStruct2 )
535+ # Also registering a class with a GUID that is not in the TypeLibrary should fail.
536+ check_get_set_raises (TypeError , register_record_class , NotInTypeLibraryTestStruct )
537+
538+ # Perform the 'Byref' and 'ArrayOfStruct tests using the registered subclasses.
539+ progress ("Testing subclasses of pythoncom.com_record." )
540+ r = o .GetStruct ()
541+ # After 'TestStruct1' was registered as an instantiable subclass
542+ # of pythoncom.com_record, the return value should have this type.
543+ assert type (r ) is TestStruct1
544+ TestStructByref (o , r )
545+ test_rec = ArrayOfStructsTestStruct ()
546+ assert type (test_rec ) is ArrayOfStructsTestStruct
547+ TestArrayOfStructs (o , test_rec )
449548
450549 # XXX - this is failing in dynamic tests, but should work fine.
451550 i1 , i2 = o .GetMultipleInterfaces ()
0 commit comments