[python-win32] How to write a COM Server implementing interfaces from type lib?

Mark Hammond mhammond at skippinet.com.au
Fri Mar 23 00:22:40 CET 2012


On 22/03/2012 11:51 PM, Jan Wedel wrote:
> 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?

Just laziness on my part :)

>> 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?

Not that I'm aware of.

> 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?

It wont matter until an attempt is made to call it, and the debug output 
should reflect that.

>
> 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.

It is almost impossible to tell.  It could be something quite trivial, 
like your method not returning exactly what is expected.  Somewhere in 
the client there will be an "if (something_obscure) return E_FAIL;"

Sorry I can't seem to be much help...

Mark
>
> Thanks a lot!
>
> //Jan
>
> ----- Originalnachricht ----- Von: "Mark
> Hammond"<skippy.hammond at gmail.com> Gesendet: Don, 3/22/2012 12:29pm
> An: "Jan Wedel"<Jan.Wedel at ettex.de> Cc: python-win32 at 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 at python.org
>> http://mail.python.org/mailman/listinfo/python-win32
>



More information about the python-win32 mailing list