【问题标题】:Accessing Excel.Application objects of other excel instances in Running Object Table in C#在 C# 中运行对象表中访问其他 excel 实例的 Excel.Application 对象
【发布时间】:2014-04-03 15:19:35
【问题描述】:

我一直在尝试访问在 Visual C# Express 2010 的 ROT 中注册的所有 Excel 2010 实例的 COM 对象。我在 http://adndevblog.typepad.com/autocad/2013/12/accessing-com-applications-from-the-running-object-table.html 上找到了一个代码,我对其进行了一些修改以返回所有可能的 @ 987654324@ 在运行对象表中注册的对象。代码:-

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”。此操作失败,因为 IID 为“{000208D5-0000-0000-C000-000000000046}”的接口的 COM 组件上的 QueryInterface 调用因以下错误而失败:Cette interface n'est pas Prize en charge(来自 HRESULT 的异常: 0x80004002 (E_NOINTERFACE))。

我已尝试检查有问题的 dll,检查注册表是否由于 TypeLibHKEY_CLASSES_ROOT 下存在不同的“版本”而导致 CLSID 发生冲突,尝试修复 Office 2010 女士,卸载旧版本的 Office (2003 ) 等来解决这个错误。但没有任何效果。我也尝试过使用Microsoft.VisualBasic 引用,然后使用Interaction.GetObject,但这也会产生同样的错误。

有什么想法吗?

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

【问题讨论】:

  • Excel 是一个单实例应用程序。如果您再次启动它,它只会要求第一个实例打开文档。并退出。使用任务管理器的进程选项卡查看您正在运行的 Excel.exe 进程的数量。只有一个是正常的。
  • 谢谢,这样就更清楚了。但有时我会打开两个 excel 实例,以便直观地比较文件。在一个实例下打开两个文件时,其中一些很难做到。这就是为什么我一直试图找出从所有实例中访问对象的方法。

标签: c# .net excel


【解决方案1】:

我认为我们面临着同样的问题。我正在使用windows7和office2010,使用与您提到的相同的方式来获取ROT表来处理所有打开的excel。

您有 GetRunningCOMObjectByName(string objectDisplayname) 来获取 com 对象,并尝试转换为 Excel.Application,但它不是那种类型。我试过,如果你通过fullName获取对象,你得到的对象是Excel.Workbook的类型,所以可以转换成Workbook。对我来说。

所以你可以从 ROT 中看到,你在 Excel 中打开的每个文档,都有一个与之相关的 fullName。而你通过名称得到的 comObj 是 Workbook 的类型。

但是我还有一些关于 ROT 的问题。如果我们查看 ROT 表,它有两个名称:!{00024505-0014-0000-C000-000000000046} 和 !{00024500-0000-0000-C000-000000000046},与 Excel.Application 类似的 classId。如果你得到这 2 项的 comObj,你可以转换为 Excel.Application。就像 Marshal.GetActiveObject("Excel.Application") 的方式一样。并且 2 项是 reference equaled

但是当有多个EXCEL.EXE进程在运行时,我想我可以在ROT中得到更多的Application项,但事实是

  1. ROT中还有我上面提到的2项,也就是说ROT中只有一个Application(可以打开2个excel工作簿查看,workbook1和workbook2)
  2. 但工作簿运行良好,您可以打开所有工作簿(工作簿 1 和工作簿 2)。
  3. workbook1.Application 引用等于 ROT 中的 Application,但 workbook2.Application 不是。

那么,如果ROT中只有一个Application并且等于workbook1.Applicaton,那么workbook2.Application在哪里呢?为什么 workbook2.Application 没有在 ROT 表中注册?

【讨论】:

  • 好吧,get Workbook 提到了另一种方法,Workbook wb = Marshal.BindToMoniker(fullName) 而不是 DllImport Win API
【解决方案2】:

为 C# 4.0+ 尝试以下代码 sn-p :

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 worbook in MSExcelWorkbookRunningInstances.Enum())
                    System.Console.WriteLine("{0}.  '{1}' '{2}'", index++, worbook.Application.Name, worbook.FullName);
            }
            catch (Exception ex)
            {
                System.Console.WriteLine("Error = '{0}'", ex.Message);   
            }
        }
    }
}

【讨论】:

  • 这是一个了不起的答案,谢谢!测试中的拼写错误“工作簿”而不是“工作簿”,确实需要一些时间才能找到 =)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-12
  • 1970-01-01
  • 2018-04-25
  • 2012-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多