Python Forum

Full Version: Help for COM server implementation ("Unspecified error" on Dispatch())
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I'm trying to implement a COM server that will expose some methods, in order to link a third-party API with a PyQt application. The application is a grammar tool called Antidote. The server was registered successfully, but then I need to instantiate it and pass its object to Antidote's API. Thus;

    def _connect(self):
        self.server = win32com.client.Dispatch("Correcteur.Antidote")  # , clsctx=pythoncom.CLSCTX_LOCAL_SERVER)
        self.antidote = win32com.client.Dispatch("Antidote.ApiOle")
        self.antidote.LanceOutilDispatch2(self.server, "C", "", "2.0")  # Server, outil, langue, version API
It successfully find the COM object in registry with the provided progid (Correcteur.Antidote), but it crash with a bunch of gibberish errors;

Error:
Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. C:\Users\user>"C:\Users\user\Desktop\AntidoteCOM.py - Shortcut.lnk" pythoncom error: ERROR: server.policy could not create an instance. Traceback (most recent call last): File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\server\policy.py", line 136, in CreateInstance return retObj._CreateInstance_(clsid, reqIID) File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\server\policy.py", line 194, in _CreateInstance_ myob = call_func(classSpec) File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\server\policy.py", line 728, in call_func return resolve_func(spec)(*args) TypeError: __init__() missing 1 required positional argument: 'parent' pythoncom error: Unexpected gateway error Traceback (most recent call last): File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\server\policy.py", line 136, in CreateInstance return retObj._CreateInstance_(clsid, reqIID) File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\server\policy.py", line 194, in _CreateInstance_ myob = call_func(classSpec) File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\server\policy.py", line 728, in call_func return resolve_func(spec)(*args) TypeError: __init__() missing 1 required positional argument: 'parent' pythoncom error: CPyFactory::CreateInstance failed to create instance. (80004005) Traceback (most recent call last): File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\client\dynamic.py", line 81, in _GetGoodDispatch IDispatch = pythoncom.connect(IDispatch) pywintypes.com_error: (-2147221021, 'Operation unavailable', None, None) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "Z:\.antidote_\AntidoteCOM.py", line 44, in _connect self.server = win32com.client.Dispatch("Correcteur.Antidote") # , clsctx=pythoncom.CLSCTX_LOCAL_SERVER) File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\client\__init__.py", line 95, in Dispatch dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch,userName,clsctx) File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\client\dynamic.py", line 98, in _GetGoodDispatchAndUserName return (_GetGoodDispatch(IDispatch, clsctx), userName) File "C:\Users\user\AppData\Local\Programs\Python\Python38\lib\site-packages\win32com\client\dynamic.py", line 83, in _GetGoodDispatch IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx, pythoncom.IID_IDispatch) pywintypes.com_error: (-2147467259, 'Unspecified error', None, None)
From some examples I've seen, I tried to run the server beforehand in another thread, but the same error remains.
    def run(self):
        pythoncom.CoInitialize()
        localserver.serve(['{D390AE78-D6A2-47CF-B462-E4F2DC9C70F5}'])  # ##
I suppose something is wrong in my COM object class (AdapteurAntidote) ?

Full code below;
#!/usr/bin/python3
import pythoncom
import winreg
import win32com.client
import win32com.server.register
from win32com.server import localserver
from pathlib import Path
from PyQt5 import QtCore, QtWidgets


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.body = QtWidgets.QTextEdit("Texte à corrigée.")
        self.setCentralWidget(self.body)
        self.antidote = ImplementationAntidote(self, self.body)


class ImplementationAntidote(QtCore.QObject):
    def __init__(self, parent: QtCore.QObject, body: QtWidgets.QTextEdit):
        super().__init__()
        self.parent = parent
        self.body = body
        # self._register()

        self._launch()
        self.worker = WorkerThread()
        self.workerThread = QtCore.QThread()
        self.workerThread.started.connect(self.worker.run)
        self.worker.moveToThread(self.workerThread)
        self.workerThread.start()
        QtCore.QTimer.singleShot(3000, self._connect)

    @staticmethod
    def hkey(path: str, name: str):
        registry_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path, 0, winreg.KEY_READ)
        value, regtype = winreg.QueryValueEx(registry_key, name)
        winreg.CloseKey(registry_key)
        return value

    def _connect(self):
        self.server = win32com.client.Dispatch("Correcteur.Antidote")  # , clsctx=pythoncom.CLSCTX_LOCAL_SERVER)  # ## Crash
        self.antidote = win32com.client.Dispatch("Antidote.ApiOle")
        self.antidote.LanceOutil2(self.server, "C", "", "2.0")  # Server, outil, langue, version API

    def _launch(self):
        # antidoteAPI = self.hkey(r"Software\Druide informatique inc.\Antidote", "VersionAPI")
        antidoteFolder = self.hkey(r"Software\Druide informatique inc.\Antidote", "DossierAntidote")
        antidotePath = Path(antidoteFolder) / "Antidote.exe"
        QtCore.QProcess.startDetached(str(antidotePath), ["-activex"])

    def _register(self):
        win32com.server.register.UseCommandLine(AdapteurAntidote)


class WorkerThread(QtCore.QObject):
    def __init__(self):
        super().__init__()

    def run(self):
        pythoncom.CoInitialize()
        localserver.serve(['{D390AE78-D6A2-47CF-B462-E4F2DC9C70F5}'])  # ##


class AdapteurAntidote:
    _reg_clsctx_ = pythoncom.CLSCTX_LOCAL_SERVER  # https://msdn.microsoft.com/en-us/library/windows/desktop/ms693716(v=vs.85).aspx
    _reg_clsid_ = "{D390AE78-D6A2-47CF-B462-E4F2DC9C70F5}"  # pythoncom.CreateGuid()
    _reg_progid_ = "Correcteur.Antidote"
    _public_methods_ = ["ActiveApplication", "ActiveDocument", "DonneDebutSelection", "DonneFinSelection",
                        "DonneIdDocumentCourant", "DonneIdZoneDeTexte", "DonneIdZoneDeTexteCourante", "DonneIntervalle",
                        "DonneLongueurZoneDeTexte", "DonneNbZonesDeTexte", "DonnePolice", "DonneTitreDocCourant",
                        "RemplaceIntervalle", "SelectionneIntervalle"]
    _public_attrs_ = []
    _reg_verprogid_ = "Correcteur.Antidote.1"
    _reg_class_spec_ = "AntidoteCOM.AdapteurAntidote"

    def __init__(self, parent):
        super().__init__()
        self.parent = parent

    def DonneIdDocumentCourant(self):
        raise NotImplementedError

    def DonneIdZoneDeTexte(self):
        raise NotImplementedError

    def DonneIdZoneDeTexteCourante(self):
        raise NotImplementedError

    def DonneIntervalle(self):
        raise NotImplementedError

    def DonneLongueurZoneDeTexte(self):
        raise NotImplementedError

    def DonneNbZonesDeTexte(self):
        raise NotImplementedError

    def DonnePolice(self):
        raise NotImplementedError

    def DonneTitreDocCourant(self):
        raise NotImplementedError

    def RemplaceIntervalle(self):
        raise NotImplementedError

    def ActiveDocument(self):
        self.parent.activeDocument()

    def ActiveApplication(self):
        self.parent.activeDocument()

    def DonneDebutSelection(self) -> int:
        return self.parent.donneDebutSelection()

    def DonneFinSelection(self) -> int:
        return self.parent.donneFinSelection()

    def SelectionneIntervalle(self, debut: int, fin: int):
        self.parent.selectionneIntervalle(debut, fin)


if __name__ == '__main__':
    app = QtWidgets.QApplication([])
    gui = MainWindow()
    gui.show()
    app.exec()
Your AdapteurAntidote expects parent argument at instantiation (in __init__). It does not pass parent when create the instance when register and you get TypeError: __init__() missing 1 required positional argument: 'parent' (first in the list of errors).
Try to implemement your class without parent in __init__ or look how to pass that argument when register (I don't know how/is it possible).
Thanks, looking back it seems obvious but I missed it for some reason. Now I have to find a way to pass the parent argument, as the COM object refuse the pointer.