Я новичок в мире COM-интерфейсов Win32. Моя конечная цель — подключиться к OPC-серверу. Я застреваю на ранних этапах процесса. Существует служба Win32 COM, которая перечисляет все доступные серверы OPC на моем компьютере (служба OPCEnum.exe), но я не могу получить к ней доступ из своей программы. Коммерческие инструменты OPC на моем компьютере могут выполнить эту задачу, поэтому я знаю, что перечислитель OPC-сервера установлен и работает. Я вижу его в списке системных служб 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
========================
['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
=============================================
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
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 на наличие строки КОММЕНТАРИЯ — но это то, что было рекомендовано в этом комментарии StackOverflow, и, похоже, это работает.
Шаг 3 не удался. Несмотря на то, что я получил имя COM-класса «OPC.ServerList.1» из CLSID, Windows/pywin32 сообщает мне, что с этим именем не связан ни один интерфейс.
Я также попробовал имя класса «OPC.ServerList» без «.1», думая, что «.1» может быть каким-то номером экземпляра. Это также не удалось.
Я новичок в мире COM-интерфейсов Win32. Моя конечная цель — подключиться к OPC-серверу. Я застреваю на ранних этапах процесса. Существует служба Win32 COM, которая перечисляет все доступные серверы OPC на моем компьютере (служба OPCEnum.exe), но я не могу получить к ней доступ из своей программы. Коммерческие инструменты OPC на моем компьютере могут выполнить эту задачу, поэтому я знаю, что перечислитель OPC-сервера установлен и работает. Я вижу его в списке системных служб Windows.
Конфигурация моей системы: 64-разрядная версия Windows 7, 64-разрядная версия Python 3.7 в Anaconda, pywin32, установленный с помощью pip (поэтому он также должен быть 64-разрядным, хотя я не понял, как это проверить).
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) [/code]
Вот результат. Выглядит хорошо до самой последней строки.
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 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) [/code]
Шаг 1, мой положительный контроль: если я знаю имя COM-класса, например «Excel.Application», я могу вернуть объект живого кода в его COM-интерфейс.
Шаг 2. Если я знаю CLSID, но не имя COM-класса, я могу получить имя. Мне не нравится, как мне приходится это делать — проверяя сам скрипт Python на наличие строки КОММЕНТАРИЯ — но это то, что было рекомендовано в этом комментарии StackOverflow, и, похоже, это работает.
Шаг 3 не удался. Несмотря на то, что я получил имя COM-класса «OPC.ServerList.1» из CLSID, Windows/pywin32 сообщает мне, что с этим именем не связан ни один интерфейс.
Я также попробовал имя класса «OPC.ServerList» без «.1», думая, что «.1» может быть каким-то номером экземпляра. Это также не удалось.