Не уверен, что это оптимальный способ сделать это, но он работает.
Идея такова:
- Создать временный класс, который обернет макрос, который вы намереваетесьcall.
- Создать функцию, которая создает экземпляр временного класса
- Вызвать вновь созданную функцию
Затем Python может зафиксировать любую ошибку, которую выдает этот класс.
ПРИМЕЧАНИЕ: Макрос все еще может зависнуть, если у макроса есть всплывающее окно MsgBox
from win32com import client as win32client
import clr
clr.AddReference('Microsoft.Vbe.Interop, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c')
clr.AddReference('Microsoft.VisualBasic')
from Microsoft.Vbe.Interop import vbext_ComponentType
def run_macro(xl, wb, sheetname, macro_name):
# Dynamically add VBA class to workbook
f = wb.VBProject.VBComponents.Add(vbext_ComponentType.vbext_ct_ClassModule)
# Avoid duplicate names
tempGuid = str(uuid.uuid4())[:8]
f.Properties.Item("Instancing").Value = 2
f.Name = "CLS_" + tempGuid
f.CodeModule.AddFromString(
"Public Sub TempClassCall()\r\n" +
" Call " + macro_name +
"\r\n" +
"End Sub\r\n")
# Dynamically add VBA module to create dynamic VBA class
f2 = wb.VBProject.VBComponents.Add(vbext_ComponentType.vbext_ct_StdModule)
f2.Name = "MOD_" + tempGuid
f2.CodeModule.AddFromString(
"Public Function instantiateTempClass_{0}() As Object\r\n Set instantiateTempClass_{0} = New CLS_{0}\r\nEnd Function".format(tempGuid))
try:
wb.Activate()
tempObject = xl.Run(sheetname + "!instantiateTempClass_{}".format(tempGuid))
tempObject.TempClassCall()
except com_error as e:
raise e
finally:
self._wb.VBProject.VBComponents.Remove(f)
self._wb.VBProject.VBComponents.Remove(f2)