How to write a COM Server implementing interfaces from type lib?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

How to write a COM Server implementing interfaces from type lib?

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

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?

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?

Thanks!

//Jan


_______________________________________________
python-win32 mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/python-win32
Reply | Threaded
Open this post in threaded view
|

Re: How to write a COM Server implementing interfaces from type lib?

Mark Hammond-4
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
> [hidden email]
> http://mail.python.org/mailman/listinfo/python-win32

_______________________________________________
python-win32 mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/python-win32
Reply | Threaded
Open this post in threaded view
|

Re: How to write a COM Server implementing interfaces from type lib?

ebeb
In reply to this post by ebeb
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" <[hidden email]>
Gesendet: Don, 3/22/2012 12:29pm
An: "Jan Wedel" <[hidden email]>
Cc: [hidden email]
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
> [hidden email]
> http://mail.python.org/mailman/listinfo/python-win32

_______________________________________________
python-win32 mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/python-win32
Reply | Threaded
Open this post in threaded view
|

Re: How to write a COM Server implementing interfaces from type lib?

ebeb
In reply to this post by ebeb
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. 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?
If yes, can I fix it or can I manually change the generated file to support the records?
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?

Thanks!

//Jan

----- Originalnachricht -----
Von: "Jan Wedel" <[hidden email]>
Gesendet: Don, 3/22/2012 1:51pm
An: [hidden email]
Cc: [hidden email]
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" <[hidden email]>
Gesendet: Don, 3/22/2012 12:29pm
An: "Jan Wedel" <[hidden email]>
Cc: [hidden email]
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
> [hidden email]
> http://mail.python.org/mailman/listinfo/python-win32

_______________________________________________
python-win32 mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/python-win32
_______________________________________________
python-win32 mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/python-win32
Reply | Threaded
Open this post in threaded view
|

Re: How to write a COM Server implementing interfaces from type lib?

Mark Hammond-3
In reply to this post by ebeb
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"<[hidden email]> Gesendet: Don, 3/22/2012 12:29pm
> An: "Jan Wedel"<[hidden email]> Cc: [hidden email]
> 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 [hidden email]
>> http://mail.python.org/mailman/listinfo/python-win32
>

_______________________________________________
python-win32 mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/python-win32
Reply | Threaded
Open this post in threaded view
|

Re: How to write a COM Server implementing interfaces from type lib?

Mark Hammond-3
In reply to this post by ebeb
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"<[hidden email]>
> Gesendet: Don, 3/22/2012 1:51pm An: [hidden email] Cc:
> [hidden email] 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"<[hidden email]> Gesendet: Don, 3/22/2012 12:29pm
> An: "Jan Wedel"<[hidden email]> Cc: [hidden email]
> 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 [hidden email]
>> http://mail.python.org/mailman/listinfo/python-win32
>
> _______________________________________________ python-win32 mailing
> list [hidden email]
> http://mail.python.org/mailman/listinfo/python-win32

_______________________________________________
python-win32 mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/python-win32
Reply | Threaded
Open this post in threaded view
|

Re: How to write a COM Server implementing interfaces from type lib?

ebeb
In reply to this post by ebeb
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" <[hidden email]>
>> Gesendet: Sam, 24.3.2012 00:53
>> An: "Jan Wedel" <[hidden email]>
>> 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"<[hidden email]>
>>> Gesendet: Fre, 3/23/2012 2:43pm
>>> An: "Jan Wedel"<[hidden email]>
>>> 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"<[hidden email]>
>>>> Gesendet: Fre, 3/23/2012 12:11pm
>>>> An: "Jan Wedel"<[hidden email]>
>>>> 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"<[hidden email]>
>>>>> Gesendet: Fre, 3/23/2012 12:30am
>>>>> An: "Jan Wedel"<[hidden email]>
>>>>> Cc: [hidden email]
>>>>> 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"<[hidden email]>
>>>>>> Gesendet: Don, 3/22/2012 1:51pm An: [hidden email] Cc:
>>>>>> [hidden email] 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"<[hidden email]>   Gesendet: Don, 3/22/2012
>>>>>> 12:29pm
>>>>>> An: "Jan Wedel"<[hidden email]>   Cc: [hidden email]
>>>>>> 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 [hidden email]
>>>>>>> http://mail.python.org/mailman/listinfo/python-win32
>>>>>>
>>>>>> _______________________________________________ python-win32 mailing
>>>>>> list [hidden email]
>>>>>> http://mail.python.org/mailman/listinfo/python-win32
>>>>>
>>>>
>>>
>>>
>>
>
>

_______________________________________________
python-win32 mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/python-win32
Reply | Threaded
Open this post in threaded view
|

Re: How to write a COM Server implementing interfaces from type lib?

Mark Hammond-3
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" <[hidden email]>
>>> Gesendet: Sam, 24.3.2012 00:53
>>> An: "Jan Wedel" <[hidden email]>
>>> 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"<[hidden email]>
>>>> Gesendet: Fre, 3/23/2012 2:43pm
>>>> An: "Jan Wedel"<[hidden email]>
>>>> 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"<[hidden email]>
>>>>> Gesendet: Fre, 3/23/2012 12:11pm
>>>>> An: "Jan Wedel"<[hidden email]>
>>>>> 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"<[hidden email]>
>>>>>> Gesendet: Fre, 3/23/2012 12:30am
>>>>>> An: "Jan Wedel"<[hidden email]>
>>>>>> Cc: [hidden email]
>>>>>> 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"<[hidden email]>
>>>>>>> Gesendet: Don, 3/22/2012 1:51pm An: [hidden email] Cc:
>>>>>>> [hidden email] 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"<[hidden email]>   Gesendet: Don, 3/22/2012
>>>>>>> 12:29pm
>>>>>>> An: "Jan Wedel"<[hidden email]>   Cc: [hidden email]
>>>>>>> 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 [hidden email]
>>>>>>>> http://mail.python.org/mailman/listinfo/python-win32
>>>>>>>
>>>>>>> _______________________________________________ python-win32 mailing
>>>>>>> list [hidden email]
>>>>>>> http://mail.python.org/mailman/listinfo/python-win32
>>>>>>
>>>>>
>>>>
>>>>
>>>
>>
>>
>


_______________________________________________
python-win32 mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/python-win32
Reply | Threaded
Open this post in threaded view
|

Re: How to write a COM Server implementing interfaces from type lib?

ebeb
Hi,

sorry for the delay but we were and still are running short on time in
this project so I eventually switched to comtypes (which has a lot other
inconveniences compared to pythoncom but at least it did work with my
special use case).

Nonetheless, I already tried your suggestion but unfortunately it didn't
work. This is the output of the code at the end of this mail using the
typelib I had problems with:

"""
GUID before:  {00000000-0000-0000-0000-000000000000}
GUID after:  {2E6664B0-69B5-4620-8CC4-9A3F790DED73}
Traceback (most recent call last):
  File "record-test.py", line 19, in <module>
    status = pythoncom.GetRecordFromTypeInfo(info)
pywintypes.com_error: (-2147024809, 'Falscher Parameter.', None, None)
"""

Additionally, I have submitted my patched version of policy.py to
support multiple typelibs.

Pythoncom is really nice to use since it manages all the ctypes pointer
stuff which in comtypes you have handle by yourself. But as I already
explained, I needed to make a decision and by fighting with pointers to
pointers to structs in python (!!!), I was able to get my COM server
running.

Anyways, thank you a lot for your help in the past.

//Jan

"""
import time
import pywintypes
import pythoncom

# Load typelibs
TL_OPC_DA =
pythoncom.LoadRegTypeLib('{3B540B51-0378-4551-ADCC-EA9B104302BF}', 3, 0,
0)

status = None

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

                 break
                 
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

print "Done!"
"""


> -----Ursprüngliche Nachricht-----
> Von: Mark Hammond [mailto:[hidden email]]
> Gesendet: Freitag, 30. März 2012 07:30
> An: Jan Wedel
> Cc: [hidden email]
> Betreff: Re: [python-win32] How to write a COM Server implementing
interfaces
> from type lib?
>
> 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" <[hidden email]>
> >>> Gesendet: Sam, 24.3.2012 00:53
> >>> An: "Jan Wedel" <[hidden email]>
> >>> 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"<[hidden email]>
> >>>> Gesendet: Fre, 3/23/2012 2:43pm
> >>>> An: "Jan Wedel"<[hidden email]>
> >>>> 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"<[hidden email]>
> >>>>> Gesendet: Fre, 3/23/2012 12:11pm
> >>>>> An: "Jan Wedel"<[hidden email]>
> >>>>> 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"<[hidden email]>
> >>>>>> Gesendet: Fre, 3/23/2012 12:30am
> >>>>>> An: "Jan Wedel"<[hidden email]>
> >>>>>> Cc: [hidden email]
> >>>>>> 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"<[hidden email]>
> >>>>>>> Gesendet: Don, 3/22/2012 1:51pm An: [hidden email]
Cc:
> >>>>>>> [hidden email] 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"<[hidden email]>   Gesendet: Don, 3/22/2012
> >>>>>>> 12:29pm
> >>>>>>> An: "Jan Wedel"<[hidden email]>   Cc:
[hidden email]
> >>>>>>> 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 [hidden email]
> >>>>>>>> http://mail.python.org/mailman/listinfo/python-win32
> >>>>>>>
> >>>>>>> _______________________________________________ python-win32
> >>>>>>> mailing list [hidden email]
> >>>>>>> http://mail.python.org/mailman/listinfo/python-win32
> >>>>>>
> >>>>>
> >>>>
> >>>>
> >>>
> >>
> >>
> >
>
>


_______________________________________________
python-win32 mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/python-win32