M
Markus Wankus
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
(e-mail address removed) 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,
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
(e-mail address removed) 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,