如何从 ROT 获取 progID?

How to get a progID from the ROT?

简介:

我正在尝试遍历 ROT (运行 Object Table) 并搜索 COM object 的特定 progID。在我的例子中,progID 应该是 Excel.Application.

例如,如果我有一个 excel 的实例 运行,我会得到 Excel.Application 这样的:

Excel.Application excel = (Excel.Application)Marshal.GetActiveObject("Excel.Application");

问题:

如果我有多个实例 运行,这个调用基本上会给我 any 个实例,但我无法识别正确的实例(例如通过 window 标题,...)。所以,我需要一个特定的实例,它可以通过打开 Workbook 或 window 标题来识别。


当前方法:

由于 Marshal.GetActiveObject 需要一个 progID,我尝试通过 ROT 查找所有当前存在的 progID:

public static void Main(string[] args)
{
    if (GetRunningObjectTable(0, out IRunningObjectTable pprot) == 0)
    {
        pprot.EnumRunning(out IEnumMoniker ppenumMoniker);
        ppenumMoniker.Reset();

        var moniker = new IMoniker[1];

        while (ppenumMoniker.Next(1, moniker, IntPtr.Zero) == 0)
        {
            CreateBindCtx(0, out IBindCtx ppbc);
            pprot.GetObject(moniker[0], out object ppunkObject);

            moniker[0].GetDisplayName(ppbc, null, out string ppszDisplayName);
            moniker[0].GetClassID(out Guid pClassID);

            ProgIDFromCLSID(ref pClassID, out string lplpszProgID);
        }
    }

    // Excel excel = (Excel)Marshal.GetActiveObject("Excel.Application"); 
}

我注意到以下几点:


问题:

此外,如果有更简单的方法来识别我的实例,请告诉我。


附加代码:

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

using Excel = Microsoft.Office.Interop.Excel.Application;

[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable pprot);

[DllImport("ole32.dll")]
private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);

[DllImport("ole32.dll")]
private static extern int ProgIDFromCLSID([In] ref Guid clsid, [MarshalAs(UnmanagedType.LPWStr)] out string lplpszProgID);

解法:

似乎无法从 ROT 中获得正确的 progID(这也无济于事,因为例如 Excel.Application 没有给出具体实例)。但是,如果您知道文件将存储在哪里(路径的一部分似乎就足够了)。


完整代码(工作示例):

// Microsoft.CSharp, 4.0.30319
// Microsoft.Office.Interop.Excel, 15.0.4420.1017

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

using Excel = Microsoft.Office.Interop.Excel;



public static void Main(string[] args)
{
    object ppunk = null;

    if (GetRunningObjectTable(0, out IRunningObjectTable pprot) == 0)
    {
        pprot.EnumRunning(out IEnumMoniker ppenumMoniker);
        ppenumMoniker.Reset();

        var moniker = new IMoniker[1];

        while (ppenumMoniker.Next(1, moniker, IntPtr.Zero) == 0)
        {
            CreateBindCtx(0, out IBindCtx ppbc);

            moniker[0].GetDisplayName(ppbc, null, out string ppszDisplayName);

            Marshal.ReleaseComObject(ppbc);

            // Identify by worksheet path
            if (ppszDisplayName.ToLower().Contains(".xls") && ppszDisplayName.ToLower().Contains("appdata"))
            {
                pprot.GetObject(moniker[0], out object ppunkObject);
                ppunk = ppunkObject;
            }
        }
    }

    if (ppunk != null)
    {
        Excel.Workbook workbook = ppunk as Excel.Workbook;
        Excel.Application excel = workbook.Application;

        // Do something
        dynamic title = workbook.FullName;

        excel.Quit();
    }
}



// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684004(v=vs.85).aspx
[DllImport("ole32.dll")]
private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable pprot);

// https://msdn.microsoft.com/de-de/library/windows/desktop/ms678542(v=vs.85).aspx
[DllImport("ole32.dll")]
private static extern void CreateBindCtx(int reserved, out IBindCtx ppbc);

很多文字,简而言之:如果您只知道工作簿的路径或文件名的一部分,基本上这个片段会给您 Excel COM 对象。