Я пытаюсь создать зарегистрированный COM-класс Python, который позволяет обратному вызову в моем основном потоке запускаться таймером в отдельном потоке.По сути, я хочу асинхронно вызывать методы VBA из python (поскольку VBA сама не может делать это очень хорошо).Я, вероятно, делаю какую-то ошибку новичка, но вот код python:
from threading import Timer
import pythoncom
import win32com.client as client
from win32com.server.register import UseCommandLine
class PythonUtilities:
_public_methods_ = [ "Test", ]
_reg_progid_ = "PythonDemos.Utilities"
_reg_clsid_ = "{C1DFDB56-C8B4-4024-A04F-22DF4DEE6C61}" #pythoncom.CreateGuid()
@staticmethod
def Test(callbackObj):
pythoncom.CoInitialize() #IDK!
callbackRoutine = client.Dispatch(callbackObj).tick
thread = Timer(1.0, callbackRoutine) #CoInitialize needs to go on this thread surely?
thread.start()
if __name__== "__main__":
print("Registering COM server...")
UseCommandLine(PythonUtilities)
Я запускаю это, делая экземпляр класса PythonUtilities
в потоке вызывающей стороны.Этот поток также содержит callbackObj
с методом .tick
.
Я создаю новый поток с помощью Timer
и регистрирую его для вызова метода tick
.Однако у меня есть ощущение, что потоку таймера необходимо CoInitialize
для загрузки информации COM, необходимой для использования win32com.client.Dispatch
object
Я получаю сообщение об ошибке:
File "<COMObject <unknown>>", line 2, in tick
pywintypes.com_error: (-2147221008, 'CoInitialize has not been called.', None, None)
из метода тиков, поэтому я угадываю, какой из тиковых потоков выполняется в CoInitializing.Вот код вызывающего абонента (в VBA, стандартный модуль)
Public callbackObj As Callback 'declare up here so they don't fall out of scope
Public pyTest As Object
Sub test()
Set pyTest = CreateObject("PythonDemos.Utilities")
Set callbackObj = New Callback
On Error Resume Next
pyTest.test callbackObj 'call the python object asynchronously
If Err.Number <> 0 Then Debug.Print Err.Description 'print error info if there is any
Debug.Print "Here"
End Sub
и класс Callback
выглядит как
Public Sub tick()
Debug.Print "Called"
End Sub
, который будет бороться за вызов любого pythoncom.CoInitialize()
, но может использоватьapi напрямую, или, надеюсь, я справлюсь с этим на стороне python.Или, может быть, я неправильно диагностировал проблему!
PS Я делаю это в VBA, возможно, это будет аналогичная история в других языках, использующих COM, так что если вы видите, что делатьвместо этого я уверен, что смогу перевести себя позже:)