查找所有打开的Excel工作簿

sig*_*gil 5 c# excel office-interop

我正在尝试获取所有当前打开的Excel工作簿的列表,以便用户可以选择从其中一个中获取一些数据。

我尝试了这个:

List<string> excelList = new List<string>();
Process[] processList = Process.GetProcessesByName("excel");
foreach (Process p in processList)
{
 excelList.Add(p.MainWindowTitle);
 Console.WriteLine(p.MainWindowTitle);
}
Run Code Online (Sandbox Code Playgroud)

但这只会获得第一个打开的Excel实例和最近打开的实例,因此在这两个实例之间打开的任何工作簿都不会在列表中。

我也开始探索此SO问题的答案中博客链接中描述的解决方案,并尝试使用博客条目中建议的代码访问“运行对象表”:

IBindCtx bc;
IRunningObjectTable rot;
CreateBindCtx(0, out bc);
bc.GetRunningObjectTable(out rot);
Run Code Online (Sandbox Code Playgroud)

这里的问题是,CreateBindCtx实际上接受a UCOMIBindCTX代替IBindCTX,但是UCOMIBindCTX对于MSDN已经过时了。

有没有一种更简单的方法可以执行我想做的事情:获取Workbook与所有打开的Excel书籍相对应的对象列表?

sig*_*gil 3

好的,我找到了一种方法来做到这一点。描述该解决方案的博客似乎不再可用,但有一个Google 缓存版本

我稍微修改了代码,以便构造函数接受 MainWindowHandle,因为我正在迭代所有进程的句柄。

班级如下。我留下了 Andrew Whitechapel 的一些评论来解释发生的情况,因为这段代码超出了我目前对 Windows 操作系统管理的了解:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;

namespace DTEExcel
{
    class ExcelApplicationRetriever
    {
        [DllImport("Oleacc.dll")]
        public static extern int AccessibleObjectFromWindow(
              int hwnd, uint dwObjectID, byte[] riid,
              ref Microsoft.Office.Interop.Excel.Window ptr);

        [DllImport("User32.dll")]
        public static extern int GetClassName(
              int hWnd, StringBuilder lpClassName, int nMaxCount);

        [DllImport("User32.dll")]
        public static extern bool EnumChildWindows(
              int hWndParent, EnumChildCallback lpEnumFunc,
              ref int lParam);

        public delegate bool EnumChildCallback(int hwnd, ref int lParam);
        private EnumChildCallback cb;
        public Excel.Application xl;

        public ExcelApplicationRetriever(int winHandle)
        {
            // We need to enumerate the child windows to find one that
            // supports accessibility. To do this, instantiate the
            // delegate and wrap the callback method in it, then call
            // EnumChildWindows, passing the delegate as the 2nd arg.
            if (winHandle != 0)
            {
                int hwndChild = 0;
                cb = new EnumChildCallback(EnumChildProc);
                EnumChildWindows(winHandle, cb, ref hwndChild);

                // If we found an accessible child window, call
                // AccessibleObjectFromWindow, passing the constant
                // OBJID_NATIVEOM (defined in winuser.h) and
                // IID_IDispatch - we want an IDispatch pointer
                // into the native object model.
                if (hwndChild != 0)
                {
                    const uint OBJID_NATIVEOM = 0xFFFFFFF0;
                    Guid IID_IDispatch = new Guid(
                         "{00020400-0000-0000-C000-000000000046}");
                    Excel.Window ptr = null;

                    int hr = AccessibleObjectFromWindow(
                          hwndChild, OBJID_NATIVEOM,
                         IID_IDispatch.ToByteArray(), ref ptr);
                    if (hr >= 0)
                    {
                        // If we successfully got a native OM
                        // IDispatch pointer, we can QI this for
                        // an Excel Application (using the implicit
                        // cast operator supplied in the PIA).
                        xl = ptr.Application;
                    }
                }
            }
        }

        public bool EnumChildProc(int hwndChild, ref int lParam)
        {
            StringBuilder buf = new StringBuilder(128);
            GetClassName(hwndChild, buf, 128);
            if (buf.ToString() == "EXCEL7")
            {
                lParam = hwndChild;
                return false;
            }
            return true;
        }

    }
}
Run Code Online (Sandbox Code Playgroud)