Python, win32 COM: у меня есть имя класса, но я не могу создать клиент, «такой интерфейс не поддерживается

Я новичок в мире COM-интерфейсов Win32. Моя конечная цель — подключиться к серверу OPC. Я застреваю в самом начале процесса. Существует служба Win32 COM, которая перечисляет все доступные серверы OPC на моей машине (служба OPCEnum.exe), но я не могу получить к ней доступ из своей программы. Коммерческие инструменты OPC на моем компьютере могут выполнять эту задачу, поэтому я знаю, что энумератор OPC Server установлен и работает. Я вижу его в списке системных служб Windows.

Конфигурация моей системы: 64-разрядная Windows 7, 64-разрядная версия Python 3.7 в Anaconda, pywin32, установленный с использованием pip (поэтому он также должен быть 64-разрядным, хотя я не понял, как это проверить).

Вот минимальный пример кода:

import win32com.client as w32

def com_client_verbose(client_class_name):
    title = client_class_name + " client"
    print(title)
    print("=" * len(title))
    com_client = w32.gencache.EnsureDispatch(client_class_name)
    print(type(com_client))
    print(dir(com_client))
    print("\n\n")
    return com_client

def class_name_verbose(clsid):
    title = clsid + " module"
    print(title)
    print("=" * len(title))
    module = w32.gencache.EnsureModule(clsid, 0, 1, 0)
    print(type(module))
    path = module.__file__
    print(path)
    # INSPECT the CODE to obtain the class name. Is this really necessary?
    # https://stackoverflow.com/questions/17225798/python-win32com-dont-know-name-of-module
    with open(path, "r") as f:
        for line in f.readlines():
            if line.startswith("# This CoClass is known by the name"):
                print(line, "\n\n")
                return line.split("'")[1]
        else:
            raise RuntimeError("CoClass comment line not found.")

# Most Windows machines have Excel installed. Excel exposes a COM interface.
print("\n\n")
xl_client = com_client_verbose("Excel.Application")

# OPCEnum CLSID is documented at: http://www.opcti.com/dcom-error-for-clsid.aspx
# win32com.client.combrowse confirms that this CLSID is active on my system.
opc_enum_clsid = "{13486D43-4821-11D2-A494-3CB306C10000}"
opc_enum_class_name = class_name_verbose(opc_enum_clsid)

# Now that we have a class name for OPCEnum, try to create its COM client.
# This fails.
opc_enum_client = com_client_verbose(opc_enum_class_name)

Вот результат. Выглядит хорошо до самой последней строчки.

Excel.Application client
========================
<class 'win32com.gen_py.00020813-0000-0000-C000-000000000046x0x1x9._Application.
_Application'>
['ActivateMicrosoftApp', 'AddChartAutoFormat', 'AddCustomList', 'CLSID', 'Calcul
ate', 'CalculateFull', 'CalculateFullRebuild', 'CalculateUntilAsyncQueriesDone',
 'CentimetersToPoints', 'CheckAbort', 'CheckSpelling', 'ConvertFormula', 'DDEExe
cute', 'DDEInitiate', 'DDEPoke', 'DDERequest', 'DDETerminate', 'DeleteChartAutoF
ormat', 'DeleteCustomList', 'DisplayXMLSourcePane', 'DoubleClick', 'Dummy1', 'Du
mmy10', 'Dummy11', 'Dummy12', 'Dummy13', 'Dummy14', 'Dummy2', 'Dummy20', 'Dummy3
', 'Dummy4', 'Dummy5', 'Dummy6', 'Dummy7', 'Dummy8', 'Dummy9', 'Evaluate', 'Exec
uteExcel4Macro', 'FileDialog', 'FindFile', 'GetCaller', 'GetClipboardFormats', '
GetCustomListContents', 'GetCustomListNum', 'GetFileConverters', 'GetInternation
al', 'GetOpenFilename', 'GetPhonetic', 'GetPreviousSelections', 'GetRegisteredFu
nctions', 'GetSaveAsFilename', 'Goto', 'Help', 'InchesToPoints', 'InputBox', 'In
tersect', 'MacroOptions', 'MailLogoff', 'MailLogon', 'NextLetter', 'OnKey', 'OnR
epeat', 'OnTime', 'OnUndo', 'Quit', 'Range', 'RecordMacro', 'RegisterXLL', 'Repe
at', 'ResetTipWizard', 'Run', 'Save', 'SaveWorkspace', 'SendKeys', 'SetDefaultCh
art', 'SharePointVersion', 'ShortcutMenus', 'Support', 'Undo', 'Union', 'Volatil
e', 'Wait', '_ApplyTypes_', '_Evaluate', '_FindFile', '_MacroOptions', '_Run2',
'_WSFunction', '_Wait', '__call__', '__class__', '__delattr__', '__dict__', '__d
ir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattr__', '__getattribu
te__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__int__', '__iter
__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__red
uce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__
', '__weakref__', '_get_good_object_', '_get_good_single_object_', '_oleobj_', '
_prop_map_get_', '_prop_map_put_', 'coclass_clsid']



{13486D43-4821-11D2-A494-3CB306C10000} module
=============================================
<class 'module'>
C:\Users\FOO\AppData\Local\Temp\gen_py\3.7\13486D43-4821-11D2-A494-3CB306
C10000x0x1x1.py
# This CoClass is known by the name 'OPC.ServerList.1'



OPC.ServerList.1 client
=======================
Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\lib\site-packages\win32com\client\dynamic.py",
line 89, 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 "C:\ProgramData\Anaconda3\lib\runpy.py", line 183, in _run_module_as_main
    mod_name, mod_spec, code = _get_module_details(mod_name, _Error)
  File "C:\ProgramData\Anaconda3\lib\runpy.py", line 109, in _get_module_details
    __import__(pkg_name)
  File "C:\Users\foo\Documents\OPC\win32com minimal example.py", line 49, in <module>
    opc_enum_client = com_client_verbose(opc_enum_class_name)
  File "C:\Users\foo\Documents\OPC\win32com minimal example.py", line 14, in com_client_verbose
    com_client = w32.gencache.EnsureDispatch(client_class_name)
  File "C:\ProgramData\Anaconda3\lib\site-packages\win32com\client\gencache.py", line 527, in EnsureDispatch
    disp = win32com.client.Dispatch(prog_id)
  File "C:\ProgramData\Anaconda3\lib\site-packages\win32com\client\__init__.py", line 95, in Dispatch
    dispatch, userName = dynamic._GetGoodDispatchAndUserName(dispatch,userName,clsctx)
  File "C:\ProgramData\Anaconda3\lib\site-packages\win32com\client\dynamic.py", line 114, in _GetGoodDispatchAndUserName
    return (_GetGoodDispatch(IDispatch, clsctx), userName)
  File "C:\ProgramData\Anaconda3\lib\site-packages\win32com\client\dynamic.py", line 91, in _GetGoodDispatch
    IDispatch = pythoncom.CoCreateInstance(IDispatch, None, clsctx, pythoncom.IID_IDispatch)
pywintypes.com_error: (-2147467262, 'No such interface supported', None, None)

Шаг 1, мой положительный контроль: если я знаю имя класса COM, например «Excel.Application», я могу вернуть объект живого кода в его COM-интерфейс.

Шаг 2: Если я знаю CLSID, но не знаю имя класса COM, я могу получить это имя. Мне не нравится способ, которым я должен это сделать - путем проверки самого скрипта Python на наличие строки COMMENT - но это то, что было рекомендовано в этот комментарий StackOverflow, и, похоже, он работает.

Шаг 3 не работает. Несмотря на то, что я получил имя класса COM «OPC.ServerList.1» из CLSID, Windows/pywin32 сообщает мне, что с этим именем не связан интерфейс.

Я также попробовал имя класса «OPC.ServerList» без «.1», думая, что «.1» может быть каким-то номером экземпляра. Это тоже не удалось.

Любые советы приветствуются, спасибо!


person John Ladasky    schedule 08.05.2020    source источник


Ответы (1)


COM имеет два (три) вида интерфейсов: Пользовательские интерфейсы (сигнатуры методов и типы в них могут быть практически любыми), интерфейсы automation (должны быть производными от IDispatch и вид метода и выбор типа ограничены). Также существуют двойные интерфейсы (интерфейсы, удовлетворяющие ограничениям обоих предыдущих типов).

Все интерфейсы в OPCEnum, а также все интерфейсы серверов OPC Classic являются настраиваемыми интерфейсами.

Я не эксперт по Python, но, насколько мне известно, win32com поддерживает только интерфейсы automation. Таким образом, вы не можете использовать win32com с серверами OPCEnum или OPC Classic.

Я немного погуглил и нашел это: https://www.codeproject.com/Articles/22563/Working-with-custom-COM-interfaces-from-Python . Я не уверен, работает ли он, но он определенно содержит интересную информацию, которая может продвинуть вас вперед. (tl; dr: предлагается использовать «comtypes» вместо «pythoncom»).

person ZbynekZ    schedule 09.05.2020
comment
Спасибо за ответ, @ZbynekZ. Я нашел другой пакет Python под названием OpenOPC, который обрабатывает детали Win32 COM и позволяет программисту сосредоточиться на OPC (pypi. org/project/OpenOPC-Python3x). Вам также потребуется работающая в вашей системе библиотека DLL, которая обрабатывает автоматизацию данных OPC. Есть несколько проприетарных DLL, но есть и бесплатная (хотя и с закрытым исходным кодом), доступная в программном обеспечении GrayBox по адресу: gray-box.net/daawrapper.php?lang=en - person John Ladasky; 21.05.2020