[python-win32] How to write a COM Server implementing interfaces from type lib?
Jan Wedel
jan.wedel at ettex.de
Tue Mar 27 10:06:33 CEST 2012
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" <mhammond at skippinet.com.au>
>> Gesendet: Sam, 24.3.2012 00:53
>> An: "Jan Wedel" <Jan.Wedel at ettex.de>
>> Betreff: Re: AW: AW: AW: AW: [python-win32] How to write a COM Server
>> implementing interfaces from type lib?
>>
>> I'm confident the E_INVALIDARG error is coming from COM itself. It
>> wouldn't surprise me at all to find GetRecordInfoFromTypeInfo simply
>> uses the type info to fetch the GUID of the record then calls the
>> ...FromGuids method.
>>
>> It might be possible to write all the info needed to the generated file,
>> but it would probably take a fair bit of work. If you look later in
>> PyRecord.cpp, all the "set attribute" and "get attribute" code is
>> implemented by way of the IRecordInfo interface - eg, GetFieldNoCopy.
>> It might be possible to steal the code from comtypes, but I'm not
>> familiar with that.
>>
>> Cheers,
>>
>> Mark
>>
>> On 24/03/2012 2:39 AM, Jan Wedel wrote:
>>>> Obviously this is a "dynamic" process - other languages/tools etc are
>>>> likely to use these struct definitions at compile time
>>>
>>> What about writing all necessary information into the generated type
>>> lib files like comtypes is doing it? Or do you need to have some
>>> native objects?
>>>
>>>> (...)it might be worth poking around the MS docs and see if they
>>>> offer any way to get an IRecordInfo given just the typelib info and
>>>> the
>>>> struct name, as that seems the only info we have at the time we
>>>> need it.
>>>> If we can find something I'll try and add the support and send
>>>> you a
>>>> custom built pythoncomxx.dll.
>>>
>>> There is some modification of the Record method necessary. In my
>>> server I pass an object created by GetModuleForTypelib(...) to the
>>> Record() method. Inside, I check if its a nodule. If yes, I load the
>>> typelib using pythoncom.LoadRegTypeLib(...) (don't know if its
>>> already cached somewhere, but it was my quick and dirty attempt).
>>> I've checked the MS doc and found a second method to retrieve the
>>> Record object. Using the returned library object, I used
>>> pythoncom.GetRecordFromTypeInfo(tlib.GetTypeInfo(8)) to retrieve the
>>> Record from the type info object instead of GetRecordFromGuids.
>>>
>>> The "8" is hard coded for testing and is the library index of the
>>> Record. If this idea could work, it's probably worth to add the
>>> index to the "RecordMap" when generating the file so we have an
>>> alternative look-up key instead of the GUID.
>>>
>>> However, it doesn't work either and gives the following result:
>>>
>>> Traceback (most recent call last):
>>> File "C:\Program Files
>>> (x86)\Python\lib\site-packages\win32com\universal.py", line 179, in
>>> dispatch
>>> retVal = ob._InvokeEx_(meth.dispid, 0, meth.invkind, args,
>>> None, None)
>>> File "C:\Program Files
>>> (x86)\Python\lib\site-packages\win32com\server\policy.py", line 346,
>>> in _InvokeEx_
>>> return self._invokeex_(dispid, lcid, wFlags, args, kwargs,
>>> serviceProvider)
>>> File "C:\Program Files
>>> (x86)\Python\lib\site-packages\win32com\server\policy.py", line 650,
>>> in _invokeex_
>>> return func(*args)
>>> File "C:\temp\opc\PyOPCComServer.py", line 241, in GetStatus
>>> status = Record("tagOPCSERVERSTATUS", GEN_TL_OPC_DA)
>>> File "C:\Program Files
>>> (x86)\Python\lib\site-packages\win32com\client\__init__.py", line
>>> 403, in Record
>>> return pythoncom.GetRecordFromTypeInfo(tlib.GetTypeInfo(8))
>>> com_error: (-2147024809, 'Falscher Parameter.', None, None)
>>>
>>> Which means E_INVALIDARG. I've checked the source code of
>>> PyRecord.cpp and it says "This function will fail if the specified
>>> type info does not have a guid defined".
>>>
>>> I don't know if this is a COM or PythonCom limitation... If it's the
>>> latter, I would really appreciate fixing the C++ code. Otherwise, it
>>> might help, having the whole definition inside the generate python
>>> file as explained above.
>>>
>>> Thanks a lot for your help!
>>>
>>> //Jan
>>>
>>>
>>> ----- Originalnachricht -----
>>> Von: "Mark Hammond"<mhammond at skippinet.com.au>
>>> Gesendet: Fre, 3/23/2012 2:43pm
>>> An: "Jan Wedel"<Jan.Wedel at ettex.de>
>>> Betreff: Re: AW: AW: AW: [python-win32] How to write a COM Server
>>> implementing interfaces from type lib?
>>>
>>> So - looking at the source, the win32com.client.Record object attempts
>>> to look up both the tlb guid and the record guid based on the name
>>> using
>>> that RecordMap dict we talked about. From there, we should wind up in
>>> win32com\src\PyRecord.cpp, where we use the typelib info and the struct
>>> GUID to call the COM function GetRecordInfoFromGuids(), which takes
>>> those GUIDs and returns an IRecordInfo interface that tells us all the
>>> struct element names and types etc.
>>>
>>> Obviously this is a "dynamic" process - other languages/tools etc are
>>> likely to use these struct definitions at compile time, which may
>>> explain why they have a NULL guid - they assume people wont need to
>>> look
>>> them up at runtime using GetRecordInfoFromGuids. It is late and I must
>>> hit bed, but it might be worth poking around the MS docs and see if
>>> they
>>> offer any way to get an IRecordInfo given just the typelib info and the
>>> struct name, as that seems the only info we have at the time we need
>>> it.
>>> If we can find something I'll try and add the support and send
>>> you a
>>> custom built pythoncomxx.dll.
>>>
>>> Cheers,
>>>
>>> Mark
>>>
>>> On 24/03/2012 12:06 AM, Jan Wedel wrote:
>>>> I tried to manually add the Records to the generated file to see if
>>>> patching genpy.py would solve the problem:
>>>>
>>>> RecordMap = {
>>>> u'tagOPCITEMVQT': '{00000000-0000-0000-0000-000000000000}',
>>>> u'tagOPCSERVERSTATE': '{00000000-0000-0000-0000-000000000000}',
>>>> u'tagOPCSERVERSTATUS':
>>>> '{00000000-0000-0000-0000-000000000000}',
>>>> }
>>>>
>>>> The client calls the GetStatus() method of my server which is
>>>> implemented as follows:
>>>>
>>>> def GetStatus(self):
>>>> """Returns the current server status"""
>>>> print "GetStatus()"
>>>>
>>>> # return None
>>>> status = Record("tagOPCSERVERSTATUS", self)
>>>>
>>>> status.ftStartTime = pywintypes.Time(self.start_time)
>>>> status.ftCurrentTime = pywintypes.Time(time.time())
>>>> status.ftLastUpdateTime =
>>>> pywintypes.Time(self.last_update_time)
>>>> status.dwServerState = ServerState.RUNNING
>>>> status.dwGroupCount = len(self.groups)
>>>> status.dwBandWidth = self.band_width
>>>> status.wMajorVersion = MAJOR_VERSION
>>>> status.wMinorVersion = MINOR_VERSION
>>>> status.wBuildNumber = BUILD_NUMBER
>>>> status.wReserved = 0
>>>> status.szVendorInfo = VENDOR_INFO
>>>>
>>>> return status
>>>>
>>>> with the following result:
>>>>
>>>> GetStatus()
>>>> pythoncom error: Failed to call the universal dispatcher
>>>>
>>>> Traceback (most recent call last):
>>>> File "C:\Program Files
>>>> (x86)\Python\lib\site-packages\win32com\universal.py",line 179, in
>>>> dispatch
>>>> retVal = ob._InvokeEx_(meth.dispid, 0, meth.invkind, args,
>>>> None, None)
>>>> File "C:\Program Files
>>>> (x86)\Python\lib\site-packages\win32com\server\policy.py", line
>>>> 346, in _InvokeEx_
>>>> return self._invokeex_(dispid, lcid, wFlags, args, kwargs,
>>>> serviceProvider)
>>>> File "C:\Program Files
>>>> (x86)\Python\lib\site-packages\win32com\server\policy.py", line
>>>> 650, in _invokeex_
>>>> return func(*args)
>>>> File "C:\temp\opc\PyOPCComServer.py", line 234, in GetStatus
>>>> status = Record("tagOPCSERVERSTATUS", self)
>>>> File "C:\Program Files
>>>> (x86)\Python\lib\site-packages\win32com\client\__init__.py", line
>>>> 399, in Record
>>>> object = gencache.EnsureDispatch(object)
>>>> File "C:\Program Files
>>>> (x86)\Python\lib\site-packages\win32com\client\gencache.py", line
>>>> 529, in EnsureDispatch
>>>> disp = win32com.client.Dispatch(prog_id)
>>>> File "C:\Program Files
>>>> (x86)\Python\lib\site-packages\win32com\client\__init__.py", line
>>>> 96, in Dispatch
>>>> return __WrapDispatch(dispatch, userName, resultCLSID,
>>>> typeinfo, clsctx=clsctx)
>>>> File "C:\Program Files
>>>> (x86)\Python\lib\site-packages\win32com\client\__init__.py", line
>>>> 43, in __WrapDispatch
>>>> return dynamic.Dispatch(dispatch, userName, WrapperClass,
>>>> typeinfo, clsctx=clsctx)
>>>> File "C:\Program Files
>>>> (x86)\Python\lib\site-packages\win32com\client\dynamic.py", line
>>>> 122, in Dispatch
>>>> typeinfo = IDispatch.GetTypeInfo()
>>>> AttributeError: EttexOPCServer instance has no attribute 'GetTypeInfo'
>>>>
>>>> pythoncom error: Failed to call the universal dispatcher
>>>>
>>>> Traceback (most recent call last):
>>>> File "C:\Program Files
>>>> (x86)\Python\lib\site-packages\win32com\universal.py",
>>>> line 195, in dispatch
>>>> WriteFromOutTuple(retVal, meth._gw_out_args, argPtr)
>>>> TypeError: The VARIANT type is unknown (0x4024).
>>>>
>>>> If found an example of how to create a Record. However, it does use
>>>> client COM and passes the object generated by Dispatch() to the
>>>> Record constructor. What object should I pass on the server side?
>>>> Obviously "self" doesn't work...
>>>>
>>>> Then I tried to return None instead which should leave the output
>>>> arguments untouch, but I only get the "TypeError: The VARIANT type
>>>> is unknown (0x4024)." message. The 0x4024 is 16420 and can be found
>>>> in the vtable definition of the generated file:
>>>>
>>>> IOPCServer_vtables_ = [
>>>> (...)
>>>> (( u'GetStatus' , u'ppServerStatus' , ), 1610678275,
>>>> (1610678275, (), [ (16420, 2, None, None) , ], 1 , 1 , 4 , 0 , 24 ,
>>>> (3, 0, None, None) , 0 , )),
>>>> (...)
>>>>
>>>> I was asking myself how does this 16420 from the vtable gets
>>>> translated into a "Ptr Ptr tagOPCSERVERSTATUS"? How does the record
>>>> definition know what fields it contains? Is is probably necessary
>>>> to add some more information to the Record such as this number,
>>>> e.g? Or does the internal COM libs uses the TypeLib information to
>>>> resolve it? It tried to look into the source of this type error but
>>>> the WriteFromOutTuple method is somewhere inside the pythoncom dll...
>>>>
>>>> I'm really stuck here...
>>>>
>>>> //Jan
>>>>
>>>>
>>>>
>>>> ----- Originalnachricht -----
>>>> Von: "Mark Hammond"<mhammond at skippinet.com.au>
>>>> Gesendet: Fre, 3/23/2012 12:11pm
>>>> An: "Jan Wedel"<Jan.Wedel at ettex.de>
>>>> Betreff: Re: AW: AW: [python-win32] How to write a COM Server
>>>> implementing interfaces from type lib?
>>>>
>>>> I haven't got the code in front of me, but in that place it would
>>>> probably be OK to use the name. I'm slightly worried about the
>>>> "global"
>>>> resolution though - there's probably code that can find a record given
>>>> just the GUID and without regard for which tlb it was defined in
>>>> (ie, so
>>>> the name itself need not be unique). In that case, a tuple of
>>>> (typelib_guid, name) would probably be OK.
>>>>
>>>> Mark
>>>>
>>>> On 23/03/2012 7:54 PM, Jan Wedel wrote:
>>>>> After I wrote my last mail, I did some further research on the
>>>>> problem. In genpy.py where the python file is generated for the
>>>>> type lib, I found this code:
>>>>>
>>>>> print>> stream, 'RecordMap = {'
>>>>> for record in recordItems.itervalues():
>>>>> if str(record.clsid) == pythoncom.IID_NULL:
>>>>> print>> stream, "\t###%s: %s, # Typedef disabled
>>>>> because it doesn't have a non-null GUID" % (repr(record.doc[0]),
>>>>> repr(str(record.clsid)))
>>>>> else:
>>>>> print>> stream, "\t%s: %s," %
>>>>> (repr(record.doc[0]), repr(str(record.clsid)))
>>>>> print>> stream, "}"
>>>>>
>>>>> I've checked the typelib with COMView, and all records, modules
>>>>> and enums defined have the UUID {00000-...00000} assigned. Then
>>>>> I've checked some random other type libs I've found on my computer
>>>>> and it seems that they are always using {0000...0000}. I don't
>>>>> know much about COM but I guess this must be OK. The type lib I'm
>>>>> using is made by the OPC foundation and there must be a numerous
>>>>> COM clients using it so it can't be that wrong, can it?
>>>>>
>>>>> The question is, is it possible to create a Record object in my
>>>>> server class and return it without the need of having a unique
>>>>> UUID assignedand without the need of beeing defined by genyp?
>>>>>
>>>>> If that doesn't work, I might also try to patch the genpy.py, but
>>>>> it seems as if you rely on the CLSID being unique. in the
>>>>> BuildOleItemsFromType method, there is the following code:
>>>>>
>>>>> elif infotype == pythoncom.TKIND_RECORD or infotype ==
>>>>> pythoncom.TKIND_UNION:
>>>>> newItem = RecordItem(info, attr, doc)
>>>>> recordItems[newItem.clsid] = newItem
>>>>>
>>>>> Because all records have the same clsid, they get overwritten by
>>>>> each other. There probably must be some other identification
>>>>> mechanism. Either we use an index or the record name itself... But
>>>>> then COM methods must be aware of these type in input and output
>>>>> parameters...
>>>>>
>>>>> Do have any suggestion on how to proceed (patch/work-around)?
>>>>>
>>>>> Thanks!
>>>>>
>>>>> //Jan
>>>>>
>>>>>
>>>>> ----- Originalnachricht -----
>>>>> Von: "Mark Hammond"<mhammond at skippinet.com.au>
>>>>> Gesendet: Fre, 3/23/2012 12:30am
>>>>> An: "Jan Wedel"<Jan.Wedel at ettex.de>
>>>>> Cc: python-win32 at python.org
>>>>> Betreff: Re: AW: [python-win32] How to write a COM Server
>>>>> implementing interfaces from type lib?
>>>>>
>>>>> On 23/03/2012 3:54 AM, Jan Wedel wrote:
>>>>>> I've actually managed to patch the policy.py to allow multiple
>>>>>> typelibraries.
>>>>>>
>>>>>> Instead of having a definition for interfaces, type library id,
>>>>>> version etc i've build this into one tuple. I've created a new
>>>>>> attribute that can have multiple of these tuples. The head of my
>>>>>> server class now looks like this:
>>>>>>
>>>>>>
>>>>>> class EttexOPCServer: _reg_progid_ = "Ettex.OPC.Automation"
>>>>>> _reg_desc_ = "ettex OPC DA Server" _reg_clsid_ =
>>>>>> "{13B51E8D-4BC2-4DED-8D4E-4614692F88E6}" _reg_catids_ = [
>>>>>> '{63D5F432-CFE4-11D1-B2C8-0060083BA1FB}' ]
>>>>>>
>>>>>> _typelib_interfaces_ = [ ("{3B540B51-0378-4551-ADCC-EA9B104302BF}",
>>>>>> 3, 0, 0, [ 'IOPCServer', 'IOPCItemProperties', ] ),
>>>>>> ("{B28EEDB1-AC6F-11D1-84D5-00608CB8A7E9}", 1, 0, 0, [ 'IOPCCommon',
>>>>>> 'IConnectionPointContainer' ] ), ] _reg_clsctx_ =
>>>>>> pythoncom.CLSCTX_LOCAL_SERVER
>>>>>>
>>>>>> I had to patch three locations in policy.py so far and I would be
>>>>>> happy to send you my changes if you like.
>>>>>
>>>>> That would be great - a patch on sourceforge would be best.
>>>>> However, it
>>>>> might be better to wait until we are sure it is working OK :)
>>>>>
>>>>>
>>>>>> However it still doesn't
>>>>>> work but I'm not sure if I have missed something in my patch or
>>>>>> if it
>>>>>> is a general problem in my server code or a bug in the framework.
>>>>>>
>>>>>> At least, the client successfully creates the object and tries to
>>>>>> call a method of the interface but I get the following debug output:
>>>>>>
>>>>>> GetStatus() pythoncom error: Failed to call the universal dispatcher
>>>>>>
>>>>>> Traceback (most recent call last): File "C:\Program Files
>>>>>> (x86)\Python\lib\site-packages\win32com\universal.py", line 195, in
>>>>>> dispatch WriteFromOutTuple(retVal, meth._gw_out_args, argPtr)
>>>>>> TypeError: The VARIANT type is unknown (0x4024). pythoncom error:
>>>>>> Unexpected gateway error
>>>>>>
>>>>>> Traceback (most recent call last): File "C:\Program Files
>>>>>> (x86)\Python\lib\site-packages\win32com\universal.py", line 195, in
>>>>>> dispatch WriteFromOutTuple(retVal, meth._gw_out_args, argPtr)
>>>>>> TypeError: The VARIANT type is unknown (0x4024). GetErrorString
>>>>>> -2147467259 0 pythoncom error: Failed to call the universal
>>>>>> dispatcher
>>>>>>
>>>>>> (...)
>>>>>>
>>>>>> I've hat a lookat the definition of GetStatus. It requires a pointer
>>>>>> to a pointer of type "tagOPCSERVERSTATUS" which is a record
>>>>>> definition in the type library. But when I look at what has been
>>>>>> generated by makepy, the record map looks pretty empty to me:
>>>>>>
>>>>>> RecordMap = { u'tagOPCITEMVQT':
>>>>>> '{00000000-0000-0000-0000-000000000000}', }
>>>>>>
>>>>>> The type lib defines 10 records! I tried to import the typelib using
>>>>>> comtypes and get that generated (excerpt):
>>>>>>
>>>>>> class tagOPCSERVERSTATUS(Structure): pass
>>>>>>
>>>>>> # values for enumeration 'tagOPCSERVERSTATE' OPC_STATUS_RUNNING = 1
>>>>>> OPC_STATUS_FAILED = 2 OPC_STATUS_NOCONFIG = 3 OPC_STATUS_SUSPENDED =
>>>>>> 4 OPC_STATUS_TEST = 5 OPC_STATUS_COMM_FAULT = 6 tagOPCSERVERSTATE =
>>>>>> c_int # enum tagOPCSERVERSTATUS._fields_ = [ ('ftStartTime',
>>>>>> _FILETIME), ('ftCurrentTime', _FILETIME), ('ftLastUpdateTime',
>>>>>> _FILETIME), ('dwServerState', tagOPCSERVERSTATE), ('dwGroupCount',
>>>>>> c_ulong), ('dwBandWidth', c_ulong), ('wMajorVersion', c_ushort),
>>>>>> ('wMinorVersion', c_ushort), ('wBuildNumber', c_ushort),
>>>>>> ('wReserved', c_ushort), ('szVendorInfo', WSTRING), ]
>>>>>>
>>>>>> Is there a bug in makepy that prevents creating these records?
>>>>>
>>>>> It would seem so :)
>>>>>
>>>>>> If
>>>>>> yes, can I fix it or can I manually change the generated file to
>>>>>> support the records?
>>>>>
>>>>> I'm really not sure - you probably need to look at the existing
>>>>> Record
>>>>> tests. The "PyCOMTest" test object (which is in the source tree and
>>>>> needs you to build it using MSVC) has some structs for testing.
>>>>>
>>>>>
>>>>>> If not, how would I create and return such a
>>>>>> pointer of a pointer in pythoncom? Is comtypes compatible with
>>>>>> pythoncom so I could use the comtypes generated files?
>>>>>
>>>>> Unfortunately there isn't any compatibility between the 2.
>>>>>
>>>>> Cheers,
>>>>>
>>>>> Mark
>>>>>>
>>>>>> Thanks!
>>>>>>
>>>>>> //Jan
>>>>>>
>>>>>> ----- Originalnachricht ----- Von: "Jan Wedel"<Jan.Wedel at ettex.de>
>>>>>> Gesendet: Don, 3/22/2012 1:51pm An: mhammond at skippinet.com.au Cc:
>>>>>> python-win32 at python.org Betreff: Re: [python-win32] How to write a
>>>>>> COM Server implementing interfaces from type lib?
>>>>>>
>>>>>> Hi Mark,
>>>>>>
>>>>>> thanks for your reply.
>>>>>>
>>>>>>> That's E_FAIL which is pretty generic. Doesn't sound like a
>>>>>>> simple failure to QI for the correct interface.
>>>>>>
>>>>>> Yeah. The client says something like "Unknown Error" which makes it
>>>>>> hard to tell what the problem actually is.
>>>>>>
>>>>>>> win32com should be able to do this given there is a tlb - see the
>>>>>>> "pippo" samples. Further, using the debug facilities and
>>>>>>> win32traceutil, you should be able to see the creation of the
>>>>>>> object, the QIs on the object and any methods actually called. But
>>>>>>> as mentioned, I doubt it is a QI failure.
>>>>>>
>>>>>> Yeah, there is a type library (OPC DA). Actually there are even two
>>>>>> type libraries, each of them contains two Interface which my server
>>>>>> needs to implement.
>>>>>>
>>>>>> I've had a look into policy.py and found that:
>>>>>>
>>>>>> def _build_typeinfos_(self): # Can only ever be one for now. (...)
>>>>>>
>>>>>> which means using _typelib_guid_ allows only one type lib, right? I
>>>>>> could try to patch your code or do you think there is a general
>>>>>> problem that would make it impossible having more than one
>>>>>> typelib in
>>>>>> your framework?
>>>>>>
>>>>>>> Have you tried contacting the author of the object?
>>>>>>
>>>>>> I am the author... I only use the 3rd party typelibs. I am writing
>>>>>> the server that must support the interfaces so that other 3rd party
>>>>>> clients can access the server component.
>>>>>>
>>>>>> I can reproduce the error using python the COMView tool trying to
>>>>>> instanciate the Server. The problem is, that I don't see why. I've
>>>>>> enabled debugging mode when registering the server. When using
>>>>>> one of
>>>>>> the 3rd party clients, rhe python trace collector shows the
>>>>>> following:
>>>>>>
>>>>>> Object with win32trace dispatcher created (object=None) Entering
>>>>>> constructor in<PyOPCComServer.EttexOPCServer instance at
>>>>>> 0x0213B058>._QueryInterface_ with unsupported IID
>>>>>> {00000003-0000-0000-C000-000000000046}
>>>>>> ({00000003-0000-0000-C000-000000000046})
>>>>>> in<PyOPCComServer.EttexOPCServer instance at
>>>>>> 0x0213B058>._QueryInterface_ with unsupported IID
>>>>>> {0000001B-0000-0000-C000-000000000046}
>>>>>> ({0000001B-0000-0000-C000-000000000046})
>>>>>> in<PyOPCComServer.EttexOPCServer instance at
>>>>>> 0x0213B058>._QueryInterface_ with unsupported IID
>>>>>> {00000018-0000-0000-C000-000000000046}
>>>>>> ({00000018-0000-0000-C000-000000000046})
>>>>>> in<PyOPCComServer.EttexOPCServer instance at
>>>>>> 0x0213B058>._QueryInterface_ with unsupported IID
>>>>>> {4C1E39E1-E3E3-4296-AA86-EC938D896E92}
>>>>>> ({4C1E39E1-E3E3-4296-AA86-EC938D896E92})
>>>>>> in<PyOPCComServer.EttexOPCServer instance at
>>>>>> 0x0213B058>._InvokeEx_-AddConnection(1, 0) [1,0,None] Connection
>>>>>> added. Active connections: 1 in<PyOPCComServer.EttexOPCServer
>>>>>> instance at 0x0213B058>._InvokeEx_-ReleaseConnection(1, 0, 1)
>>>>>> [1,0,None] Connection released. Active connections: 0
>>>>>>
>>>>>> The QI calls are standard COM calls, not application specific
>>>>>> (AFAIK). You can see the "Entering constructor" message which is in
>>>>>> my python constructor of the server. Just for fun, I implemented the
>>>>>> IExternalConnection interface to see if the methods are called.
>>>>>> "Connection added. ..." is my output. But I can't see an further
>>>>>> requests that shows what the problem is. Are there any calls in
>>>>>> pythoncom that could fail and does not give debug output?
>>>>>>
>>>>>> My pythoncom server starts like that:
>>>>>>
>>>>>> class EttexOPCServer: _reg_progid_ = "Ettex.OPC.Automation"
>>>>>> _reg_desc_ = "ettex OPC DA Server" _reg_clsid_ =
>>>>>> "{13B51E8D-4BC2-4DED-8D4E-4614692F88E6}" _reg_catids_ = [
>>>>>> '{63D5F432-CFE4-11D1-B2C8-0060083BA1FB}' ] _com_interfaces_ = [
>>>>>> '{00000019-0000-0000-C000-000000000046}', # IExternalConnection (Is
>>>>>> it really mandatory?) '{39C13A4D-011E-11D0-9675-0020AFD8ADB3}', #
>>>>>> IOPCServer '{F31DFDE2-07B6-11D2-B2D8-0060083BA1FB}', # IOPCCommon
>>>>>> '{39C13A72-011E-11D0-9675-0020AFD8ADB3}', # IOPCItemProperties
>>>>>> '{B196B284-BAB4-101A-B69C-00AA00341D07}' #
>>>>>> IConnectionPointContainer ] _typelib_guid_ =
>>>>>> "{3B540B51-0378-4551-ADCC-EA9B104302BF}" _typelib_version_ = (3, 0)
>>>>>> _reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER
>>>>>>
>>>>>> _public_methods_ = [ 'Connect', 'Disconnect', 'GetErrorString' ] ##
>>>>>> IExternalConnection methods _public_methods_ += [ 'AddConnection',
>>>>>> 'ReleaseConnection' ]
>>>>>>
>>>>>> ################################################################# ##
>>>>>> COM Class Attributes
>>>>>> #################################################################
>>>>>> _public_attrs_ = [ 'ClientName', 'OPCGroups' ]
>>>>>>
>>>>>> ################################################################# ##
>>>>>> COM Class Read-Only Attributes
>>>>>> #################################################################
>>>>>> _readonly_attrs_ = [ 'OPCGroups' ]
>>>>>>
>>>>>> I don't have all interface methods implemented at the time but I
>>>>>> guess that doesn't matter as long as not all type libs have been
>>>>>> loaded, does it?
>>>>>>
>>>>>> If its not possible with pythoncom, is it possible with comtypes?
>>>>>> I've tried that as well with nearly the same result ("Unknown
>>>>>> error"):
>>>>>>
>>>>>> # OPC Data Access 3.00 Type Library opc_da_tl =
>>>>>> comtypes.GUID("{3B540B51-0378-4551-ADCC-EA9B104302BF}") # OPC Common
>>>>>> 1.10 Type Library opc_com_tl =
>>>>>> comtypes.GUID("{B28EEDB1-AC6F-11D1-84D5-00608CB8A7E9}")
>>>>>>
>>>>>> GetModule((opc_da_tl, 3, 0)) GetModule((opc_com_tl, 1, 0))
>>>>>>
>>>>>> import comtypes.gen.OPCDA as OpcDa import comtypes.gen.OPCCOMN as
>>>>>> OpcCommon
>>>>>>
>>>>>> class EttexOPCServer2(OpcCommon.IOPCCommon,
>>>>>> OpcCommon.IConnectionPointContainer, OpcDa.IOPCServer,
>>>>>> OpcDa.IOPCItemProperties): _reg_progid_ = "Ettex.OPC.Automation2"
>>>>>> _reg_desc_ = "ettex OPC DA Server 2" _reg_novers_progid_ =
>>>>>> _reg_progid_ _reg_clsid_ = "{80A2B8F7-792E-43F4-95F8-CD6BB4B413AD}"
>>>>>> _reg_catids_ = [ '{63D5F432-CFE4-11D1-B2C8-0060083BA1FB}' ]
>>>>>> _reg_clsctx_ = comtypes.CLSCTX_LOCAL_SERVER _regcls_ =
>>>>>> comtypes.server.localserver.REGCLS_MULTIPLEUSE
>>>>>>
>>>>>> _com_interfaces_ = [OpcCommon.IOPCCommon,
>>>>>> OpcCommon.IConnectionPointContainer, OpcDa.IOPCServer,
>>>>>> OpcDa.IOPCItemProperties]
>>>>>>
>>>>>> I don't know what to do. Is there anything more I can do to debug to
>>>>>> find out WHAT exactly causes the error? Because as you can see, the
>>>>>> pythoncom debug output doesn't show this error that is returned by
>>>>>> the client.
>>>>>>
>>>>>> Thanks a lot!
>>>>>>
>>>>>> //Jan
>>>>>>
>>>>>> ----- Originalnachricht ----- Von: "Mark
>>>>>> Hammond"<skippy.hammond at gmail.com> Gesendet: Don, 3/22/2012
>>>>>> 12:29pm
>>>>>> An: "Jan Wedel"<Jan.Wedel at ettex.de> Cc: python-win32 at python.org
>>>>>> Betreff: Re: [python-win32] How to write a COM Server implementing
>>>>>> interfaces from type lib?
>>>>>>
>>>>>> On 22/03/2012 2:53 AM, Jan Wedel wrote:
>>>>>>> Hi,
>>>>>>>
>>>>>>> I'm currently having trouble to write a COM-Server that has some
>>>>>>> special requirements: - It needs to derive from IUnknown - It needs
>>>>>>> to implement multiple interface from two different proprietary
>>>>>>> typelibs (dlls) - It needs to implement a custom category
>>>>>>>
>>>>>>> At first I started with pythoncom. I used the attribute
>>>>>>> _reg_catids_ to specify the category and _com_interfaces_ to
>>>>>>> specify the interfaces I want to implement. The client (proprietary
>>>>>>> 3rd party sw, no source) sees the server but throws some 0x80004005
>>>>>>> error on CoCreateInstance.
>>>>>>
>>>>>> That's E_FAIL which is pretty generic. Doesn't sound like a simple
>>>>>> failure to QI for the correct interface.
>>>>>>
>>>>>> win32com should be able to do this given there is a tlb - see the
>>>>>> "pippo" samples. Further, using the debug facilities and
>>>>>> win32traceutil, you should be able to see the creation of the
>>>>>> object, the QIs on the object and any methods actually called. But
>>>>>> as mentioned, I doubt it is a QI failure.
>>>>>>
>>>>>>> I was hoping that I can just tell the COM dispatcher, "yes, I have
>>>>>>> these interface implemented" and implement the methods without
>>>>>>> really having the interface classes available.
>>>>>>>
>>>>>>> I read, that pythoncom can only create components that use
>>>>>>> IDispatch so I guess the _com_interfaces_ idea won't work, will
>>>>>>> it?
>>>>>>
>>>>>> As above, you should be able to fully implement them so long as
>>>>>> makepy has been run.
>>>>>>
>>>>>>> Then I did some further research and found comtypes. I tried to
>>>>>>> write a server stub again. I used GetModule to load the type
>>>>>>> library containing the Interfaces, importing the generated
>>>>>>> interface classes and let the main server class extend these
>>>>>>> interfaces.
>>>>>>>
>>>>>>> The first problem was, that comtypes did not support the
>>>>>>> _reg_catids_ attribute or anything similar so I had to add the
>>>>>>> Implemented Categories key manually to the registry. Then, I was
>>>>>>> able to see the server through the client, which obviously filters
>>>>>>> by categories, but it still shows the same error as before.
>>>>>>>
>>>>>>> So, what is the correct/best way to implement a server that needs
>>>>>>> to implement custom interfaces and categories? Or is it possible at
>>>>>>> all using python?
>>>>>>
>>>>>> The fact you get the same error there implies something else is
>>>>>> going wrong, but it is impossible to guess what. Have you tried
>>>>>> contacting the author of the object?
>>>>>>
>>>>>> Mark
>>>>>>
>>>>>>>
>>>>>>> Thanks!
>>>>>>>
>>>>>>> //Jan
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> _______________________________________________ python-win32
>>>>>>> mailing list python-win32 at python.org
>>>>>>> http://mail.python.org/mailman/listinfo/python-win32
>>>>>>
>>>>>> _______________________________________________ python-win32 mailing
>>>>>> list python-win32 at python.org
>>>>>> http://mail.python.org/mailman/listinfo/python-win32
>>>>>
>>>>
>>>
>>>
>>
>
>
More information about the python-win32
mailing list