问题描述
我如何获得对每个正在运行的Excel应用程序实例的引用的完整列表(无论其工作簿数量和可见性状态如何)?
我知道我可以使用Windows API查找每个Excel工作簿窗口(其窗口类名称为EXCEL7
),获取其句柄以与AccessibleObjectFromWindow
函数一起使用,然后调度并获取应用程序COM对象。
尽管仅适用于至少可见一个工作簿的Excel应用程序实例。我还如何获取隐藏的和/或没有工作簿的Excel应用程序实例?
Excel应用程序实例窗口(其窗口类名称为XLMAIN
)不会检索任何可访问的对象。
我正在寻找一种解释,无论是否使用伪代码或任何编程语言的代码,只要我自己能够理解并实现(使用Python)即可。
解决方法
我想用Python来实现这一点,尽管我问这个问题时并不需要答案是关于Python,因为一般的解释(不必专门针对编程语言)就足够了。
查看了win32com.client模块在Python中实现的GetObject
VB函数的源代码之后,我注意到它调用了Moniker
函数:
def Moniker(Pathname,clsctx = pythoncom.CLSCTX_ALL):
"""
Python friendly version of GetObject's moniker functionality.
"""
moniker,i,bindCtx = pythoncom.MkParseDisplayName(Pathname)
dispatch = moniker.BindToObject(bindCtx,None,pythoncom.IID_IDispatch)
return __WrapDispatch(dispatch,Pathname,clsctx=clsctx)
MkParseDisplayName函数将我引向objbase.h header's functions,在那里我发现了一个我不知道的GetRunningObjectTable函数。
一段时间后,搜索有关它的多个代码段,并尝试将它们组合在一起以执行我想要的操作而不会引发错误,并确保它仅获取Excel应用程序实例(我在代码中添加了Microsoft Word,以展示如何将其与其他COM对象一起使用),而无需重复,我将下面的代码放在一起。
from pythoncom import CreateBindCtx as create_bind_context,GetRunningObjectTable as get_running_object_table,IID_IDispatch as dispatch_interface_iid
from win32com.client import Dispatch as dispatch
running_object_table = get_running_object_table()
bind_context = create_bind_context()
excel_application_class_clsid = '{00024500-0000-0000-C000-000000000046}'
word_application_class_clsid = '{000209FF-0000-0000-C000-000000000046}'
excel_application_clsid = '{000208D5-0000-0000-C000-000000000046}'
word_application_clsid = '{00020970-0000-0000-C000-000000000046}'
excel_applications = []
for moniker in running_object_table:
name = moniker.GetDisplayName(bind_context,None)
if all(clsid not in name for clsid in [excel_application_class_clsid,word_application_class_clsid]):
continue
unknown_com_interface = running_object_table.GetObject(moniker)
dispatch_interface = unknown_com_interface.QueryInterface(dispatch_interface_iid)
dispatch_clsid = str(dispatch_interface.GetTypeInfo().GetTypeAttr().iid)
if dispatch_clsid not in [excel_application_clsid,word_application_clsid]:
continue
com_object = dispatch(dispatch=dispatch_interface)
excel_application = com_object.Application
if id(excel_application) not in [id(excel_application) for excel_application in excel_applications]:
excel_applications.append(excel_application)
input(excel_applications)
if检查是我发现要过滤掉我不想要的东西的方法,尽管我不确定这是否是一个好方法。
pywin32 package's documentation(包含win32com模块和documentation of the pythoncom module)对我有很大帮助,并且与Windows API documentation一起我对COM有了很多了解。 / p>
对于任何想用另一种编程语言进行操作的人来说,应该很容易看到上面的代码中使用了什么。以下是一些主要的帮助列表:GetRunningObjectTable function,CreateBindCtx function,IMoniker::GetDisplayName method,IRunningObjectTable::GetObject method,IUnknown::QueryInterface method,IDispatch::GetTypeInfo method和{{3} }。
特定于没有Word的Excel实例的功能:
from pythoncom import (
CreateBindCtx as create_bind_context_com_interface,IID_IDispatch as dispatch_com_interface_iid,GetRunningObjectTable as get_running_object_table_com_interface,)
from win32com.client import (
Dispatch as dispatch,)
def get_excel_instances():
'''
Returns a list of the running Microsoft Excel application
instances as component object model (COM) objects.
'''
running_object_table_com_interface = get_running_object_table_com_interface()
bind_context_com_interface = create_bind_context_com_interface()
excel_application_class_clsid = '{00024500-0000-0000-C000-000000000046}'
excel_application_clsid = '{000208D5-0000-0000-C000-000000000046}'
excel_instance_com_objects = []
for moniker_com_interface in running_object_table_com_interface:
display_name = moniker_com_interface.GetDisplayName(bind_context_com_interface,None)
if excel_application_class_clsid not in display_name:
continue
unknown_com_interface = running_object_table_com_interface.GetObject(moniker_com_interface)
dispatch_com_interface = unknown_com_interface.QueryInterface(dispatch_com_interface_iid)
dispatch_clsid = str(object=dispatch_com_interface.GetTypeInfo().GetTypeAttr().iid)
if dispatch_clsid != excel_application_clsid:
continue
excel_instance_com_object = dispatch(dispatch=dispatch_com_interface)
excel_instance_com_objects.append(excel_instance_com_object)
return excel_instance_com_objects
excel_instances = get_excel_instances()
input()