win32, COM, and Solidworks (again) - Still Trying

Markus Wankus markus_wankus at hotmail.com
Wed Aug 6 21:55:02 EDT 2003


I posted a looong time ago regarding this COM stuff (see the pasted post at 
the end) and I am still beating my head against the wall on this.

I have been trying to debug this further and I think the root of my problem 
lies in win32com.client.__WrapDispatch(), pasted here:

def __WrapDispatch(dispatch, userName = None, resultCLSID = None, typeinfo 
= None, \
                  UnicodeToString = NeedUnicodeConversions, clsctx = 
pythoncom.CLSCTX_SERVER,
                  WrapperClass = None):
  """
    Helper function to return a makepy generated class for a CLSID if it 
exists,
    otherwise cope by using CDispatch.
  """
  if resultCLSID is None:
    try:
      typeinfo = dispatch.GetTypeInfo()
      if typeinfo is not None: # Some objects return NULL, some raise 
exceptions...
        resultCLSID = str(typeinfo.GetTypeAttr()[0])
    except pythoncom.com_error:
      pass
  if resultCLSID is not None:
    import gencache
    # Attempt to load generated module support
    # This may load the module, and make it available
    klass = gencache.GetClassForCLSID(resultCLSID)
    if klass is not None:
      return klass(dispatch)

  # Return a "dynamic" object - best we can do!
  if WrapperClass is None: WrapperClass = CDispatch
  return dynamic.Dispatch(dispatch, userName, WrapperClass, typeinfo, 
UnicodeToString=UnicodeToString,clsctx=clsctx)


For my objects, dispatch.GetTypeInfo() causes the following exception:

Traceback (most recent call last):
  File "F:\_DEV\solidworks\swpycom.py", line 12, in ?
    print sw._oleobj_.GetTypeInfo()
pywintypes.com_error: (-2147467263, 'Not implemented', None, None)

Hmmm...'Not Implemented'.  Because this fails, I can't obtain the CLSID of 
itself, thus it can't use the makepy generated code to bind itself 
properly.  I always get back dumb CDispatch objects.  I have been trying to 
hack a way of getting the CLSID of the dispatch object, but it just isn't 
there.

Anyway - if anyone reading this knows anything about what I am talking 
about, your help would be greatly appreciated.

Thanks,
-- 
Markus


=============8< (old post) ===============
Refer to: http://mail.python.org/pipermail/python-list/2003-May/161346.html

win32com and early binding: Definitive answer? Markus Wankus 
markus_wankus at hotmail.com Sat, 03 May 2003 12:24:14 -0400

Hi all,
I have been playing with Python and COM - attempting to talk to a 3D CAD 
package.  I have posted on this before, and I have also had a lot of direct 
hints from Mark Hammond - but I am looking for a definitive answer to my 
problem here.  I have seen other similar threads while Googling for a 
solution, but all of the answers were the same, with no real explanation as 
to why (at least nothing that I understand - being a COM newbie).
Anyway - the problem is that the particular application (Solidworks) 
apparently refuses to use early binding.  I have gone through the Python 
Programming on Win32 book with a fine-toothed comb but it just doesn't say 
anything about this sort of situation.  Some code is in order:
An example of early binding:
import win32com.client
sw = win32com.client.Dispatch('SldWorks.Application')
print repr(sw)
produces the output:

'<COMObject SldWorks.Application>'
OK - fine.  No problem.  Running makepy on the type library generates a 
module just fine, and it appears in the gen_py folder.  So, technically 
this should force early binding:
# Use these commands in Python code to auto generate .py support
from win32com.client import gencache
gencache.EnsureModule('{83A33D31-27C5-11CE-BFD4-00400513BB57}', 0, 10, 0)
import win32com.client
sw = win32com.client.Dispatch('SldWorks.Application')
print repr(sw)
which also produces the output:

'<COMObject SldWorks.Application>'
ARRGH! OK - well the file generated by makepy has the following in it:
(...a whole bunch of crap...)
class ISldWorks(DispatchBaseClass):
"""Interface for SolidWorks"""
CLSID = pythoncom.MakeIID('{83A33D22-27C5-11CE-BFD4-00400513BB57}')
(..various methods, etc...)
And near the bottom of the file:
# This CoClass is known by the name 'SldWorks.Application.10'
class SldWorks(CoClassBaseClass): # A CoClass
CLSID = pythoncom.MakeIID("{B49E4B36-A1A9-46A4-A738-545948A64113}")
coclass_sources = [
DSldWorksEvents,
]
default_source = DSldWorksEvents
coclass_interfaces = [
ISldWorks,
]
default_interface = ISldWorks
(NOTE: 'SldWorks.Application.10' is a shortcut to 'SldWorks.Application' in 
the Registry.  This is in case you have different versions instlled at the 
same time.  Dispatching 'SldWorks.Application.10' produces the same 
results.)
Various Google threads, as well as emails to me from Mark Hammond himself 
(which I very much appreciate - but this was awhile ago and I would like to 
put this to bed) has eluded to the fact that Solidworks does not implement 
IDispatch properly, which is the cause of my problems.  I CAN get it to 
work by doing the following:
# Use these commands in Python code to auto generate .py support
from win32com.client import gencache
sldmod = gencache.EnsureModule('{83A33D31-27C5-11CE-BFD4-00400513BB57}', 0, 
10, 0)
import win32com.client
sw = win32com.client.Dispatch('SldWorks.Application')
sw = sldmod.ISldWorks(sw)
print repr(sw)
which produces the following output:
<win32com.gen_py.SldWorks 2001plus Type Library.ISldWorks>
The question is this:  Is there ANY way to get around this without having 
to do the extra step of running that object through the interface?  I could 
live with it if it was just the main application object - but I have to do 
this for every single Solidworks object I want to instantiate.  So if a 
method of the application object returns a document object for instance, I 
have to look into the API and find out what type of object it is *supposed* 
to retun, tack an "I" on the front, and run it through its interface with:
newObject = sldmod.I<real_object_name>(newObject)
Which I could automate if I new the object type I was supposed to get back 
ahead of time - but I can't see a way to figure this out?
I guess my only other option is to use something like SWIG to wrap the 
.dll, or ctypes or something - but that seems like so much more work!  Not 
to mention the fact that I don't know much about them either - so it may 
take me forever to figure those methods out as well.
Anyway, as usual - any help would be greatly appreciated.  FWIW - my next 
step is to get a definitive book on COM (not necessarily anything to do 
with Python) to learn more background.  Perhaps there is a way in raw COM 
to handle this.  Also - the docs for the Solidworks API say the following 
(if this helps):
Programmer's Guide
COM vs. Dispatch
SolidWorks exposes its API functionality through standard COM objects. For 
OLE automation, the API is exposed using IDispatch.
The Dispatch interface accepts and returns arguments as Variants and 
IDispatch pointers so they can be handled by languages such as Basic. All 
Visual Basic, VBA, or VC++ executable (.exe) implementations should use the 
Dispatch interface.
A COM implementation gives your application direct access to the underlying 
objects or arrays, and subsequently, increased performance. COM 
implementations will provide slightly more functionality, with operations 
such as enumeration, and will also return an HRESULT value for each API 
function call. The COM interface is currently only available to VC++ add-in 
DLL implementations.
The COM interface is recommended for all VC++ add-in DLL projects. If your 
product will be an .exe implementation, then you are required to use the 
Dispatch interface since custom marshaling is not provided.
Which all sounds good - but it just doesn't work.  I can provide 
.tlb/.idl/gen_py files if anyone is really keen...
Thanks ahead of time for any assistance,
-- 
Markus




More information about the Python-list mailing list