Hi,

I am trying to apply the Siemens S7ProSim Automation object using 
Python. However, after running makepy my test script fails. I have done 
some digging into DispatchWithEvents, makepy and MSDN. It turns out that 
the event interface seems not to be included in the result file from 
makepy, while the vtable for the same is. This causes calls to 
DispatchWithEvents to fail.

I ran the MS ITypelib viewer over the dll, and also makepy. Here are the 
results.

For reference, from the OLE Viewer (some details left out):
--------------------------------------------
    // typelib filename: s7wspsmx.dll
    [
      uuid(83CC0D80-FEDA-11D1-BE76-0060B06816CF),
      version(1.0),
      helpstring("Siemens S7ProSim COM Object")
    ]
    library S7PROSIMLib
    {
        // Forward declare all types defined in this typelib
        interface IS7ProSimEvents;
        interface IS7ProSim;
   
        [
          odl,
          uuid(83CC0D82-FEDA-11D1-BE76-0060B06816CF),
          helpstring("Event interface for S7ProSim COM Object"),
          oleautomation
        ]
        interface IS7ProSimEvents : IUnknown {
            [helpstring("Fired when single scan is done.")]
            HRESULT _stdcall ScanFinished(VARIANT ScanInfo);
            [helpstring("Fired when unable to connect to Control Engine.")]
            HRESULT _stdcall ConnectionError(
                            BSTR ControlEngine,
                            long Error);
            [helpstring("Fired when a new PLC switch state is detected.")]
            HRESULT _stdcall PLCSimStateChanged(BSTR NewState);
            [helpstring("Fired when a Pause/Continue state change is 
detected.")]
            HRESULT _stdcall PauseStateChanged(BSTR NewState);
            [helpstring("Fired when a ScanMode change is detected.")]
            HRESULT _stdcall ScanModeChanged(BSTR NewState);
        };
   
        [
          uuid(83CC0D83-FEDA-11D1-BE76-0060B06816CF),
          helpstring("S7ProSim Class")
        ]
        coclass S7ProSim {
            [default] interface IS7ProSim;
            [default, source] interface IS7ProSimEvents;
        };
   
        [
          odl,
          uuid(83CC0D81-FEDA-11D1-BE76-0060B06816CF),
          helpstring("IS7ProSim Interface for S7ProSim COM Object"),
          dual,
          oleautomation
        ]
        interface IS7ProSim : IDispatch {
            (.... dispatchable functions ....)
        };
        (.... constant declarations ....)
    };

For reference, from '83CC0D80-FEDA-11D1-BE76-0060B06816CFx0x1x0.py' 
(some details left out):
------------------------------------------------------------------------------
    # -*- coding: mbcs -*-
    # Created by makepy.py version 0.4.95
    # By python version 2.4 (#60, Nov 30 2004, 11:49:19) [MSC v.1310 32 
bit (Intel)]
    # From type library 's7wspsmx.dll'
    # On Wed Jul 18 17:55:20 2007
    """Siemens S7ProSim COM Object"""
    makepy_version = '0.4.95'
    python_version = 0x20400f0
   
    (....)
       
    CLSID = IID('{83CC0D80-FEDA-11D1-BE76-0060B06816CF}')
    MajorVersion = 1
    MinorVersion = 0
    LibraryFlags = 8
    LCID = 0x0
   
    class constants:
        (....)
            
    from win32com.client import DispatchBaseClass
    class IS7ProSim(DispatchBaseClass):
        """IS7ProSim Interface for S7ProSim COM Object"""
        CLSID = IID('{83CC0D81-FEDA-11D1-BE76-0060B06816CF}')
        coclass_clsid = IID('{83CC0D83-FEDA-11D1-BE76-0060B06816CF}')
        (.... dispatchable functions ....)
           
    from win32com.client import CoClassBaseClass
    # This CoClass is known by the name 'S7wspsmx.S7ProSim.1'
    class S7ProSim(CoClassBaseClass): # A CoClass
        # S7ProSim Class
        CLSID = IID('{83CC0D83-FEDA-11D1-BE76-0060B06816CF}')
        coclass_sources = [
        ]
        coclass_interfaces = [
            IS7ProSim,
        ]
        default_interface = IS7ProSim
   
    IS7ProSim_vtables_dispatch_ = 1
    IS7ProSim_vtables_ = [
        (.... dispatchable funtions table entries ....)
    ]
   
    IS7ProSimEvents_vtables_dispatch_ = 0
    IS7ProSimEvents_vtables_ = [
        (( 'ScanFinished' , 'ScanInfo' , ), 1, (1, (), [ (12, 0, None, 
None) , ], 1 , 1 , 4 , 0 , 12 , (3, 0, None, None) , 0 , )),
        (( 'ConnectionError' , 'ControlEngine' , 'Error' , ), 2, (2, (), 
[ (8, 0, None, None) ,
                (3, 0, None, None) , ], 1 , 1 , 4 , 0 , 16 , (3, 0, 
None, None) , 0 , )),
        (( 'PLCSimStateChanged' , 'NewState' , ), 3, (3, (), [ (8, 0, 
None, None) , ], 1 , 1 , 4 , 0 , 20 , (3, 0, None, None) , 0 , )),
        (( 'PauseStateChanged' , 'NewState' , ), 4, (4, (), [ (8, 0, 
None, None) , ], 1 , 1 , 4 , 0 , 24 , (3, 0, None, None) , 0 , )),
        (( 'ScanModeChanged' , 'NewState' , ), 5, (5, (), [ (8, 0, None, 
None) , ], 1 , 1 , 4 , 0 , 28 , (3, 0, None, None) , 0 , )),
    ]
   
    RecordMap = {
    }
   
    CLSIDToClassMap = {
        '{83CC0D81-FEDA-11D1-BE76-0060B06816CF}' : IS7ProSim,
        '{83CC0D83-FEDA-11D1-BE76-0060B06816CF}' : S7ProSim,
    }
    CLSIDToPackageMap = {}
    win32com.client.CLSIDToClass.RegisterCLSIDsFromDict( CLSIDToClassMap )
    VTablesToPackageMap = {}
    VTablesToClassMap = {
        '{83CC0D81-FEDA-11D1-BE76-0060B06816CF}' : 'IS7ProSim',
        '{83CC0D82-FEDA-11D1-BE76-0060B06816CF}' : 'IS7ProSimEvents',
    }
   
   
    NamesToIIDMap = {
        'IS7ProSimEvents' : '{83CC0D82-FEDA-11D1-BE76-0060B06816CF}',
        'IS7ProSim' : '{83CC0D81-FEDA-11D1-BE76-0060B06816CF}',
    }
   
    win32com.client.constants.__dicts__.append(constants.__dict__)
----------end of listings 
---------------------------------------------------------------

Note the differences between these listings in the definition of CoClass 
IS7ProSim, repeated here for convenience:
    Ole Viewer:
        [
          uuid(83CC0D83-FEDA-11D1-BE76-0060B06816CF),
          helpstring("S7ProSim Class")
        ]
        coclass S7ProSim {
            [default] interface IS7ProSim;
            [default, source] interface IS7ProSimEvents;
        };
    makepy
        class S7ProSim(CoClassBaseClass): # A CoClass
            # S7ProSim Class
            CLSID = IID('{83CC0D83-FEDA-11D1-BE76-0060B06816CF}')
            coclass_sources = [
            ]
            coclass_interfaces = [
                IS7ProSim,
            ]
            default_interface = IS7ProSim

OLE Viewer finds a second interface IS7ProSimEvents that is made the 
default source
makepy does not recognise IS7ProSimEvents as an interface at all.

Running my (test) script yields this:
----------------------
    import win32com.client as w32c
    sS7PROSIM = 'S7wspsmx.S7ProSim.1'
    class testCalls:
       (.... class definition with event functions ....)
    obj = w32c.DispatchWithEvents(sS7PROSIM, testCalls)
   
Result:    
      File "C:\Python24\Lib\site-packages\win32com\client\__init__.py", 
line 263, in DispatchWithEvents
        raise ValueError, "This COM object does not support events."
    ValueError: This COM object does not support events.

In file "C:\Python24\Lib\site-packages\win32com\client\__init__.py", 
line 261-263:
       events_class = getevents(clsid)
       if events_class is None:
         raise ValueError, "This COM object does not support events."

get_events(clsid) seems to return None.
The clsid given to getevents is 
"IID('{83CC0D81-FEDA-11D1-BE76-0060B06816CF}')"
This is the clsid for the IS7ProSim, derived from IDispatch.
   
In file "C:\Python24\Lib\site-packages\win32com\client\__init__.py", 
line 323-376:
    def getevents(clsid):
        (....)
        # find clsid given progid or clsid
        clsid=str(pywintypes.IID(clsid))
        # return default outgoing interface for that class
        klass = gencache.GetClassForCLSID(clsid)
        try:
          return klass.default_source
        except AttributeError:
          # See if we have a coclass for the interfaces.
          try:
            return 
gencache.GetClassForCLSID(klass.coclass_clsid).default_source
          except AttributeError:
            return None

gencache.GetClassForCLSID(clsid) returns the class with docstring 
"""IS7ProSim Interface for S7ProSim COM Object"""
This is correct.
This class has no sources, as can be seen in the OLE Viewer output, and 
the AttributError is thrown. OK.
klass.coclass_clsid is '{83CC0D83-FEDA-11D1-BE76-0060B06816CF}'. OK.
gencache.GetClassForCLSID(klass.coclass_clsid) returns a class with the 
following properties:
    base class          : CoClassBaseClass
    coclass_interface : [<class 
win32com.gen_py.83CC0D80-FEDA-11D1-BE76-0060B06816CFx0x1x0.IS7ProSim at 
0x00E4DCF0>]
    coclass_sources      : []
    clsid              : {83CC0D83-FEDA-11D1-BE76-0060B06816CF}
There is no attribute default_source, so the AttributeError is thrown again.

This is correct with respect to the makepy output, but not with repsect 
to the actual OLE object. 
IMHO the makepy output should have included IS7PrSomEvents as an 
interface and declared it as
default_source within CoClass S7SimPro. Right?

So how come it does not?

Tracking the makepy actions:
----------------------------
A convenient break point location is makepy.py line 263: gen.generate(....)
Walking on till genpy.py line 737 self.CollectOleItemInfosFromType() 
returned a (correct) list
of all OleItem information headers:
      infotype                             doc[0]                    doc[1]
      TKIND_INTERFACE,    'IS7ProSimEvents', 'Event interface for 
S7ProSim COM Object'
      TKIND_COCLASS,         'S7ProSim',             'S7ProSim Class'
      TKIND_DISPATCH,       'IS7ProSim',           'IS7ProSim Interface 
for S7ProSim COM Object'
      a number of (infotype 0 and 6) entries for the constant enumerations
This list is run through to colelct and build all oleItems, enumItems, 
recordItems and vtableItems
     pass 0: TKIND_INTERFACE,     'IS7ProSimEvents', 'Event interface 
for S7ProSim COM Object'
           oleItem, vtableItem = self._Build_Interface(type_info_tuple) 
returns (None, <VTableItem>)
              returns (None, <VTableItem>). This will lead to the 
exclusion of this interface later on.

So how does this happen?

In self._Build_Interface:
    oleItem = vtableItem = None
    if infotype == pythoncom.TKIND_DISPATCH or \
       (infotype == pythoncom.TKIND_INTERFACE and attr[11] & 
pythoncom.TYPEFLAG_FDISPATCHABLE):
        oleItem = DispatchItem(info, attr, doc)
Well, infotype == TKIND_INTERFACE, and attr[11] (wTypeFlags) == 256 
(TYPEFLAG_FOLEAUTOMATION).
This seems to be a good reason to ignore the interface: oleItem remains 
None,

BTW: a simple DisPatch call (i.o. DispatchWithEvents) works fine, except 
for ignoring the events of course.

Did I stumble on a bug here or am I completely missing a point? Please 
help out.

Thanks,
Len Remmerswaal

_______________________________________________
Python-win32 mailing list
Python-win32@python.org
http://mail.python.org/mailman/listinfo/python-win32

Reply via email to