在 C# 中运行对象表中访问其他 Excel 实例的 Excel.应用程序对象
本文关键字:Excel 对象 实例 应用程序 其他 运行 访问 | 更新日期: 2023-09-27 17:56:40
我一直在尝试访问在Visual C# Express 2010中ROT中注册的所有Excel 2010实例的COM对象。我在 http://adndevblog.typepad.com/autocad/2013/12/accessing-com-applications-from-the-running-object-table.html 上找到了一个代码,我对其进行了一些修改,以返回在运行对象表中注册的所有可能的Excel.Application
对象。代码 :-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
namespace APPROT
{
[ComVisible(true)]
class COMApp
{
[DllImport("Ole32.dll")]
public static extern int CreateBindCtx(
uint reserved,
out IBindCtx ppbc);
[DllImport("Ole32.dll")]
public static extern int GetRunningObjectTable(
int reserved,
out IRunningObjectTable prot);
[STAThread]
public static List<Excel.Application> GetRunningInstances()
{
string[] progIds = new string[] { "Excel.Application"};
List<string> clsIds = new List<string>();
// get the app clsid
foreach (string progId in progIds)
{
Type type = Type.GetTypeFromProgID(progId);
if (type != null)
clsIds.Add(type.GUID.ToString().ToUpper());
}
// get Running Object Table ...
IRunningObjectTable Rot = null;
GetRunningObjectTable(0, out Rot);
if (Rot == null)
return null;
// get enumerator for ROT entries
IEnumMoniker monikerEnumerator = null;
Rot.EnumRunning(out monikerEnumerator);
if (monikerEnumerator == null)
return null;
monikerEnumerator.Reset();
List<Excel.Application> instances = new List<Excel.Application>();
IntPtr pNumFetched = new IntPtr();
IMoniker[] monikers = new IMoniker[1];
// go through all entries and identifies app instances
while (monikerEnumerator.Next(1, monikers, pNumFetched) == 0)
{
object ComObject;
Rot.GetObject(monikers[0], out ComObject);
if (ComObject == null)
continue;
try
{
instances.Add((Excel.Application)ComObject);
}
catch {}
}
return instances;
}
}
}
但这只返回 excel 的第一个实例的Excel.Application
对象。我还尝试使用文件名和 http://dotnet-snippets.de/snippet/laufende-com-objekte-abfragen/526(德语站点)上提到的代码访问该对象,即使用 GetRunningCOMObjectByName(string objectDisplayname)
,但是在获得 COM 对象后,当我尝试将其转换为 Excel.Application
时,我收到以下错误:-
无法将类型为"System.__ComObject"的 COM 对象强制转换为接口类型"Microsoft.Office.Interop.Excel.Application"。此操作失败,因为 COM 组件上的 COM 组件对 IID '{000208D5-0000-0000-C000-0000000000046}' 的接口的 QueryInterface 调用失败,原因如下错误:Cette interface n'est pas prise en charge (来自 HRESULT: 0x80004002 (E_NOINTERFACE)的异常)。
我尝试检查错误的dll,检查注册表是否由于HKEY_CLASSES_ROOT
下TypeLib
存在不同的"版本"而发生冲突,尝试修复Office 2010女士,卸载旧版本的Office(2003)等以解决此错误。但没有任何效果。我也尝试使用Microsoft.VisualBasic
引用,然后使用Interaction.GetObject
,但这也给出了相同的错误。
有什么想法吗?
我也尝试了 https://stackoverflow.com/a/779710/2960814 上提到的后期绑定方法。但这也允许访问 ROT 中的第一个 Excel 实例。
我认为我们正面临同样的问题。我正在使用Windows7和office2010,使用与您提到的相同的方式获取ROT表来处理所有打开的Excel。
你有 GetRunningCOMObjectByName(string objectDisplayname) 来获取 com 对象,并尝试转换为 Excel.Application,但它不是那种类型。我试过了,如果你通过全名获取对象,你得到的对象是Excel.Workbook的类型,所以它可以转换为工作簿。它对我有用。
所以你可以从ROT中看到,你在Excel中打开的每个文档,都有一个与之相关的全名。您按名称获得的comObj是 工作簿 。
但我仍然对 ROT 有一些疑问。 如果我们查看 ROT 表,它有两个带有名称的项目:!{00024505-0014-0000-C000-000000000046} 和 !{00024500-0000-0000-C000-000000000046},与 Excel.Application 类似的类 Id 。如果你得到这2项的comObj,你可以转换为Excel.Application。就像Marshal.GetActiveObject("Excel.Application")所做的那样。并且 2 项是参考相等的。
但是当有多个 EXCEL.EXE 进程运行时,我想我可以在 ROT 中获得更多的应用程序项目,但事实是
- ROT中
- 还有我上面提到的2个项目,这意味着ROT中只有一个应用程序(您可以打开2个excel工作簿进行检查,工作簿1和工作簿2)
- 但是工作簿运行良好,您可以打开所有工作簿(工作簿1和工作簿2)。
- 工作簿1.应用程序引用等于 ROT 中的应用程序,但工作簿 2。申请不是。
因此,如果 ROT 中只有一个应用程序并且等于工作簿 1。应用,工作簿在哪里2。应用?为什么选择工作簿2。应用程序未在 ROT 表中注册?
尝试以下 C# 4.0+ 代码片段:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace ROT.TestConsole
{
/// <summary>
/// Gets MS Excel running workbook instances via ROT
/// </summary>
public class MSExcelWorkbookRunningInstances
{
[DllImport("ole32.dll")]
static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
[DllImport("ole32.dll")]
public static extern void GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
public static IEnumerable<dynamic> Enum()
{
// get Running Object Table ...
IRunningObjectTable Rot;
GetRunningObjectTable(0, out Rot);
// get enumerator for ROT entries
IEnumMoniker monikerEnumerator = null;
Rot.EnumRunning(out monikerEnumerator);
IntPtr pNumFetched = new IntPtr();
IMoniker[] monikers = new IMoniker[1];
IBindCtx bindCtx;
CreateBindCtx(0, out bindCtx);
while (monikerEnumerator.Next(1, monikers, pNumFetched) == 0)
{
string applicationName = "";
dynamic workBook = null;
try
{
Guid IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}");
monikers[0].BindToObject(bindCtx, null, ref IUnknown, out workBook);
applicationName = workBook.Application.Name;
}
catch { }
if (applicationName == "Microsoft Excel") yield return workBook;
}
}
}
}
测试它:
using System;
namespace ROT.TestConsole
{
class Program
{
static void Main(string[] args)
{
try
{
int index = 1;
foreach (dynamic workbook in MSExcelWorkbookRunningInstances.Enum())
System.Console.WriteLine("{0}. '{1}' '{2}'", index++, workbook.Application.Name, workbook.FullName);
}
catch (Exception ex)
{
System.Console.WriteLine("Error = '{0}'", ex.Message);
}
}
}
}