6

我正在尝试获取所有当前打开的 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);
}

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

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

IBindCtx bc;
IRunningObjectTable rot;
CreateBindCtx(0, out bc);
bc.GetRunningObjectTable(out rot);

这里的问题是它CreateBindCtx实际上接受 aUCOMIBindCTX而不是IBindCTX,但每个MSDNUCOMIBindCTX已过时。

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

4

3 回答 3

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;
        }

    }
}
于 2012-12-10T20:07:06.267 回答
0

此代码是使用最后一条评论中的资源(链接)和您提供的代码放在一起的。这应该拉入所有打开的工作簿名称。

using Excel = Microsoft.Office.Interop.Excel;        

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


// Callback passed to EnumChildWindows to find any window with the
// registered classname "paneClassDC" - this is the class name of
// PowerPoint's accessible document window.
public bool EnumChildProc(int hwnd, ref int lParam)
{
    StringBuilder windowClass = new StringBuilder(128);
    GetClassName(hwnd, windowClass, 128);
    s += windowClass.ToString() + "\n";
    if (windowClass.ToString() == "EXCEL7")
    {
        lParam = hwnd;
    }
    return true;
}

public delegate bool EnumChildCallback(int hwnd, ref int lParam);


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


[DllImport("User32")]
public static extern int FindWindowEx(
    int hwndParent, int hwndChildAfter, string lpszClass,
    int missing);


// AccessibleObjectFromWindow gets the IDispatch pointer of an object
// that supports IAccessible, which allows us to get to the native OM.
[DllImport("Oleacc.dll")]
private static extern int AccessibleObjectFromWindow(
    int hwnd, uint dwObjectID,
    byte[] riid,
    ref Excel.Window ptr);


// Get the window handle for a running instance of PowerPoint.
internal List<String> GetAccessibleObject()
{

    List<String> workbookNames = new List<String>();
    try
    {
        // Walk the children of the desktop to find PowerPoint’s main
        // window.
        int hwnd = FindWindowEx(0, 0, "XLMAIN", 0);
        while(hwnd != 0)
        if (hwnd != 0)
        {
            // Walk the children of this window to see if any are
            // IAccessible.
            int hWndChild = 0;
            EnumChildCallback cb =
                new EnumChildCallback(EnumChildProc);
            EnumChildWindows(hwnd, cb, ref hWndChild);


            if (hWndChild != 0)
            {
                // OBJID_NATIVEOM gets us a pointer to the native 
                // object model.
                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)
                {
                    Excel.Application eApp = ptr.Application;
                    if (eApp != null)
                    {
                        foreach (Excel.Workbook wb in eApp.Workbooks)
                        {
                            workbookNames.Add(wb.FullName);
                        }
                        Marshal.ReleaseComObject(eApp);
                        GC.WaitForPendingFinalizers();
                        GC.Collect();
                    }
                }

                hwnd = FindWindowEx(0, hwnd, "XLMAIN", 0);
            }
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex.ToString());
    }
    return workbookNames;
}
于 2012-12-10T18:53:29.600 回答
0
public void closeOpenedFile(string file_name)
{
    //Excel Application Object
    Microsoft.Office.Interop.Excel.Application oExcelApp;
    //Get reference to Excel.Application from the ROT.
    if (Process.GetProcessesByName("EXCEL").Count() > 0)
    {
        oExcelApp = (Microsoft.Office.Interop.Excel.Application)System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application");

        foreach (Microsoft.Office.Interop.Excel.Workbook WB in oExcelApp.Workbooks)
        {
            //MessageBox.Show(WB.FullName);
            if (WB.Name == file_name)
            {
                WB.Save();
                WB.Close();
                //oExcelApp.Quit();
            }
        }
    }
}
于 2016-02-18T20:09:54.137 回答