在 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_ROOTTypeLib存在不同的"版本"而发生冲突,尝试修复Office 2010女士,卸载旧版本的Office(2003)等以解决此错误。但没有任何效果。我也尝试使用Microsoft.VisualBasic引用,然后使用Interaction.GetObject,但这也给出了相同的错误。

有什么想法吗?

我也尝试了 https://stackoverflow.com/a/779710/2960814 上提到的后期绑定方法。但这也允许访问 ROT 中的第一个 Excel 实例。

在 C# 中运行对象表中访问其他 Excel 实例的 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中
  1. 还有我上面提到的2个项目,这意味着ROT中只有一个应用程序(您可以打开2个excel工作簿进行检查,工作簿1和工作簿2)
  2. 但是工作簿运行良好,您可以打开所有工作簿(工作簿1和工作簿2)。
  3. 工作簿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);   
            }
        }
    }
}