如何引用所有正在运行的 Excel 应用程序实例(包括隐藏的和没有工作簿的实例)的 COM 对象?

use*_*973 3 com excel winapi pseudocode

如何获取每个正在运行的 Excel 应用程序实例的完整引用列表(无论其工作簿数量和可见性状态如何)?


我知道我可以使用Windows API来查找每个Excel工作簿窗口(具有窗口类名称EXCEL7),获取它们的句柄以与该AccessibleObjectFromWindow函数一起使用,然后调度并获取应用程序COM对象。

但这仅适用于至少有一个可见工作簿的 Excel 应用程序实例。我怎样才能获取隐藏和/或没有工作簿的 Excel 应用程序实例?

Excel 应用程序实例窗口(其窗口类名称为XLMAIN)不检索任何可访问的对象。

我正在寻找带有或不带有伪代码或任何编程语言的代码的解释,只要我能够自己理解和实现它(用Python)。

use*_*973 8

我想用 Python 实现这一点,尽管我提出这个问题时并不要求答案与 Python 相关,因为一般性解释(不必特定于某种编程语言)可能就足够了。

在查看了win32com.clientGetObject模块在 Python 中实现的 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)
Run Code Online (Sandbox Code Playgroud)

MkParseDisplayName函数引导我到objbase.h 标头的函数在那里我找到了我不知道的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)
Run Code Online (Sandbox Code Playgroud)

if 检查是我发现过滤掉我不想要的东西的方法,尽管我不确定这是否是一个好方法。

pywin32包的文档(包含 win32com 模块以及pythoncom 模块的文档)对我帮助很大,结合Windows API 文档,我对 COM 有了更多的了解。

对于任何想要用另一种编程语言执行此操作的人来说,应该很容易了解上面代码中使用的内容。以下列出了主要的帮助内容:GetRunningObjectTable 函数CreateBindCtx 函数IMoniker::GetDisplayName 方法IRunningObjectTable::GetObject 方法IUnknown::QueryInterface 方法IDispatch::GetTypeInfo 方法ITypeInfo::GetTypeAttr 方法


特定于没有 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()
Run Code Online (Sandbox Code Playgroud)