It seems I *can* make this work with the pythoncom test object.

I created a structure with no GUID in a TLB with a name StructWithoutUUID.

Then, I used the code at the end of this message as a quick hack to try out the SetGuid and the output is:

"""
as expected, failed to create the record normally: The structure 'StructWithoutUUID' is not defined
in module '<module 'win32com.gen_py.6BCD...
GUID before:  {00000000-0000-0000-0000-000000000000}
GUID after:  {EFC72B2F-84D1-4474-8D91-F59928A9C13E}
GOT com_struct(int_value=0, str_value=u'')
"""

So maybe you could check the test code you used - eg, make sure the ITypeInfo you are using is the one you expect etc.

I probably *could* hack this into win32com to do this magically behind the scenes, but I'm not really sure it is worthwhile - most typelibs with structures seems to have the GUID set correctly. Let's see if it gets you any further without any other gross hacks, and if it does, I'll rethink that.

The code I used to test is:
"""
import sys
import win32com.client
import pythoncom

ob = win32com.client.Dispatch("PyCOMTest.PyCOMTest")
module = sys.modules[ob.__class__.__module__]
tlb = pythoncom.LoadRegTypeLib(module.CLSID, module.MajorVersion, module.MinorVersion, module.LCID)

try:
    rec = win32com.client.Record("StructWithoutUUID", ob)
    print "Hrm - this should have failed!"
except ValueError, exc:
    print "as expected, failed to create the record normally:", exc

for i in range(tlb.GetTypeInfoCount()):
    info = tlb.GetTypeInfo(i);
    if info.GetDocumentation(-1)[0] == "StructWithoutUUID":
        print "GUID before: ", info.GetTypeAttr()[0]
        cti = info.QueryInterface(pythoncom.IID_ICreateTypeInfo)
        cti.SetGuid(pythoncom.CreateGuid())
        print "GUID after: ", info.GetTypeAttr()[0]
        rec = pythoncom.GetRecordFromTypeInfo(info)
        print "GOT", rec
        rec.int_value = 123

        break
"""

Cheers,

Mark


On 27/03/2012 7:06 PM, Jan Wedel wrote:
I tried the following from the console:

 >>> import pythoncom

Get the type lib:

 >>> TL_OPC_DA =
pythoncom.LoadRegTypeLib('{3B540B51-0378-4551-ADCC-EA9B104302BF}', 3, 0, 0)
 >>> TL_OPC_DA
<PyITypeLib at 0x02BB2B98 with obj at 0x004D3390>

Index 8 is the record I try to create:

 >>> TL_OPC_DA.GetTypeInfo(8)
<PyITypeInfo at 0x02BB2BB0 with obj at 0x004D4804>
 >>> recinfo = TL_OPC_DA.GetTypeInfo(8)

Cast to ICreateTypeInfo:

 >>> icti = recinfo.QueryInterface(pythoncom.IID_ICreateTypeInfo)
 >>> icti
<PyICreateTypeInfo at 0x02BB2BC8 with obj at 0x004D4800>

Set random GUID:

 >>> icti.SetGuid(pythoncom.CreateGuid())

BAM! :

 >>> pythoncom.GetRecordFromTypeInfo(icti)
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
pywintypes.com_error: (-2147024809, 'Falscher Parameter.', None, None)

Try another cast because I wasn't sure that GetRecordFromTypeInfo
supports/checks the ICreateTypeInfo interface.

 >>> iti = icti.QueryInterface(pythoncom.IID_ITypeInfo)
 >>> iti
<PyITypeInfo at 0x02BB2BF8 with obj at 0x004D4804>

BAM! :

 >>> pythoncom.GetRecordFromTypeInfo(iti)
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
pywintypes.com_error: (-2147024809, 'Falscher Parameter.', None, None)

Unfortunately, as you can see, it doesn't work. Do you know if there is
a way to get a more verbose error message or some COM debugging console
to know what COM is actually complaining about? I mean, I'm pretty sure
that it works as you assumed (Like COM tries to get the GUID from the
TypeInfo and then calls GetRecordFromGuid which would explain why
setting a GUID still doesnt work) but just to make sure, you know?

//Jan

Am 27.03.2012 00:24, schrieb Mark Hammond:
On 26/03/2012 8:51 PM, Jan Wedel wrote:
I'm confident the E_INVALIDARG error is coming from COM itself.  It

wouldn't surprise me at all to find GetRecordInfoFromTypeInfo simply

uses the type info to fetch the GUID of the record then calls the

...FromGuids method.



Yeah, maybe. I just tried to find some explanation and found this:

http://www.autohotkey.com/forum/topic84172.html



This thread seems to be related and presents a solution. As far as I
understand, COM is complaining because the UUID is missing and in this
solution a random GUID is created and attached to the typeinfo...

That's interesting and might well work.  It could all be done from
pure Python as ICreateTypeInfo is exposed.

It might be possible to write all the info needed to the generated
file,

but it would probably take a fair bit of work.  If you look later in

PyRecord.cpp, all the "set attribute" and "get attribute" code is

implemented by way of the IRecordInfo interface - eg, GetFieldNoCopy.

It might be possible to steal the code from comtypes, but I'm not

familiar with that.



The question is, is it necessaey to use the C++ part at all? Would it be
  possible to just keep the Record stuff in pure python? Why would it be
necessary to dynamically loade the TypeInfo from a DLL that most likely
will not change that often?

The problem is the code that knows what "something =
aRecord.someAtribute" does.  It needs to know how to move the memory
around so the right thing happens - that is what GetFieldNoCopy does.

The thing is, I'm currently working on a customer project and
unfortunately I need this stuff getting to work soon. I would really
appreciate you applying a fix to the C++ layer of pythoncom if possible
at all. But I also understand that you can't spend all your spare time
on that problem.

I'd be happy to apply a fix if I knew what the fix was.  From pure
Python, you might be able to:

* load the typelib
* find the type info for the guid.
* QI the type info for ICreateTypeInfo.
* Set the GUID to some random GUID.
* Call pythoncom.GetRecordFromTypeInfo with the modified typeinfo.
* Use the result object as normal.

So, if it's not easy for you to fix this, could you tell me what would
be necessary to fix or work-around this? My "vision" is to manually
create a static definition of all relevant structs somewhere which might
  be just laborious work.

The problem is how that static info is actually used rather than the
generation of it.  Some code will need to use that static info to
support getting and setting attributes on the record.

My problem is that I don't know much about the
internals of COM. Is it possible to write a pure python class that has
the correct attributes and methods so I can return an instance of this
class from a COM method of my server and the receiver and COM itself
would be able to interpret it as the correct struct or does it require
some native code?

It would almost certainly require some native (or ctypes) code to do
it all statically without an IRecordInfo interface to perform the
heavy lifting.

I'll try and find time to use one of the pywin32 test objects to see
if this can work by dynamically setting a GUID, but it is unlikely to
happen for a few days.

Cheers,

Mark



//Jan

----- Originalnachricht -----
Von: "Mark Hammond" <mhamm...@skippinet.com.au>
Gesendet: Sam, 24.3.2012 00:53
An: "Jan Wedel" <jan.we...@ettex.de>
Betreff: Re: AW: AW: AW: AW: [python-win32] How to write a COM Server
implementing interfaces from type lib?

I'm confident the E_INVALIDARG error is coming from COM itself.  It
wouldn't surprise me at all to find GetRecordInfoFromTypeInfo simply
uses the type info to fetch the GUID of the record then calls the
...FromGuids method.

It might be possible to write all the info needed to the generated file,
but it would probably take a fair bit of work.  If you look later in
PyRecord.cpp, all the "set attribute" and "get attribute" code is
implemented by way of the IRecordInfo interface - eg, GetFieldNoCopy.
It might be possible to steal the code from comtypes, but I'm not
familiar with that.

Cheers,

Mark

On 24/03/2012 2:39 AM, Jan Wedel wrote:
Obviously this is a "dynamic" process - other languages/tools etc are
likely to use these struct definitions at compile time

What about writing all necessary information into the generated type
lib files like comtypes is doing it? Or do you need to have some
native objects?

(...)it might be worth poking around the MS docs and see if they
offer any way to get an IRecordInfo given just the typelib info and
the
struct name, as that seems the only info we have at the time we
need it.
    If we can find something I'll try and add the support and send
you a
custom built pythoncomxx.dll.

There is some modification of the Record method necessary. In my
server I pass an object created by GetModuleForTypelib(...) to the
Record() method. Inside, I check if its a nodule. If yes, I load the
typelib using pythoncom.LoadRegTypeLib(...) (don't know if its
already cached somewhere, but it was my quick and dirty attempt).
I've checked the MS doc and found a second method to retrieve the
Record object. Using the returned library object, I used
pythoncom.GetRecordFromTypeInfo(tlib.GetTypeInfo(8)) to retrieve the
Record from the type info object instead of GetRecordFromGuids.

The "8" is hard coded for testing and is the library index of the
Record. If this idea could work, it's probably worth to add the
index to the "RecordMap" when generating the file so we have an
alternative look-up key instead of the GUID.

However, it doesn't work either and gives the following result:

Traceback (most recent call last):
    File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\universal.py", line 179, in
dispatch
      retVal = ob._InvokeEx_(meth.dispid, 0, meth.invkind, args,
None, None)
    File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\server\policy.py", line 346,
in _InvokeEx_
      return self._invokeex_(dispid, lcid, wFlags, args, kwargs,
serviceProvider)
    File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\server\policy.py", line 650,
in _invokeex_
      return func(*args)
    File "C:\temp\opc\PyOPCComServer.py", line 241, in GetStatus
      status = Record("tagOPCSERVERSTATUS", GEN_TL_OPC_DA)
    File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\client\__init__.py", line
403, in Record
      return pythoncom.GetRecordFromTypeInfo(tlib.GetTypeInfo(8))
com_error: (-2147024809, 'Falscher Parameter.', None, None)

Which means E_INVALIDARG. I've checked the source code of
PyRecord.cpp and it says "This function will fail if the specified
type info does not have a guid defined".

I don't know if this is a COM or PythonCom limitation... If it's the
latter, I would really appreciate fixing the C++ code. Otherwise, it
might help, having the whole definition inside the generate python
file as explained above.

Thanks a lot for your help!

//Jan


----- Originalnachricht -----
Von: "Mark Hammond"<mhamm...@skippinet.com.au>
Gesendet: Fre, 3/23/2012 2:43pm
An: "Jan Wedel"<jan.we...@ettex.de>
Betreff: Re: AW: AW: AW: [python-win32] How to write a COM Server
implementing interfaces from type lib?

So - looking at the source, the win32com.client.Record object attempts
to look up both the tlb guid and the record guid based on the name
using
that RecordMap dict we talked about.  From there, we should wind up in
win32com\src\PyRecord.cpp, where we use the typelib info and the struct
GUID to call the COM function GetRecordInfoFromGuids(), which takes
those GUIDs and returns an IRecordInfo interface that tells us all the
struct element names and types etc.

Obviously this is a "dynamic" process - other languages/tools etc are
likely to use these struct definitions at compile time, which may
explain why they have a NULL guid - they assume people wont need to
look
them up at runtime using GetRecordInfoFromGuids.  It is late and I must
hit bed, but it might be worth poking around the MS docs and see if
they
offer any way to get an IRecordInfo given just the typelib info and the
struct name, as that seems the only info we have at the time we need
it.
    If we can find something I'll try and add the support and send
you a
custom built pythoncomxx.dll.

Cheers,

Mark

On 24/03/2012 12:06 AM, Jan Wedel wrote:
I tried to manually add the Records to the generated file to see if
patching genpy.py would solve the problem:

RecordMap = {
       u'tagOPCITEMVQT': '{00000000-0000-0000-0000-000000000000}',
       u'tagOPCSERVERSTATE': '{00000000-0000-0000-0000-000000000000}',
       u'tagOPCSERVERSTATUS':
'{00000000-0000-0000-0000-000000000000}',
}

The client calls the GetStatus() method of my server which is
implemented as follows:

       def GetStatus(self):
           """Returns the current server status"""
           print "GetStatus()"

           # return None
           status = Record("tagOPCSERVERSTATUS", self)

           status.ftStartTime = pywintypes.Time(self.start_time)
           status.ftCurrentTime = pywintypes.Time(time.time())
           status.ftLastUpdateTime =
pywintypes.Time(self.last_update_time)
           status.dwServerState = ServerState.RUNNING
           status.dwGroupCount = len(self.groups)
           status.dwBandWidth = self.band_width
           status.wMajorVersion = MAJOR_VERSION
           status.wMinorVersion = MINOR_VERSION
           status.wBuildNumber = BUILD_NUMBER
           status.wReserved = 0
           status.szVendorInfo = VENDOR_INFO

           return status

with the following result:

GetStatus()
pythoncom error: Failed to call the universal dispatcher

Traceback (most recent call last):
     File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\universal.py",line 179, in
dispatch
       retVal = ob._InvokeEx_(meth.dispid, 0, meth.invkind, args,
None, None)
     File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\server\policy.py", line
346, in _InvokeEx_
       return self._invokeex_(dispid, lcid, wFlags, args, kwargs,
serviceProvider)
     File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\server\policy.py", line
650, in _invokeex_
       return func(*args)
     File "C:\temp\opc\PyOPCComServer.py", line 234, in GetStatus
       status = Record("tagOPCSERVERSTATUS", self)
     File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\client\__init__.py", line
399, in Record
       object = gencache.EnsureDispatch(object)
     File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\client\gencache.py", line
529, in EnsureDispatch
       disp = win32com.client.Dispatch(prog_id)
     File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\client\__init__.py", line
96, in Dispatch
       return __WrapDispatch(dispatch, userName, resultCLSID,
typeinfo, clsctx=clsctx)
     File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\client\__init__.py", line
43, in __WrapDispatch
       return dynamic.Dispatch(dispatch, userName, WrapperClass,
typeinfo, clsctx=clsctx)
     File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\client\dynamic.py", line
122, in Dispatch
       typeinfo = IDispatch.GetTypeInfo()
AttributeError: EttexOPCServer instance has no attribute 'GetTypeInfo'

pythoncom error: Failed to call the universal dispatcher

Traceback (most recent call last):
     File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\universal.py",
line 195, in dispatch
       WriteFromOutTuple(retVal, meth._gw_out_args, argPtr)
TypeError: The VARIANT type is unknown (0x4024).

If found an example of how to create a Record. However, it does use
client COM and passes the object generated by Dispatch() to the
Record constructor. What object should I pass on the server side?
Obviously "self" doesn't work...

Then I tried to return None instead which should leave the output
arguments untouch, but I only get the "TypeError: The VARIANT type
is unknown (0x4024)." message. The 0x4024 is 16420 and can be found
in the vtable definition of the generated file:

IOPCServer_vtables_ = [
(...)
       (( u'GetStatus' , u'ppServerStatus' , ), 1610678275,
(1610678275, (), [ (16420, 2, None, None) , ], 1 , 1 , 4 , 0 , 24 ,
(3, 0, None, None) , 0 , )),
(...)

I was asking myself how does this 16420 from the vtable gets
translated into a "Ptr Ptr tagOPCSERVERSTATUS"? How does the record
definition know what fields it contains? Is is probably necessary
to add some more information to the Record such as this number,
e.g? Or does the internal COM libs uses the TypeLib information to
resolve it? It tried to look into the source of this type error but
the WriteFromOutTuple method is somewhere inside the pythoncom dll...

I'm really stuck here...

//Jan



----- Originalnachricht -----
Von: "Mark Hammond"<mhamm...@skippinet.com.au>
Gesendet: Fre, 3/23/2012 12:11pm
An: "Jan Wedel"<jan.we...@ettex.de>
Betreff: Re: AW: AW: [python-win32] How to write a COM Server
implementing interfaces from type lib?

I haven't got the code in front of me, but in that place it would
probably be OK to use the name.  I'm slightly worried about the
"global"
resolution though - there's probably code that can find a record given
just the GUID and without regard for which tlb it was defined in
(ie, so
the name itself need not be unique).  In that case, a tuple of
(typelib_guid, name) would probably be OK.

Mark

On 23/03/2012 7:54 PM, Jan Wedel wrote:
After I wrote my last mail, I did some further research on the
problem. In genpy.py where the python file is generated for the
type lib, I found this code:

       print>>   stream, 'RecordMap = {'
        for record in recordItems.itervalues():
            if str(record.clsid) == pythoncom.IID_NULL:
                print>>   stream, "\t###%s: %s, # Typedef disabled
because it doesn't have a non-null GUID" % (repr(record.doc[0]),
repr(str(record.clsid)))
            else:
                print>>   stream, "\t%s: %s," %
(repr(record.doc[0]), repr(str(record.clsid)))
        print>>   stream, "}"

I've checked the typelib with COMView, and all records, modules
and enums defined have the UUID {00000-...00000} assigned. Then
I've checked some random other type libs I've found on my computer
and it seems that they are always using {0000...0000}. I don't
know much about COM but I guess this must be OK. The type lib I'm
using is made by the OPC foundation and there must be a numerous
COM clients using it so it can't be that wrong, can it?

The question is, is it possible to create a Record object in my
server class and return it without the need of having a unique
UUID assignedand without the need of beeing defined by genyp?

If that doesn't work, I might also try to patch the genpy.py, but
it seems as if you rely on the CLSID being unique. in the
BuildOleItemsFromType method, there is the following code:

          elif infotype == pythoncom.TKIND_RECORD or infotype ==
pythoncom.TKIND_UNION:
            newItem = RecordItem(info, attr, doc)
            recordItems[newItem.clsid] = newItem

Because all records have the same clsid, they get overwritten by
each other. There probably must be some other identification
mechanism. Either we use an index or the record name itself... But
then COM methods must be aware of these type in input and output
parameters...

Do have any suggestion on how to proceed (patch/work-around)?

Thanks!

//Jan


----- Originalnachricht -----
Von: "Mark Hammond"<mhamm...@skippinet.com.au>
Gesendet: Fre, 3/23/2012 12:30am
An: "Jan Wedel"<jan.we...@ettex.de>
Cc: python-win32@python.org
Betreff: Re: AW: [python-win32] How to write a COM Server
implementing interfaces from type lib?

On 23/03/2012 3:54 AM, Jan Wedel wrote:
I've actually managed to patch the policy.py to allow multiple
typelibraries.

Instead of having a definition for interfaces, type library id,
version etc i've build this into one tuple. I've created a new
attribute that can have multiple of these tuples. The head of my
server class now looks like this:


class EttexOPCServer: _reg_progid_ = "Ettex.OPC.Automation"
_reg_desc_ = "ettex OPC DA Server" _reg_clsid_ =
"{13B51E8D-4BC2-4DED-8D4E-4614692F88E6}" _reg_catids_ = [
'{63D5F432-CFE4-11D1-B2C8-0060083BA1FB}' ]

_typelib_interfaces_ = [ ("{3B540B51-0378-4551-ADCC-EA9B104302BF}",
3, 0, 0, [ 'IOPCServer', 'IOPCItemProperties', ] ),
("{B28EEDB1-AC6F-11D1-84D5-00608CB8A7E9}", 1, 0, 0, [ 'IOPCCommon',
'IConnectionPointContainer' ] ), ] _reg_clsctx_ =
pythoncom.CLSCTX_LOCAL_SERVER

I had to patch three locations in policy.py so far and I would be
happy to send you my changes if you like.

That would be great - a patch on sourceforge would be best.
However, it
might be better to wait until we are sure it is working OK :)


However it still doesn't
work but I'm not sure if I have missed something in my patch or
if it
is a general problem in my server code or a bug in the framework.

At least, the client successfully creates the object and tries to
call a method of the interface but I get the following debug output:

GetStatus() pythoncom error: Failed to call the universal dispatcher

Traceback (most recent call last): File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\universal.py", line 195, in
dispatch WriteFromOutTuple(retVal, meth._gw_out_args, argPtr)
TypeError: The VARIANT type is unknown (0x4024). pythoncom error:
Unexpected gateway error

Traceback (most recent call last): File "C:\Program Files
(x86)\Python\lib\site-packages\win32com\universal.py", line 195, in
dispatch WriteFromOutTuple(retVal, meth._gw_out_args, argPtr)
TypeError: The VARIANT type is unknown (0x4024). GetErrorString
-2147467259 0 pythoncom error: Failed to call the universal
dispatcher

(...)

I've hat a lookat the definition of GetStatus. It requires a pointer
to a pointer of type "tagOPCSERVERSTATUS" which is a record
definition in the type library. But when I look at what has been
generated by makepy, the record map looks pretty empty to me:

RecordMap = { u'tagOPCITEMVQT':
'{00000000-0000-0000-0000-000000000000}', }

The type lib defines 10 records! I tried to import the typelib using
comtypes and get that generated (excerpt):

class tagOPCSERVERSTATUS(Structure): pass

# values for enumeration 'tagOPCSERVERSTATE' OPC_STATUS_RUNNING = 1
OPC_STATUS_FAILED = 2 OPC_STATUS_NOCONFIG = 3 OPC_STATUS_SUSPENDED =
4 OPC_STATUS_TEST = 5 OPC_STATUS_COMM_FAULT = 6 tagOPCSERVERSTATE =
c_int # enum tagOPCSERVERSTATUS._fields_ = [ ('ftStartTime',
_FILETIME), ('ftCurrentTime', _FILETIME), ('ftLastUpdateTime',
_FILETIME), ('dwServerState', tagOPCSERVERSTATE), ('dwGroupCount',
c_ulong), ('dwBandWidth', c_ulong), ('wMajorVersion', c_ushort),
('wMinorVersion', c_ushort), ('wBuildNumber', c_ushort),
('wReserved', c_ushort), ('szVendorInfo', WSTRING), ]

Is there a bug in makepy that prevents creating these records?

It would seem so :)

If
yes, can I fix it or can I manually change the generated file to
support the records?

I'm really not sure - you probably need to look at the existing
Record
tests.  The "PyCOMTest" test object (which is in the source tree and
needs you to build it using MSVC) has some structs for testing.


If not, how would I create and return such a
pointer of a pointer in pythoncom? Is comtypes compatible with
pythoncom so I could use the comtypes generated files?

Unfortunately there isn't any compatibility between the 2.

Cheers,

Mark

Thanks!

//Jan

----- Originalnachricht ----- Von: "Jan Wedel"<jan.we...@ettex.de>
Gesendet: Don, 3/22/2012 1:51pm An: mhamm...@skippinet.com.au Cc:
python-win32@python.org Betreff: Re: [python-win32] How to write a
COM Server implementing interfaces from type lib?

Hi Mark,

thanks for your reply.

That's E_FAIL which is pretty generic.  Doesn't sound like a
simple failure to QI for the correct interface.

Yeah. The client says something like "Unknown Error" which makes it
hard to tell what the problem actually is.

win32com should be able to do this given there is a tlb - see the
"pippo" samples.  Further, using the debug facilities and
win32traceutil, you should be able to see the creation of the
object, the QIs on the object and any methods actually called.  But
as mentioned, I doubt it is a QI failure.

Yeah, there is a type library (OPC DA). Actually there are even two
type libraries, each of them contains two Interface which my server
needs to implement.

I've had a look into policy.py and found that:

def _build_typeinfos_(self): # Can only ever be one for now. (...)

which means using _typelib_guid_ allows only one type lib, right? I
could try to patch your code or do you think there is a general
problem that would make it impossible having more than one
typelib in
your framework?

Have you tried contacting the author of the object?

I am the author... I only use the 3rd party typelibs. I am writing
the server that must support the interfaces so that other 3rd party
clients can access the server component.

I can reproduce the error using python the COMView tool trying to
instanciate the Server. The problem is, that I don't see why. I've
enabled debugging mode when registering the server. When using
one of
the 3rd party clients, rhe python trace collector shows the
following:

Object with win32trace dispatcher created (object=None) Entering
constructor in<PyOPCComServer.EttexOPCServer instance at
0x0213B058>._QueryInterface_ with unsupported IID
{00000003-0000-0000-C000-000000000046}
({00000003-0000-0000-C000-000000000046})
in<PyOPCComServer.EttexOPCServer instance at
0x0213B058>._QueryInterface_ with unsupported IID
{0000001B-0000-0000-C000-000000000046}
({0000001B-0000-0000-C000-000000000046})
in<PyOPCComServer.EttexOPCServer instance at
0x0213B058>._QueryInterface_ with unsupported IID
{00000018-0000-0000-C000-000000000046}
({00000018-0000-0000-C000-000000000046})
in<PyOPCComServer.EttexOPCServer instance at
0x0213B058>._QueryInterface_ with unsupported IID
{4C1E39E1-E3E3-4296-AA86-EC938D896E92}
({4C1E39E1-E3E3-4296-AA86-EC938D896E92})
in<PyOPCComServer.EttexOPCServer instance at
0x0213B058>._InvokeEx_-AddConnection(1, 0) [1,0,None] Connection
added. Active connections: 1 in<PyOPCComServer.EttexOPCServer
instance at 0x0213B058>._InvokeEx_-ReleaseConnection(1, 0, 1)
[1,0,None] Connection released. Active connections: 0

The QI calls are standard COM calls, not application specific
(AFAIK). You can see the "Entering constructor" message which is in
my python constructor of the server. Just for fun, I implemented the
IExternalConnection interface to see if the methods are called.
"Connection added. ..." is my output. But I can't see an further
requests that shows what the problem is. Are there any calls in
pythoncom that could fail and does not give debug output?

My pythoncom server starts like that:

class EttexOPCServer: _reg_progid_ = "Ettex.OPC.Automation"
_reg_desc_ = "ettex OPC DA Server" _reg_clsid_ =
"{13B51E8D-4BC2-4DED-8D4E-4614692F88E6}" _reg_catids_ = [
'{63D5F432-CFE4-11D1-B2C8-0060083BA1FB}' ] _com_interfaces_ = [
'{00000019-0000-0000-C000-000000000046}', # IExternalConnection (Is
it really mandatory?) '{39C13A4D-011E-11D0-9675-0020AFD8ADB3}', #
IOPCServer '{F31DFDE2-07B6-11D2-B2D8-0060083BA1FB}', # IOPCCommon
'{39C13A72-011E-11D0-9675-0020AFD8ADB3}', # IOPCItemProperties
'{B196B284-BAB4-101A-B69C-00AA00341D07}'  #
IConnectionPointContainer ] _typelib_guid_ =
"{3B540B51-0378-4551-ADCC-EA9B104302BF}" _typelib_version_ = (3, 0)
_reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER

_public_methods_ =  [ 'Connect', 'Disconnect', 'GetErrorString' ] ##
IExternalConnection methods _public_methods_ += [ 'AddConnection',
'ReleaseConnection' ]

################################################################# ##
COM Class Attributes
#################################################################
_public_attrs_ = [ 'ClientName', 'OPCGroups' ]

################################################################# ##
COM Class Read-Only Attributes
#################################################################
_readonly_attrs_ = [ 'OPCGroups' ]

I don't have all interface methods implemented at the time but I
guess that doesn't matter as long as not all type libs have been
loaded, does it?

If its not possible with pythoncom, is it possible with comtypes?
I've tried that as well with nearly the same result ("Unknown
error"):

# OPC Data Access 3.00 Type Library opc_da_tl =
comtypes.GUID("{3B540B51-0378-4551-ADCC-EA9B104302BF}") # OPC Common
1.10 Type Library opc_com_tl =
comtypes.GUID("{B28EEDB1-AC6F-11D1-84D5-00608CB8A7E9}")

GetModule((opc_da_tl, 3, 0)) GetModule((opc_com_tl, 1, 0))

import comtypes.gen.OPCDA as OpcDa import comtypes.gen.OPCCOMN as
OpcCommon

class EttexOPCServer2(OpcCommon.IOPCCommon,
OpcCommon.IConnectionPointContainer, OpcDa.IOPCServer,
OpcDa.IOPCItemProperties): _reg_progid_ = "Ettex.OPC.Automation2"
_reg_desc_ = "ettex OPC DA Server 2" _reg_novers_progid_ =
_reg_progid_ _reg_clsid_ = "{80A2B8F7-792E-43F4-95F8-CD6BB4B413AD}"
_reg_catids_ = [ '{63D5F432-CFE4-11D1-B2C8-0060083BA1FB}' ]
_reg_clsctx_ = comtypes.CLSCTX_LOCAL_SERVER _regcls_ =
comtypes.server.localserver.REGCLS_MULTIPLEUSE

_com_interfaces_ = [OpcCommon.IOPCCommon,
OpcCommon.IConnectionPointContainer, OpcDa.IOPCServer,
OpcDa.IOPCItemProperties]

I don't know what to do. Is there anything more I can do to debug to
find out WHAT exactly causes the error? Because as you can see, the
pythoncom debug output doesn't show this error that is returned by
the client.

Thanks a lot!

//Jan

----- Originalnachricht ----- Von: "Mark
Hammond"<skippy.hamm...@gmail.com>   Gesendet: Don, 3/22/2012
12:29pm
An: "Jan Wedel"<jan.we...@ettex.de>   Cc: python-win32@python.org
Betreff: Re: [python-win32] How to write a COM Server implementing
interfaces from type lib?

On 22/03/2012 2:53 AM, Jan Wedel wrote:
Hi,

I'm currently having trouble to write a COM-Server that has some
special requirements: - It needs to derive from IUnknown - It needs
to implement multiple interface from two different proprietary
typelibs (dlls) - It needs to implement a custom category

At first I started with pythoncom. I used the attribute
_reg_catids_ to specify the category and _com_interfaces_ to
specify the interfaces I want to implement. The client (proprietary
3rd party sw, no source) sees the server but throws some 0x80004005
error on CoCreateInstance.

That's E_FAIL which is pretty generic.  Doesn't sound like a simple
failure to QI for the correct interface.

win32com should be able to do this given there is a tlb - see the
"pippo" samples.  Further, using the debug facilities and
win32traceutil, you should be able to see the creation of the
object, the QIs on the object and any methods actually called.  But
as mentioned, I doubt it is a QI failure.

I was hoping that I can just tell the COM dispatcher, "yes, I have
these interface implemented" and implement the methods without
really having the interface classes available.

I read, that pythoncom can only create components that use
IDispatch so I guess the _com_interfaces_ idea won't work, will
it?

As above, you should be able to fully implement them so long as
makepy has been run.

Then I did some further research and found comtypes. I tried to
write a server stub again. I used GetModule to load the type
library containing the Interfaces, importing the generated
interface classes and let the main server class extend these
interfaces.

The first problem was, that comtypes did not support the
_reg_catids_ attribute or anything similar so I had to add the
Implemented Categories key manually to the registry. Then, I was
able to see the server through the client, which obviously filters
by categories, but it still shows the same error as before.

So, what is the correct/best way to implement a server that needs
to implement custom interfaces and categories? Or is it possible at
all using python?

The fact you get the same error there implies something else is
going wrong, but it is impossible to guess what.  Have you tried
contacting the author of the object?

Mark


Thanks!

//Jan



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

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










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

Reply via email to