@@ -34,15 +34,15 @@ class ICallFrameEventsImplemSimple(windows.com.COMImplementation):
3434
3535 def __init__ (self ):
3636 super (ICallFrameEventsImplemSimple , self ).__init__ ()
37- self .called = 0
37+ self .oncall = 0
3838
3939 def OnCall (self , This , pFrame ):
40- self .called += 1
41- return self .called
40+ self .oncall += 1
41+ return self .oncall
4242
4343def test_com_implementation_simple ():
4444 obj = ICallFrameEventsImplemSimple ()
45- assert obj .com_refcount == 1
45+ assert obj ._com_refcount == 1
4646 assert obj .AddRef () == 2
4747 assert obj .Release () == 1
4848 assert id (obj ) in obj ._get_keepalive_registry ()
@@ -55,7 +55,7 @@ def test_com_implementation_query_interface():
5555 # Emulate a call from C code
5656 obj .QueryInterface (obj ._as_parameter_ , ctypes .pointer (gdef .IUnknown .IID ), ctypes .pointer (iunk ))
5757 assert obj ._as_parameter_ == iunk .value
58- assert obj .com_refcount == 2 # +1 on QueryInterface
58+ assert obj ._com_refcount == 2 # +1 on QueryInterface
5959 assert iunk .Release () == 1 # We are now working via ctypes/vtable call
6060
6161def test_com_implementation_keep_alive ():
@@ -75,3 +75,129 @@ def test_com_implementation_keep_alive():
7575 assert objcom .Release () == 0 # trigger Revoke / free !
7676 gc .collect ()
7777 assert wref () is None # Check object is dead in python world
78+
79+
80+ # Test multiple interface code
81+
82+ class SimpleMultipleInterfaceImplem (windows .com .COMImplementation ):
83+ IMPLEMENT = [
84+ gdef .ICallFrameEvents , # OnCall
85+ gdef .IPersist # GetClassID
86+ ]
87+
88+ def __init__ (self ):
89+ super (SimpleMultipleInterfaceImplem , self ).__init__ ()
90+ self .oncall = 0
91+ self .getclassid = 0
92+ self .global_call = 0 # Proof this is the same object
93+
94+ def OnCall (self , This , pFrame ):
95+ self .oncall += 1
96+ self .global_call += 1
97+ return self .oncall
98+
99+ def GetClassID (self , This , pClassID ):
100+ self .getclassid += 1
101+ self .global_call += 1
102+ return self .getclassid
103+
104+ def test_com_multiple_implementation_no_collision ():
105+ obj = SimpleMultipleInterfaceImplem ()
106+ with pytest .raises (ValueError ):
107+ x = obj ._as_parameter_ # _as_parameter_ cannot be ask directly on object implementing multiple interfaces
108+
109+ # Create a C-like pointer from the interface
110+ objcom = gdef .ICallFrameEvents (obj .as_interface (gdef .ICallFrameEvents ))
111+ assert objcom .OnCall (None ) == 1 # Count the number of call : proof of correct python-side execution
112+ assert objcom .OnCall (None ) == 2
113+ assert obj .global_call == 2
114+
115+ objcom2 = gdef .IPersist (obj .as_interface (gdef .IPersist ))
116+ assert objcom2 .GetClassID (None ) == 1 # Count the number of call : proof of correct python-side execution
117+ assert objcom2 .GetClassID (None ) == 2
118+ assert obj .global_call == 4
119+
120+
121+
122+ def test_com_multiple_implementation_query_interface ():
123+ obj = SimpleMultipleInterfaceImplem ()
124+ iunk = gdef .IUnknown ()
125+ # Emulate a call from C code
126+ obj .QueryInterface (obj .as_interface (gdef .ICallFrameEvents ), ctypes .pointer (gdef .IUnknown .IID ), ctypes .pointer (iunk ))
127+ assert obj ._com_refcount == 2
128+ assert iunk .value
129+ assert iunk .value in obj ._vtables_ptr .values ()
130+ # Use the standard C/ctypes Iunknown pointer to query other types and verify pointer/refcount
131+ assert iunk .query (gdef .ICallFrameEvents ).value == obj ._vtables_ptr [str (gdef .ICallFrameEvents .IID )]
132+ assert obj ._com_refcount == 3
133+ assert iunk .query (gdef .IPersist ).value == obj ._vtables_ptr [str (gdef .IPersist .IID )]
134+ assert obj ._com_refcount == 4
135+
136+
137+ class CollisionMultipleInterfaceImplem (windows .com .COMImplementation ):
138+ # Both share the same root & have "Invoke"
139+ IMPLEMENT = [
140+ gdef .IIdleSettings , # Invoke
141+ gdef .ITaskFolderCollection , # Invoke
142+ ]
143+
144+ def __init__ (self ):
145+ super (CollisionMultipleInterfaceImplem , self ).__init__ ()
146+ self .iidlesettings_invoke = 0
147+ self .itaskfoldercollection_invoke = 0
148+
149+ def IIdleSettings_Invoke (self , This , dispIdMember , riid , lcid , wFlags , pDispParams , pVarResult , pExcepInfo , puArgErr ):
150+ print ('IIdleSettings.Invoke' )
151+ self .iidlesettings_invoke += 1
152+ return self .iidlesettings_invoke
153+
154+ def ITaskFolderCollection_Invoke (self , This , dispIdMember , riid , lcid , wFlags , pDispParams , pVarResult , pExcepInfo , puArgErr ):
155+ print ('ITaskFolderCollection.Invoke' )
156+ self .itaskfoldercollection_invoke += 1
157+ return self .itaskfoldercollection_invoke
158+
159+ def not_implemented (self , * args ):
160+ return gdef .E_NOTIMPL
161+
162+ # IIdleSettings
163+
164+ IIdleSettings_GetTypeInfoCount = not_implemented
165+ IIdleSettings_GetTypeInfo = not_implemented
166+ IIdleSettings_GetIDsOfNames = not_implemented
167+ get_IdleDuration = not_implemented
168+ put_IdleDuration = not_implemented
169+ get_WaitTimeout = not_implemented
170+ put_WaitTimeout = not_implemented
171+ get_StopOnIdleEnd = not_implemented
172+ put_StopOnIdleEnd = not_implemented
173+ get_RestartOnIdle = not_implemented
174+ put_RestartOnIdle = not_implemented
175+
176+ # ITaskFolderCollection
177+
178+ ITaskFolderCollection_GetTypeInfoCount = not_implemented
179+ ITaskFolderCollection_GetTypeInfo = not_implemented
180+ ITaskFolderCollection_GetIDsOfNames = not_implemented
181+ get_Count = not_implemented
182+ get_Item = not_implemented
183+ get__NewEnum = not_implemented
184+
185+
186+ def test_com_multiple_implementation_with_collision ():
187+ obj = CollisionMultipleInterfaceImplem ()
188+ assert obj
189+
190+ # Emulate a call from C code
191+ iunk = gdef .IUnknown (obj .as_interface (gdef .IUnknown ))
192+
193+ idlesetting = iunk .query (gdef .IIdleSettings )
194+ taskfoldercollection = iunk .query (gdef .ITaskFolderCollection )
195+ taskfoldercollection .Invoke (0 , None , 0 , 0 , None , None , None , None )
196+ assert obj .itaskfoldercollection_invoke == 1
197+ assert obj .iidlesettings_invoke == 0
198+ taskfoldercollection .Invoke (0 , None , 0 , 0 , None , None , None , None )
199+ assert obj .itaskfoldercollection_invoke == 2
200+ assert obj .iidlesettings_invoke == 0
201+ idlesetting .Invoke (0 , None , 0 , 0 , None , None , None , None )
202+ assert obj .itaskfoldercollection_invoke == 2
203+ assert obj .iidlesettings_invoke == 1
0 commit comments