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