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

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,
 
M

Markus Wankus

You should use code similar to:

mod = gencache.EnsureModule("...", ...)

ob = Dispatch("Whatever.Object")
# ob now "dumb dispatch", but should be object "foo"
# in the gencache module
# Explicitly convert ob to one of these object.
ob = mod.Foo(ob)

Mark.

Thanks, Mark. That's what I have been doing in the past (on your advice),
but I have been trying to automate that process and found it next to
impossible (from what I can figure out anyway). The reason this is an
incredible pain is that every single method or attribute of 'ob' that
returns an object, returns a dumb object. I need to know what class it is
*supposed* to be, and run it through the gencache module class constructor.
This is a huge pain.

I realize this is nothing to do with win32all - it is Solidworks. Is there
a way I can tell them about this problem and possibly get them to fix it?
What have they not done in their C++ code that is making their
implementation of COM so stupid?

If there was a away I could make my own Dispatch class that forced this re-
instantiation I would be happy, but I can't see any way to figure out what
gencache class I need for a given object at runtime. I wonder if I could
somehow generate my own, smarter module from scratch using the gencache
module as a starting point. It would be incredibly painful...

Anyway - thanks again for your help, Mr. Hammond. Perhaps I should let
this drop already and accept the fact that it can't easily be done...
 
A

Andrew MacIntyre

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)

If you need to go beyond the IDispatch interface, Thomas Heller's ctypes
package looks to be your only option, short of creating a full-blown
Pythonwin wrapper (ala Pythonwin's MAPI support) for the SolidWorks COM
API.

Regards,
Andrew.
 
M

Markus Wankus

If you need to go beyond the IDispatch interface, Thomas Heller's ctypes
package looks to be your only option, short of creating a full-blown
Pythonwin wrapper (ala Pythonwin's MAPI support) for the SolidWorks COM
API.

Yes, I was looking into this but was a little mystified at first. I
generated the python wrapper and had a look inside, but was unsure of the
extent of the Python code I was going to be required to write, as well as
type conversions.

I guess I'll have another look at it. Thanks for the info.
 
J

John J. Lee

Markus Wankus said:
On Thu, 7 Aug 2003 23:06:01 +1000 (EST), Andrew MacIntyre


Yes, I was looking into this but was a little mystified at first. I
generated the python wrapper and had a look inside, but was unsure of
the extent of the Python code I was going to be required to write, as
well as type conversions.

He wasn't talking about the automatically-generated makepy Python
code, but rather hand-crafted C code wrapper around a particular
vtable COM interface (though Mark may have some tools to help with the
process of writing such wrappers, I don't think it's automated).

ctypes is an easy way of doing it than writing a C wrapper.


John
 
M

Markus Wankus

You could ask them to implement the type info interfaces for their
objects.


Yes, this is the rub.

Thanks, again - Mark. I'll try to see if they will listen to suggestions
for future releases...
 
M

Markus Wankus

He wasn't talking about the automatically-generated makepy Python
code, but rather hand-crafted C code wrapper around a particular
vtable COM interface (though Mark may have some tools to help with the
process of writing such wrappers, I don't think it's automated).

ctypes is an easy way of doing it than writing a C wrapper.

Hmmm.. I guess I am not following. I'll go back to the ctypes page again
and give it another go.

Thanks,
 
J

John J. Lee

Markus Wankus said:
Hmmm.. I guess I am not following. I'll go back to the ctypes page
again and give it another go.

Ah, maybe it was me who wasn't following. I haven't looked at the
ctypes COM support. I was thinking you were referring to the
win32all-generated Python code, but maybe ctypes does Python code
generation too, and you were referring to that? Dunno.


John
 
M

Markus Wankus

Markus Wankus said:
]
If you need to go beyond the IDispatch interface, Thomas Heller's
ctypes
package looks to be your only option, short of creating a full- blown
Pythonwin wrapper (ala Pythonwin's MAPI support) for the
SolidWorks COM
API.


Yes, I was looking into this but was a little mystified at first. I
generated the python wrapper and had a look inside, but was unsure of
the extent of the Python code I was going to be required to write, as
well as type conversions.

He wasn't talking about the automatically-generated makepy Python
code, but rather hand-crafted C code wrapper around a particular
vtable COM interface (though Mark may have some tools to help with the
process of writing such wrappers, I don't think it's automated).

ctypes is an easy way of doing it than writing a C wrapper.

Hmmm.. I guess I am not following. I'll go back to the ctypes page
again and give it another go.

Ah, maybe it was me who wasn't following. I haven't looked at the
ctypes COM support. I was thinking you were referring to the
win32all-generated Python code, but maybe ctypes does Python code
generation too, and you were referring to that? Dunno.


John

Yeah - ctypes generates Python code as well and that is what I was
referring to. Anyway - thanks to all who responded. I just wish I knew a
little more...ah well. You can't beat experience. Too bad it takes so
long.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top