Windows 10 + Python 3.6.3 64-разрядная версия (также пробовал 32-разрядную версию). Я разработчик Python, пытаюсь использовать COM (почти) впервые и столкнулся с этим огромным блокировщиком.
Проблема
У меня возникали различные ошибки при попытке использовать IRTDServer, реализованный в dll (написанной не мной), через win32com или comtypes. Использовать win32com оказалось сложнее. Ниже я включил пример модульного теста для обеих библиотек.
Доступ к серверу из Excel 2016 работает как положено; это возвращает ожидаемое значение:
Вот простой тестовый пример, который должен подключиться к серверу, но не подключается. (Это всего лишь одна версия, так как я много раз менял ее, пытаясь отладить проблему.)
Traceback (most recent call last):
File "env\lib\site-packages\win32com\client\gencache.py", line 532, in EnsureDispatch
ti = disp._oleobj_.GetTypeInfo()
pywintypes.com_error: (-2147467263, 'Not implemented', None, None)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "test\test.py", line 23, in test_win32com
_rtd.connect()
File "test\test.py", line 16, in connect
self._rtd = win32com.client.CastTo(dispatch, 'IRtdServer')
File "env\lib\site-packages\win32com\client\__init__.py", line 134, in CastTo
ob = gencache.EnsureDispatch(ob)
File "env\lib\site-packages\win32com\client\gencache.py", line 543, in EnsureDispatch
raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
TypeError: This COM object can not automate the makepy process - please run makepy manually for this object
Следуя этим инструкциям, я успешно запустил скрипт makepy:
> env\Scripts\python.exe env\lib\site-packages\win32com\client\makepy.py "foo.bar"
Generating to C:\Users\user1\AppData\Local\Temp\gen_py\3.5\longuuid1x0x1x0.py
Building definitions from type library...
Generating...
Importing module
(Я заменил UUID в stackoverflow в целях конфиденциальности. Этот UUID совпадает с UUID библиотеки типов для «foo.bar».)< /p>
Сгенерированный файл содержит различные определения функций и типов IRTdServer и IRTDUpdateEvent. Но в этом файле оба интерфейса являются подклассами win32com.client.DispatchBaseClass, тогда как по OleViewDotNet они должны быть подклассами IUnknown?
Однако, когда я попытался снова запустить модульный тест, я получил ту же ошибку, что и раньше. Как будто механизм поиска не находит сгенерированный модуль?
Кроме того, меня тревожит возврат GetTypeInfo «Не реализовано». Насколько я понимаю, win32com использует этот метод (часть COM-интерфейса IDispatch) для определения типов аргументов и возвращаемых значений для всех других функций в других интерфейсах, включая IRtdServer. Если он не реализован, он не сможет правильно определить типы. Тем не менее, сгенерированный файл, похоже, содержит эту информацию, что также вызывает недоумение.
Казалось бы, это указывает на то, что функция ServerStart существует, но она не определена? (Выглядит очень странно. В этой загадке должно быть что-то еще.)
Попробовал передать параметр интерфейса="IRtdServer" в CreateObject :
File "test\test.py", line 13, in __init__
self._comObj = comtypes.client.CreateObject(clsid, interface="IRtdServer")
File "env\lib\site-packages\comtypes\client\__init__.py", line 238, in CreateObject
obj = comtypes.CoCreateInstance(clsid, clsctx=clsctx, interface=interface)
File "env\lib\site-packages\comtypes\__init__.py", line 1223, in CoCreateInstance
p = POINTER(interface)()
TypeError: Cannot create instance: has no _type_
Код трассировки в библиотеке comtypes, который, казалось бы, указывает на то, что параметру интерфейса нужен класс интерфейса, а не строка. Я нашел различные интерфейсы, определенные в библиотеке comtypes: IDispatch, IPersist, IServiceProvider. Все они являются подклассами IUnknown. Согласно OleViewDotNet, IRTdServer также является подклассом IUnknown. Это наводит меня на мысль, что мне нужно аналогичным образом написать класс IRTdServer на Python, чтобы использовать интерфейс, но я не знаю, как это сделать.
Я заметил динамический параметр CreateObject. Код указывает, что это взаимоисключающее значение параметра интерфейса, поэтому я попробовал это:
Windows 10 + Python 3.6.3 64-разрядная версия (также пробовал 32-разрядную версию). Я разработчик Python, пытаюсь использовать COM (почти) впервые и столкнулся с этим огромным блокировщиком.
Проблема
У меня возникали различные ошибки при попытке использовать IRTDServer, реализованный в dll (написанной не мной), через win32com или comtypes. Использовать win32com оказалось сложнее. Ниже я включил пример модульного теста для обеих библиотек.
Доступ к серверу из Excel 2016 работает как положено; это возвращает ожидаемое значение:
Вот простой тестовый пример, который должен подключиться к серверу, но не подключается. (Это всего лишь одна версия, так как я много раз менял ее, пытаясь отладить проблему.)
[code]from unittest import TestCase
class COMtest(TestCase): def test_win32com(self): import win32com.client from win32com.server.util import wrap
class RTDclient: # are these only required when implementing the server? _com_interfaces_ = ["IRTDUpdateEvent"] _public_methods_ = ["Disconnect", "UpdateNotify"] _public_attrs_ = ["HeartbeatInterval"]
[code]Traceback (most recent call last): File "env\lib\site-packages\win32com\client\gencache.py", line 532, in EnsureDispatch ti = disp._oleobj_.GetTypeInfo() pywintypes.com_error: (-2147467263, 'Not implemented', None, None)
During handling of the above exception, another exception occurred:
Traceback (most recent call last): File "test\test.py", line 23, in test_win32com _rtd.connect() File "test\test.py", line 16, in connect self._rtd = win32com.client.CastTo(dispatch, 'IRtdServer') File "env\lib\site-packages\win32com\client\__init__.py", line 134, in CastTo ob = gencache.EnsureDispatch(ob) File "env\lib\site-packages\win32com\client\gencache.py", line 543, in EnsureDispatch raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object") TypeError: This COM object can not automate the makepy process - please run makepy manually for this object [/code]
Следуя этим инструкциям, я успешно запустил скрипт makepy:
[code]> env\Scripts\python.exe env\lib\site-packages\win32com\client\makepy.py "foo.bar" Generating to C:\Users\user1\AppData\Local\Temp\gen_py\3.5\longuuid1x0x1x0.py Building definitions from type library... Generating... Importing module [/code]
(Я заменил UUID в stackoverflow в целях конфиденциальности. Этот UUID совпадает с UUID библиотеки типов для «foo.bar».)< /p>
Сгенерированный файл содержит различные определения функций и типов IRTdServer и IRTDUpdateEvent. Но в этом файле оба интерфейса являются подклассами win32com.client.DispatchBaseClass, тогда как по OleViewDotNet они должны быть подклассами IUnknown?
Однако, когда я попытался снова запустить модульный тест, я получил ту же ошибку, что и раньше. Как будто механизм поиска не находит сгенерированный модуль?
Кроме того, меня тревожит возврат GetTypeInfo «Не реализовано». Насколько я понимаю, win32com использует этот метод (часть COM-интерфейса IDispatch) для определения типов аргументов и возвращаемых значений для всех других функций в других интерфейсах, включая IRtdServer. Если он не реализован, он не сможет правильно определить типы. Тем не менее, сгенерированный файл, похоже, содержит эту информацию, что также вызывает недоумение.
Код с использованием библиотеки comtypes
[code]from unittest import TestCase
class COMtest(TestCase): def test_comtypes(self): import comtypes.client
class RTDclient: # are these for win32com only? _com_interfaces_ = ["IRTDUpdateEvent"] _public_methods_ = ["Disconnect", "UpdateNotify"] _public_attrs_ = ["HeartbeatInterval"]
def __init__(self, clsid): self._comObj = comtypes.client.CreateObject(clsid) def connect(self): self._rtd = self._comObj.IRtdServer() result = self._rtd.ServerStart(self) assert result > 0
[code] File "test\test.py", line 27, in test_comtypes _rtd.connect() File "test\test.py", line 16, in connect self._rtd = self._comObj.IRTDServer() File "env\lib\site-packages\comtypes\client\dynamic.py", line 110, in __getattr__ dispid = self._comobj.GetIDsOfNames(name)[0] File "env\lib\site-packages\comtypes\automation.py", line 708, in GetIDsOfNames self.__com_GetIDsOfNames(riid_null, arr, len(names), lcid, ids) _ctypes.COMError: (-2147352570, 'Unknown name.', (None, None, None, 0, None)) [/code]
Некоторые другие решения, которые я пробовал
( На основе поиска в Google и ответов в комментариях ниже)
[list] [*](Пере-)Зарегистрировал DLL [*] Зарегистрировал 32-битную версию DLL и попробовал 32-битную версию Python [*]Установить режим совместимости python.exe с Windows XP SP3 [*]Попробовал не создавать экземпляр IRtdServer, то есть заменить эти две строки:
[code]self._rtd = self._comObj.IRtdServer() result = self._rtd.ServerStart(self) [/code]
[code]TypeError: 'NoneType' object is not callable [/code]
Казалось бы, это указывает на то, что функция ServerStart существует, но она не определена? (Выглядит очень странно. В этой загадке должно быть что-то еще.) [*]Попробовал передать параметр интерфейса="IRtdServer" в CreateObject :
[code] File "test\test.py", line 13, in __init__ self._comObj = comtypes.client.CreateObject(clsid, interface="IRtdServer") File "env\lib\site-packages\comtypes\client\__init__.py", line 238, in CreateObject obj = comtypes.CoCreateInstance(clsid, clsctx=clsctx, interface=interface) File "env\lib\site-packages\comtypes\__init__.py", line 1223, in CoCreateInstance p = POINTER(interface)() TypeError: Cannot create instance: has no _type_ [/code]
Код трассировки в библиотеке comtypes, который, казалось бы, указывает на то, что параметру интерфейса нужен класс интерфейса, а не строка. Я нашел различные интерфейсы, определенные в библиотеке comtypes: IDispatch, IPersist, IServiceProvider. Все они являются подклассами IUnknown. Согласно OleViewDotNet, IRTdServer также является подклассом IUnknown. Это наводит меня на мысль, что мне нужно аналогичным образом написать класс IRTdServer на Python, чтобы использовать интерфейс, но я не знаю, как это сделать. [*]Я заметил динамический параметр CreateObject. Код указывает, что это взаимоисключающее значение параметра интерфейса, поэтому я попробовал это: